New spec tests: test-cek-try-seq (CEK try/seq), test-htmx (htmx directive coverage, 292L), test-hs-diag, test-perform-chain (IO suspension chains). tests/hs-*.js: Node.js-side hyperscript runners for browser-mode testing (hs-behavioral-node, hs-behavioral-runner, hs-parse-audit, hs-run-timed). Vendors shared/static/scripts/htmx.min.js. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
118 lines
4.2 KiB
JavaScript
118 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Audit: extract all HS sources from behavioral tests and check parse/compile.
|
|
* Uses child_process.execSync with timeout to handle hangs.
|
|
*/
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { execSync } = require('child_process');
|
|
|
|
const PROJECT = path.resolve(__dirname, '..');
|
|
|
|
// Extract HS sources from test file with their suite/test names
|
|
const testSx = fs.readFileSync(path.join(PROJECT, 'spec/tests/test-hyperscript-behavioral.sx'), 'utf8');
|
|
|
|
// Extract suites and their test counts from comments
|
|
const suitePattern = /;; ── (\S+) \((\d+) tests\)/g;
|
|
const suites = [];
|
|
let m;
|
|
while ((m = suitePattern.exec(testSx)) !== null) {
|
|
suites.push({ name: m[1], count: parseInt(m[2]) });
|
|
}
|
|
|
|
console.log('Test suites (831 total):');
|
|
let grandTotal = 0;
|
|
for (const s of suites) {
|
|
grandTotal += s.count;
|
|
console.log(` ${s.name}: ${s.count}`);
|
|
}
|
|
console.log(` TOTAL: ${grandTotal}`);
|
|
|
|
// Categorize tests by type: DOM-action tests vs eval-only tests vs stub tests
|
|
const lines = testSx.split('\n');
|
|
let currentSuite = '';
|
|
let inDeftest = false;
|
|
let testName = '';
|
|
let testBody = '';
|
|
let depth = 0;
|
|
|
|
const testCategories = {};
|
|
|
|
for (const line of lines) {
|
|
const suiteMatch = line.match(/\(defsuite\s+"([^"]+)"/);
|
|
if (suiteMatch) { currentSuite = suiteMatch[1]; continue; }
|
|
|
|
const testMatch = line.match(/\(deftest\s+"([^"]+)"/);
|
|
if (testMatch) { inDeftest = true; testName = testMatch[1]; testBody = line; depth = 1; continue; }
|
|
|
|
if (inDeftest) {
|
|
testBody += '\n' + line;
|
|
depth += (line.match(/\(/g) || []).length - (line.match(/\)/g) || []).length;
|
|
if (depth <= 0) {
|
|
// Categorize this test
|
|
if (!testCategories[currentSuite]) testCategories[currentSuite] = { domAction: 0, evalOnly: 0, stub: 0, notImpl: 0, names: [] };
|
|
|
|
if (testBody.includes('NOT IMPLEMENTED') || testBody.includes('not-implemented')) {
|
|
testCategories[currentSuite].stub++;
|
|
} else if (testBody.includes('dom-dispatch') || testBody.includes('dom-set-inner-html') && testBody.includes('hs-activate!')) {
|
|
testCategories[currentSuite].domAction++;
|
|
testCategories[currentSuite].names.push(testName);
|
|
} else if (testBody.includes('eval-hs') || testBody.includes('hs-eval')) {
|
|
testCategories[currentSuite].evalOnly++;
|
|
} else {
|
|
testCategories[currentSuite].domAction++;
|
|
testCategories[currentSuite].names.push(testName);
|
|
}
|
|
|
|
inDeftest = false;
|
|
testBody = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count unique HS sources
|
|
const attrPattern = /dom-set-attr\s+\S+\s+"_"\s+"([^"]+)"/g;
|
|
const hsSources = new Set();
|
|
while ((m = attrPattern.exec(testSx)) !== null) {
|
|
hsSources.add(m[1].replace(/\\"/g, '"'));
|
|
}
|
|
|
|
console.log(`\n${hsSources.size} unique HS sources across ${grandTotal} tests`);
|
|
|
|
// Analyze HS source complexity
|
|
const features = {
|
|
'on click': 0, 'on load': 0, 'on keyup': 0, 'on mouseenter': 0,
|
|
'on mutation': 0, 'on intersect': 0, 'on every': 0,
|
|
'add .': 0, 'remove .': 0, 'toggle .': 0, 'set ': 0, 'put ': 0,
|
|
'if ': 0, 'repeat': 0, 'wait ': 0, 'send ': 0, 'take ': 0,
|
|
'transition': 0, 'hide': 0, 'show': 0, 'log': 0,
|
|
'call ': 0, 'fetch ': 0, 'tell ': 0, 'halt': 0,
|
|
'morph': 0, 'go ': 0, 'append': 0, 'settle': 0,
|
|
'def ': 0, 'increment': 0, 'decrement': 0,
|
|
'bind ': 0, 'closest': 0, 'in ': 0, 'as ': 0,
|
|
'init': 0, 'every ': 0, 'measure': 0,
|
|
};
|
|
|
|
for (const src of hsSources) {
|
|
for (const feat of Object.keys(features)) {
|
|
if (src.includes(feat)) features[feat]++;
|
|
}
|
|
}
|
|
|
|
console.log('\nFeature usage in test sources:');
|
|
for (const [feat, count] of Object.entries(features).sort((a,b) => b[1] - a[1])) {
|
|
if (count > 0) console.log(` ${feat.trim()}: ${count}`);
|
|
}
|
|
|
|
// Summarize categories
|
|
console.log('\nPer-suite breakdown:');
|
|
let totalDom = 0, totalEval = 0, totalStub = 0;
|
|
for (const [suite, cat] of Object.entries(testCategories).sort((a,b) => (b[1].domAction+b[1].evalOnly) - (a[1].domAction+a[1].evalOnly))) {
|
|
const total = cat.domAction + cat.evalOnly + cat.stub;
|
|
totalDom += cat.domAction;
|
|
totalEval += cat.evalOnly;
|
|
totalStub += cat.stub;
|
|
console.log(` ${suite}: ${total} tests (${cat.domAction} DOM, ${cat.evalOnly} eval, ${cat.stub} stub)`);
|
|
}
|
|
console.log(`\nTotals: ${totalDom} DOM-action, ${totalEval} eval-only, ${totalStub} stubs`);
|