| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- /**
- * CommonMark 0.31.2 spec conformance runner.
- *
- * Loads js/statum.js through the DOM shim, renders every example in
- * spec-tests.json and compares (whitespace-normalised) against the expected
- * HTML. Sections listed in OUT_OF_SCOPE are counted separately: they cover
- * raw HTML pass-through and schemes we deliberately neutralise, so they are
- * excluded from the pass-rate denominator.
- *
- * Usage: qjs tests/markdown/spec-test.js [section-filter]
- */
- import * as std from 'std';
- import { installShim } from './shim.js';
- var ROOT = import.meta.url ? '.' : '.';
- function fail(msg) {
- std.err.puts(msg + '\n');
- std.exit(1);
- }
- installShim();
- (0, eval)(std.loadFile('js/statum.js'));
- var render = globalThis.statum._testMarkdown;
- if (typeof render !== 'function') {
- fail('statum._testMarkdown is not exposed — was __STATUM_TEST__ set before loading?');
- }
- /** cmark-style normalisation: collapse whitespace runs, trim the ends. */
- function normalize(html) {
- return String(html).replace(/[ \t\r\n]+/g, ' ').trim();
- }
- var OUT_OF_SCOPE = {
- 'HTML blocks': 'raw HTML blocks are escaped, not passed through',
- 'Raw HTML': 'inline raw HTML is escaped, not passed through'
- };
- var data;
- try {
- data = JSON.parse(std.loadFile('tests/markdown/spec-tests.json'));
- } catch (e) {
- fail('cannot read tests/markdown/spec-tests.json: ' + e);
- }
- var filter = scriptArgs.length > 1 ? scriptArgs[1] : null;
- var sections = {};
- var order = [];
- var pass = 0, failCount = 0, skipped = 0;
- var failures = [];
- for (var i = 0; i < data.length; i++) {
- var t = data[i];
- if (filter && t.section.indexOf(filter) < 0) continue;
- if (Object.prototype.hasOwnProperty.call(OUT_OF_SCOPE, t.section)) { skipped++; continue; }
- var got;
- try {
- got = normalize(render(t.markdown));
- } catch (e) {
- got = '<<threw ' + e + '>>';
- }
- var expected = normalize(t.html);
- var sec = sections[t.section];
- if (!sec) {
- sec = sections[t.section] = { pass: 0, fail: 0 };
- order.push(t.section);
- }
- if (got === expected) {
- pass++; sec.pass++;
- } else {
- failCount++; sec.fail++;
- failures.push({ example: t.example, section: t.section, markdown: t.markdown, html: t.html, got: got });
- }
- }
- print('CommonMark 0.31.2 spec suite (in scope: ' + (pass + failCount) + ', out of scope: ' + skipped + ')');
- print('');
- for (var s = 0; s < order.length; s++) {
- var st = sections[order[s]];
- var total = st.pass + st.fail;
- print(' ' + pad(order[s], 38) + ' ' + pad(String(st.pass) + '/' + total, 8) +
- bar(st.pass, total));
- }
- print('');
- var rate = (pass + failCount) ? (100 * pass / (pass + failCount)) : 0;
- print(' ' + pad('TOTAL', 38) + ' ' + pad(pass + '/' + (pass + failCount), 8) + rate.toFixed(1) + '%');
- print('');
- if (failures.length && (!filter || scriptArgs.indexOf('-v') >= 0)) {
- print('failures:');
- for (var f = 0; f < failures.length; f++) {
- var x = failures[f];
- print(' #' + x.example + ' [' + x.section + '] ' + JSON.stringify(x.markdown));
- print(' got: ' + x.got);
- print(' exp: ' + normalize(x.html));
- }
- print('');
- }
- function pad(s, n) {
- while (s.length < n) s += ' ';
- return s;
- }
- function bar(p, total) {
- if (!total) return '';
- var filled = Math.round(20 * p / total);
- var s = '';
- for (var i = 0; i < 20; i++) s += i < filled ? '#' : '.';
- return ' |' + s + '|';
- }
- // Exit status is decided by the caller (run.sh) based on the target rate; we
- // print the numbers and fail only on hard errors above.
- std.exit(0);
|