Split operator-dictionary-001.html into smaller pieces to avoid timeout in debug modes. (#19516)

diff --git a/mathml/presentation-markup/operators/operator-dictionary-001.html b/mathml/presentation-markup/operators/operator-dictionary-001.html
index ea3d5ae..e4e5c68 100644
--- a/mathml/presentation-markup/operators/operator-dictionary-001.html
+++ b/mathml/presentation-markup/operators/operator-dictionary-001.html
@@ -13,266 +13,14 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/mathml/support/feature-detection.js"></script>
 <script src="/mathml/support/operator-dictionary.js"></script>
-<style>
-  @font-face {
-    font-family: operators;
-    src: url("/fonts/math/operators.woff");
-  }
-  math, math * {
-      font-family: operators;
-      /* Use large enough font-size so that 1/18em = 2.77px > epsilon and
-         one can really distinguish lspace/rspace values. */
-      font-size: 50px;
-  }
-</style>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
 <script>
   setup({ explicit_done: true });
   window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
-
   async function runTests() {
-      let epsilon = 1;
       let json = await fetchOperatorDictionary();
-
-      // The operator dictionary has more than one thousand of entries so the
-      // tests are grouped in chunks so that these don't get much more
-      // importance than other MathML tests. For easy debugging, one can set the
-      // chunk size to 1. Also, note that the test div will remain visible for
-      // failed tests.
-      const entryPerChunk = 50
-
-      var counter = 0;
-      var tests = {
-          "lspace/rspace": null,
-          "movablelimits": null,
-          "largeop": null,
-          "stretchy": null,
-          "symmetric": null,
-          "accent": null
-      };
-
-      for (key in json.dictionary) {
-
-          if (counter % entryPerChunk === 0) {
-              // Start of a new chunk.
-              // Complete current async tests and create new ones for the next chunk.
-              for (name in tests) {
-                  if (tests[name]) tests[name].done();
-                  tests[name] = async_test(`Operator dictionary chunk ${1 + counter / entryPerChunk} - ${name}`);
-              }
-          }
-
-          let parsedKey = splitKey(key);
-          let entry = json.dictionary[key];
-
-          tests["lspace/rspace"].step(function() {
-              assert_true(MathMLFeatureDetection.has_operator_spacing());
-              document.body.insertAdjacentHTML("beforeend", `<div>\
-lspace/rspace for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <mrow>\
-    <mn>&nbsp;</mn>\
-    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
-    <mn>&nbsp;</mn>\
-  </mrow>\
-</math>\
- VS \
-<math>\
-  <mrow>\
-    <mn>&nbsp;</mn>\
-    <mo form="${parsedKey.form}" lspace="${defaultPropertyValue(entry, 'lspace')}" rspace="${defaultPropertyValue(entry, 'rspace')}">${parsedKey.characters}</mo>\
-    <mn>&nbsp;</mn>\
-  </mrow>\
-</math>\
-</div>`);
-              var div = document.body.lastElementChild;
-              var mrows = div.getElementsByTagName("mrow");
-              function spaceBetween(element, i, j) {
-                  return element.children[j].getBoundingClientRect().left -
-                      element.children[i].getBoundingClientRect().right;
-              }
-              var lspace = spaceBetween(mrows[0], 0, 1);
-              var rspace = spaceBetween(mrows[0], 1, 2);
-              var lspaceRef = spaceBetween(mrows[1], 0, 1);
-              var rspaceRef = spaceBetween(mrows[1], 1, 2);
-              assert_approx_equals(lspace, lspaceRef, epsilon, `lspace (${key})`);
-              assert_approx_equals(rspace, rspaceRef, epsilon, `rspace (${key})`);
-              div.style.display = "none";
-          });
-
-          tests["movablelimits"].step(function() {
-              assert_true(MathMLFeatureDetection.has_movablelimits());
-              var defaultValue = defaultPropertyValue(entry, "movablelimits");
-              document.body.insertAdjacentHTML("beforeend", `<div>\
-movablelimits for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <munder>\
-    <mo stretchy="false" form="${parsedKey.form}">${parsedKey.characters}</mo>\
-    <mn>&nbsp;</mn>\
-  </munder>\
-</math>\
- VS \
-<math>\
-  <munder>\
-    <mo stretchy="false" form="${parsedKey.form}" movablelimits="${defaultValue}">${parsedKey.characters}</mo>\
-    <mn>&nbsp;</mn>\
-  </munder>\
-</math>\
-</div>`);
-              var div = document.body.lastElementChild;
-              var munders = div.getElementsByTagName("munder");
-              munder = munders[0].getBoundingClientRect()
-              munderRef = munders[1].getBoundingClientRect()
-              assert_approx_equals(munder.height, munderRef.height, epsilon, `Movablelimits property for ${key} should be '${defaultValue}'`);
-              div.style.display = "none";
-          });
-
-          tests["largeop"].step(function() {
-              // FIXME: Should really detect largeop support...
-              assert_true(MathMLFeatureDetection.has_mspace());
-              var defaultValue = defaultPropertyValue(entry, "largeop");
-              document.body.insertAdjacentHTML("beforeend", `<div>\
-largeop for "${parsedKey.characters}" (${parsedKey.form}): \
-<math displaystyle="true">\
-  <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
-</math>\
- VS \
-<math displaystyle="true">\
-  <mo form="${parsedKey.form}" largeop="${defaultValue}">${parsedKey.characters}</mo>\
-</math>\
-</div>`);
-              var div = document.body.lastElementChild;
-              var mos = div.getElementsByTagName("mo");
-              mo = mos[0].getBoundingClientRect()
-              moRef = mos[1].getBoundingClientRect()
-              assert_approx_equals(mo.height, moRef.height, epsilon, `Largeop property for ${key} should be '${defaultValue}'`);
-              div.style.display = "none";
-          });
-
-          if (entry.horizontal) {
-              tests["stretchy"].step(function() {
-                  // FIXME: Should really detect stretchy support...
-                  assert_true(MathMLFeatureDetection.has_munder());
-                  var defaultValue = defaultPropertyValue(entry, "stretchy");
-                  document.body.insertAdjacentHTML("beforeend", `<div>\
-stretchy for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <munder>\
-    <mn>&nbsp;&nbsp;</mn>\
-    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
-  </munder>\
-</math>\
- VS \
-<math>\
-  <munder>\
-    <mn>&nbsp;&nbsp;</mn>\
-    <mo form="${parsedKey.form}" stretchy="${defaultValue}">${parsedKey.characters}</mo>\
-  </munder>\
-</math>\
-</div>`);
-                  var div = document.body.lastElementChild;
-                  var mos = div.getElementsByTagName("mo");
-                  mo = mos[0].getBoundingClientRect()
-                  moRef = mos[1].getBoundingClientRect()
-                  assert_approx_equals(mo.width, moRef.width, epsilon, `Stretchy property for ${key} should be '${defaultValue}'`);
-                  div.style.display = "none";
-              });
-          } else {
-              tests["stretchy"].step(function() {
-                  // FIXME: Should really detect stretchy support...
-                  assert_true(MathMLFeatureDetection.has_mspace());
-                  var defaultValue = defaultPropertyValue(entry, "stretchy");
-                  document.body.insertAdjacentHTML("beforeend", `<div>\
-stretchy for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <mrow>\
-    <mo form="${parsedKey.form}" symmetric="false">${parsedKey.characters}</mo>\
-    <mspace height="2em"></mspace>\
-  </mrow>\
-</math>\
- VS \
-<math>\
-  <mrow>\
-    <mo form="${parsedKey.form}" symmetric="false" stretchy="${defaultValue}">${parsedKey.characters}</mo>\
-    <mspace height="2em"></mspace>\
-  </mrow>\
-</math>\
-</div>`);
-                  var div = document.body.lastElementChild;
-                  var mos = div.getElementsByTagName("mo");
-                  mo = mos[0].getBoundingClientRect()
-                  moRef = mos[1].getBoundingClientRect()
-                  assert_approx_equals(mo.height, moRef.height, epsilon, `Stretchy property for ${key} should be '${defaultValue}'`);
-                  div.style.display = "none";
-              });
-              tests["symmetric"].step(function() {
-                  // FIXME: Should really detect symmetric support...
-                  assert_true(MathMLFeatureDetection.has_mspace());
-                  var defaultValue = defaultPropertyValue(entry, "symmetric");
-                  document.body.insertAdjacentHTML("beforeend", `<div>\
-symmetric for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <mrow>\
-    <mo form="${parsedKey.form}" stretchy="true">${parsedKey.characters}</mo>\
-    <mspace height="1.5em"></mspace>\
-  </mrow>\
-</math>\
- VS \
-<math>\
-  <mrow>\
-    <mo form="${parsedKey.form}" stretchy="true" symmetric="${defaultValue}">${parsedKey.characters}</mo>\
-    <mspace height="1.5em"></mspace>\
-  </mrow>\
-</math>\
-</div>`);
-                  var div = document.body.lastElementChild;
-                  var mos = div.getElementsByTagName("mo");
-                  mo = mos[0].getBoundingClientRect()
-                  moRef = mos[1].getBoundingClientRect()
-                  assert_approx_equals(mo.height, moRef.height, epsilon, `Symmetric property for ${key} should be '${defaultValue}'`);
-                  div.style.display = "none";
-              });
-          }
-
-          tests["accent"].step(function() {
-              // FIXME: Should really detect accent support...
-              assert_true(MathMLFeatureDetection.has_mover());
-              var defaultValue = defaultPropertyValue(entry, "accent");
-              document.body.insertAdjacentHTML("beforeend", `<div>\
-accent for "${parsedKey.characters}" (${parsedKey.form}): \
-<math>\
-  <mover>\
-    <mn>&nbsp;</mn>\
-    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
-  </mover>\
-</math>\
- VS \
-<math>\
-  <mover>\
-    <mn>&nbsp;</mn>\
-    <mo form="${parsedKey.form}" accent="${defaultValue}">${parsedKey.characters}</mo>\
-  </mover>\
-</math>\
-</div>`);
-              var div = document.body.lastElementChild;
-              var movers = div.getElementsByTagName("mover");
-              function gapBetweenBaseAndScript(mover) {
-                  return mover.children[0].getBoundingClientRect().top -
-                      mover.children[1].getBoundingClientRect().bottom;
-              }
-              var gap = gapBetweenBaseAndScript(movers[0])
-              var gapRef = gapBetweenBaseAndScript(movers[1])
-              assert_approx_equals(gap, gapRef, epsilon, `Accent property for ${key} should be '${defaultValue}'`);
-              div.style.display = "none";
-          });
-
-          counter++;
-      }
-
-      // Complete current async tests.
-      for (name in tests) {
-          if (tests[name]) tests[name].done();
-      }
-
+      OperatorDictionaryTests.run(json, "lspace/rspace");
       done();
   }
 </script>
diff --git a/mathml/presentation-markup/operators/operator-dictionary-002.html b/mathml/presentation-markup/operators/operator-dictionary-002.html
new file mode 100644
index 0000000..ede3adc
--- /dev/null
+++ b/mathml/presentation-markup/operators/operator-dictionary-002.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Operator dictionary</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dictionary-based-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-dictionary">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis">
+<meta name="assert" content="Verify default properties for characters that are in the operator dictionary, as well as for U+00A0 NO-BREAK SPACE">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/operator-dictionary.js"></script>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
+  async function runTests() {
+      let json = await fetchOperatorDictionary();
+      OperatorDictionaryTests.run(json, "movablelimits");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/mathml/presentation-markup/operators/operator-dictionary-003.html b/mathml/presentation-markup/operators/operator-dictionary-003.html
new file mode 100644
index 0000000..bb3c638
--- /dev/null
+++ b/mathml/presentation-markup/operators/operator-dictionary-003.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Operator dictionary</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dictionary-based-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-dictionary">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis">
+<meta name="assert" content="Verify default properties for characters that are in the operator dictionary, as well as for U+00A0 NO-BREAK SPACE">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/operator-dictionary.js"></script>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
+  async function runTests() {
+      let json = await fetchOperatorDictionary();
+      OperatorDictionaryTests.run(json, "largeop");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/mathml/presentation-markup/operators/operator-dictionary-004.html b/mathml/presentation-markup/operators/operator-dictionary-004.html
new file mode 100644
index 0000000..79bf523
--- /dev/null
+++ b/mathml/presentation-markup/operators/operator-dictionary-004.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Operator dictionary</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dictionary-based-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-dictionary">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis">
+<meta name="assert" content="Verify default properties for characters that are in the operator dictionary, as well as for U+00A0 NO-BREAK SPACE">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/operator-dictionary.js"></script>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
+  async function runTests() {
+      let json = await fetchOperatorDictionary();
+      OperatorDictionaryTests.run(json, "stretchy");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/mathml/presentation-markup/operators/operator-dictionary-005.html b/mathml/presentation-markup/operators/operator-dictionary-005.html
new file mode 100644
index 0000000..4c1ee6d
--- /dev/null
+++ b/mathml/presentation-markup/operators/operator-dictionary-005.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Operator dictionary</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dictionary-based-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-dictionary">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis">
+<meta name="assert" content="Verify default properties for characters that are in the operator dictionary, as well as for U+00A0 NO-BREAK SPACE">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/operator-dictionary.js"></script>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
+  async function runTests() {
+      let json = await fetchOperatorDictionary();
+      OperatorDictionaryTests.run(json, "symmetric");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/mathml/presentation-markup/operators/operator-dictionary-006.html b/mathml/presentation-markup/operators/operator-dictionary-006.html
new file mode 100644
index 0000000..cdd3e16
--- /dev/null
+++ b/mathml/presentation-markup/operators/operator-dictionary-006.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Operator dictionary</title>
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-fence-separator-or-accent-mo">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dictionary-based-attributes">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#operator-dictionary">
+<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis">
+<meta name="assert" content="Verify default properties for characters that are in the operator dictionary, as well as for U+00A0 NO-BREAK SPACE">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/mathml/support/feature-detection.js"></script>
+<script src="/mathml/support/operator-dictionary.js"></script>
+<script src="./support/operator-dictionary-tests.js"></script>
+<link rel="stylesheet" href="./support/operator-dictionary-tests.css"/>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("load", () => { document.fonts.ready.then(runTests); });
+  async function runTests() {
+      let json = await fetchOperatorDictionary();
+      OperatorDictionaryTests.run(json, "accent");
+      done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+</body>
+</html>
diff --git a/mathml/presentation-markup/operators/support/operator-dictionary-tests.css b/mathml/presentation-markup/operators/support/operator-dictionary-tests.css
new file mode 100644
index 0000000..4f80ad7
--- /dev/null
+++ b/mathml/presentation-markup/operators/support/operator-dictionary-tests.css
@@ -0,0 +1,10 @@
+@font-face {
+    font-family: operators;
+    src: url("/fonts/math/operators.woff");
+}
+math, math * {
+    font-family: operators;
+    /* Use large enough font-size so that 1/18em = 2.77px > epsilon and
+       one can really distinguish lspace/rspace values. */
+    font-size: 50px;
+}
diff --git a/mathml/presentation-markup/operators/support/operator-dictionary-tests.js b/mathml/presentation-markup/operators/support/operator-dictionary-tests.js
new file mode 100644
index 0000000..838a22c
--- /dev/null
+++ b/mathml/presentation-markup/operators/support/operator-dictionary-tests.js
@@ -0,0 +1,256 @@
+var OperatorDictionaryTests = {
+    "lspace/rspace": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        assert_true(MathMLFeatureDetection.has_operator_spacing());
+        document.body.insertAdjacentHTML("beforeend", `<div>\
+lspace/rspace for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <mrow>\
+    <mn>&nbsp;</mn>\
+    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
+    <mn>&nbsp;</mn>\
+  </mrow>\
+</math>\
+ VS \
+<math>\
+  <mrow>\
+    <mn>&nbsp;</mn>\
+    <mo form="${parsedKey.form}" lspace="${defaultPropertyValue(entry, 'lspace')}" rspace="${defaultPropertyValue(entry, 'rspace')}">${parsedKey.characters}</mo>\
+    <mn>&nbsp;</mn>\
+  </mrow>\
+</math>\
+</div>`);
+        var div = document.body.lastElementChild;
+        var mrows = div.getElementsByTagName("mrow");
+        function spaceBetween(element, i, j) {
+            return element.children[j].getBoundingClientRect().left -
+                element.children[i].getBoundingClientRect().right;
+        }
+        var lspace = spaceBetween(mrows[0], 0, 1);
+        var rspace = spaceBetween(mrows[0], 1, 2);
+        var lspaceRef = spaceBetween(mrows[1], 0, 1);
+        var rspaceRef = spaceBetween(mrows[1], 1, 2);
+        assert_approx_equals(lspace, lspaceRef, epsilon, `lspace (${key})`);
+        assert_approx_equals(rspace, rspaceRef, epsilon, `rspace (${key})`);
+        div.style.display = "none";
+    },
+
+    "movablelimits": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        assert_true(MathMLFeatureDetection.has_movablelimits());
+        var defaultValue = defaultPropertyValue(entry, "movablelimits");
+        document.body.insertAdjacentHTML("beforeend", `<div>\
+movablelimits for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <munder>\
+    <mo stretchy="false" form="${parsedKey.form}">${parsedKey.characters}</mo>\
+    <mn>&nbsp;</mn>\
+  </munder>\
+</math>\
+ VS \
+<math>\
+  <munder>\
+    <mo stretchy="false" form="${parsedKey.form}" movablelimits="${defaultValue}">${parsedKey.characters}</mo>\
+    <mn>&nbsp;</mn>\
+  </munder>\
+</math>\
+</div>`);
+        var div = document.body.lastElementChild;
+        var munders = div.getElementsByTagName("munder");
+        munder = munders[0].getBoundingClientRect()
+        munderRef = munders[1].getBoundingClientRect()
+        assert_approx_equals(munder.height, munderRef.height, epsilon, `Movablelimits property for ${key} should be '${defaultValue}'`);
+        div.style.display = "none";
+    },
+
+    "largeop": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        // FIXME: Should really detect largeop support...
+        assert_true(MathMLFeatureDetection.has_mspace());
+        var defaultValue = defaultPropertyValue(entry, "largeop");
+        document.body.insertAdjacentHTML("beforeend", `<div>\
+largeop for "${parsedKey.characters}" (${parsedKey.form}): \
+<math displaystyle="true">\
+  <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
+</math>\
+ VS \
+<math displaystyle="true">\
+  <mo form="${parsedKey.form}" largeop="${defaultValue}">${parsedKey.characters}</mo>\
+</math>\
+</div>`);
+        var div = document.body.lastElementChild;
+        var mos = div.getElementsByTagName("mo");
+        mo = mos[0].getBoundingClientRect()
+        moRef = mos[1].getBoundingClientRect()
+        assert_approx_equals(mo.height, moRef.height, epsilon, `Largeop property for ${key} should be '${defaultValue}'`);
+        div.style.display = "none";
+    },
+
+    "stretchy": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        if (entry.horizontal) {
+            // FIXME: Should really detect stretchy support...
+            assert_true(MathMLFeatureDetection.has_munder());
+            var defaultValue = defaultPropertyValue(entry, "stretchy");
+            document.body.insertAdjacentHTML("beforeend", `<div>\
+stretchy for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <munder>\
+    <mn>&nbsp;&nbsp;</mn>\
+    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
+  </munder>\
+</math>\
+ VS \
+<math>\
+  <munder>\
+    <mn>&nbsp;&nbsp;</mn>\
+    <mo form="${parsedKey.form}" stretchy="${defaultValue}">${parsedKey.characters}</mo>\
+  </munder>\
+</math>\
+</div>`);
+            var div = document.body.lastElementChild;
+            var mos = div.getElementsByTagName("mo");
+            mo = mos[0].getBoundingClientRect()
+            moRef = mos[1].getBoundingClientRect()
+            assert_approx_equals(mo.width, moRef.width, epsilon, `Stretchy property for ${key} should be '${defaultValue}'`);
+            div.style.display = "none";
+        } else {
+            // FIXME: Should really detect stretchy support...
+            assert_true(MathMLFeatureDetection.has_mspace());
+            var defaultValue = defaultPropertyValue(entry, "stretchy");
+            document.body.insertAdjacentHTML("beforeend", `<div>\
+stretchy for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <mrow>\
+    <mo form="${parsedKey.form}" symmetric="false">${parsedKey.characters}</mo>\
+    <mspace height="2em"></mspace>\
+  </mrow>\
+</math>\
+ VS \
+<math>\
+  <mrow>\
+    <mo form="${parsedKey.form}" symmetric="false" stretchy="${defaultValue}">${parsedKey.characters}</mo>\
+    <mspace height="2em"></mspace>\
+  </mrow>\
+</math>\
+</div>`);
+            var div = document.body.lastElementChild;
+            var mos = div.getElementsByTagName("mo");
+            mo = mos[0].getBoundingClientRect()
+            moRef = mos[1].getBoundingClientRect()
+            assert_approx_equals(mo.height, moRef.height, epsilon, `Stretchy property for ${key} should be '${defaultValue}'`);
+            div.style.display = "none";
+        }
+    },
+
+    "symmetric": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        // FIXME: Should really detect symmetric support...
+        assert_true(MathMLFeatureDetection.has_mspace());
+        var defaultValue = defaultPropertyValue(entry, "symmetric");
+        document.body.insertAdjacentHTML("beforeend", `<div>\
+symmetric for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <mrow>\
+    <mo form="${parsedKey.form}" stretchy="true">${parsedKey.characters}</mo>\
+    <mspace height="1.5em"></mspace>\
+  </mrow>\
+</math>\
+ VS \
+<math>\
+  <mrow>\
+    <mo form="${parsedKey.form}" stretchy="true" symmetric="${defaultValue}">${parsedKey.characters}</mo>\
+    <mspace height="1.5em"></mspace>\
+  </mrow>\
+</math>\
+</div>`);
+        var div = document.body.lastElementChild;
+        var mos = div.getElementsByTagName("mo");
+        mo = mos[0].getBoundingClientRect()
+        moRef = mos[1].getBoundingClientRect()
+        assert_approx_equals(mo.height, moRef.height, epsilon, `Symmetric property for ${key} should be '${defaultValue}'`);
+        div.style.display = "none";
+    },
+
+    "accent": function(json, key) {
+        let parsedKey = splitKey(key);
+        let entry = json.dictionary[key];
+        let epsilon = 1;
+
+        // FIXME: Should really detect accent support...
+        assert_true(MathMLFeatureDetection.has_mover());
+        var defaultValue = defaultPropertyValue(entry, "accent");
+        document.body.insertAdjacentHTML("beforeend", `<div>\
+accent for "${parsedKey.characters}" (${parsedKey.form}): \
+<math>\
+  <mover>\
+    <mn>&nbsp;</mn>\
+    <mo form="${parsedKey.form}">${parsedKey.characters}</mo>\
+  </mover>\
+</math>\
+ VS \
+<math>\
+  <mover>\
+    <mn>&nbsp;</mn>\
+    <mo form="${parsedKey.form}" accent="${defaultValue}">${parsedKey.characters}</mo>\
+  </mover>\
+</math>\
+</div>`);
+        var div = document.body.lastElementChild;
+        var movers = div.getElementsByTagName("mover");
+        function gapBetweenBaseAndScript(mover) {
+            return mover.children[0].getBoundingClientRect().top -
+                mover.children[1].getBoundingClientRect().bottom;
+        }
+        var gap = gapBetweenBaseAndScript(movers[0])
+        var gapRef = gapBetweenBaseAndScript(movers[1])
+        assert_approx_equals(gap, gapRef, epsilon, `Accent property for ${key} should be '${defaultValue}'`);
+        div.style.display = "none";
+    },
+
+    run: function(json, name) {
+        // The operator dictionary has more than one thousand of entries so the
+        // tests are grouped in chunks so that these don't get much more
+        // importance than other MathML tests. For easy debugging, one can set the
+        // chunk size to 1. Also, note that the test div will remain visible for
+        // failed tests.
+        const entryPerChunk = 50
+
+        var counter = 0;
+        var test;
+
+        for (key in json.dictionary) {
+
+            if (counter % entryPerChunk === 0) {
+                // Start of a new chunk.
+                // Complete current async tests and create new ones for the next chunk.
+                if (test) test.done();
+                test = async_test(`Operator dictionary chunk ${1 + counter / entryPerChunk} - ${name}`);
+            }
+
+            test.step(function() {
+                OperatorDictionaryTests[name](json, key);
+            });
+
+          counter++;
+        }
+
+        // Complete current async test.
+        if (test) test.done();
+    }
+};