spec-test.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * CommonMark 0.31.2 spec conformance runner.
  3. *
  4. * Loads js/statum.js through the DOM shim, renders every example in
  5. * spec-tests.json and compares (whitespace-normalised) against the expected
  6. * HTML. Sections listed in OUT_OF_SCOPE are counted separately: they cover
  7. * raw HTML pass-through and schemes we deliberately neutralise, so they are
  8. * excluded from the pass-rate denominator.
  9. *
  10. * Usage: qjs tests/markdown/spec-test.js [section-filter]
  11. */
  12. import * as std from 'std';
  13. import { installShim } from './shim.js';
  14. var ROOT = import.meta.url ? '.' : '.';
  15. function fail(msg) {
  16. std.err.puts(msg + '\n');
  17. std.exit(1);
  18. }
  19. installShim();
  20. (0, eval)(std.loadFile('js/statum.js'));
  21. var render = globalThis.statum._testMarkdown;
  22. if (typeof render !== 'function') {
  23. fail('statum._testMarkdown is not exposed — was __STATUM_TEST__ set before loading?');
  24. }
  25. /** cmark-style normalisation: collapse whitespace runs, trim the ends. */
  26. function normalize(html) {
  27. return String(html).replace(/[ \t\r\n]+/g, ' ').trim();
  28. }
  29. var OUT_OF_SCOPE = {
  30. 'HTML blocks': 'raw HTML blocks are escaped, not passed through',
  31. 'Raw HTML': 'inline raw HTML is escaped, not passed through'
  32. };
  33. var data;
  34. try {
  35. data = JSON.parse(std.loadFile('tests/markdown/spec-tests.json'));
  36. } catch (e) {
  37. fail('cannot read tests/markdown/spec-tests.json: ' + e);
  38. }
  39. var filter = scriptArgs.length > 1 ? scriptArgs[1] : null;
  40. var sections = {};
  41. var order = [];
  42. var pass = 0, failCount = 0, skipped = 0;
  43. var failures = [];
  44. for (var i = 0; i < data.length; i++) {
  45. var t = data[i];
  46. if (filter && t.section.indexOf(filter) < 0) continue;
  47. if (Object.prototype.hasOwnProperty.call(OUT_OF_SCOPE, t.section)) { skipped++; continue; }
  48. var got;
  49. try {
  50. got = normalize(render(t.markdown));
  51. } catch (e) {
  52. got = '<<threw ' + e + '>>';
  53. }
  54. var expected = normalize(t.html);
  55. var sec = sections[t.section];
  56. if (!sec) {
  57. sec = sections[t.section] = { pass: 0, fail: 0 };
  58. order.push(t.section);
  59. }
  60. if (got === expected) {
  61. pass++; sec.pass++;
  62. } else {
  63. failCount++; sec.fail++;
  64. failures.push({ example: t.example, section: t.section, markdown: t.markdown, html: t.html, got: got });
  65. }
  66. }
  67. print('CommonMark 0.31.2 spec suite (in scope: ' + (pass + failCount) + ', out of scope: ' + skipped + ')');
  68. print('');
  69. for (var s = 0; s < order.length; s++) {
  70. var st = sections[order[s]];
  71. var total = st.pass + st.fail;
  72. print(' ' + pad(order[s], 38) + ' ' + pad(String(st.pass) + '/' + total, 8) +
  73. bar(st.pass, total));
  74. }
  75. print('');
  76. var rate = (pass + failCount) ? (100 * pass / (pass + failCount)) : 0;
  77. print(' ' + pad('TOTAL', 38) + ' ' + pad(pass + '/' + (pass + failCount), 8) + rate.toFixed(1) + '%');
  78. print('');
  79. if (failures.length && (!filter || scriptArgs.indexOf('-v') >= 0)) {
  80. print('failures:');
  81. for (var f = 0; f < failures.length; f++) {
  82. var x = failures[f];
  83. print(' #' + x.example + ' [' + x.section + '] ' + JSON.stringify(x.markdown));
  84. print(' got: ' + x.got);
  85. print(' exp: ' + normalize(x.html));
  86. }
  87. print('');
  88. }
  89. function pad(s, n) {
  90. while (s.length < n) s += ' ';
  91. return s;
  92. }
  93. function bar(p, total) {
  94. if (!total) return '';
  95. var filled = Math.round(20 * p / total);
  96. var s = '';
  97. for (var i = 0; i < 20; i++) s += i < filled ? '#' : '.';
  98. return ' |' + s + '|';
  99. }
  100. // Exit status is decided by the caller (run.sh) based on the target rate; we
  101. // print the numbers and fail only on hard errors above.
  102. std.exit(0);