Files
pbakaus_impeccable/tests/detect-static-html-skill-install.test.mjs
T
f4f16e3802 Fix: vendor static-HTML parsers so skill installs detect fully (#434)
Skill and plugin copies of the detector had no htmlparser2/css-select/css-tree/domutils, so HTML scans silently fell back to regex and exited 0. Bundle those parsers into the engine tree and exit 1 if the fallback still fires.

AI assistance: Cursor Grok 4.6 implemented this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 08:24:11 -07:00

104 lines
4.1 KiB
JavaScript

import { after, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const engineDir = path.join(root, 'cli', 'engine');
const skillDetect = path.join(root, 'skill', 'scripts', 'detect.mjs');
const configDep = path.join(root, 'cli', 'lib', 'impeccable-config.mjs');
const CSS = ':root{--grad:linear-gradient(90deg,#7C3AED,#EC4899)}\n.hero h1{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}';
const HTML = '<html><head><link rel=stylesheet href=s.css></head><body><div class=hero><h1>Hi</h1></div></body></html>';
function copyDir(src, dest) {
fs.mkdirSync(dest, { recursive: true });
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) copyDir(srcPath, destPath);
else fs.copyFileSync(srcPath, destPath);
}
}
function copyDetectorExternalDeps(scriptsDir) {
fs.mkdirSync(path.join(scriptsDir, 'lib'), { recursive: true });
fs.copyFileSync(configDep, path.join(scriptsDir, 'lib', 'impeccable-config.mjs'));
}
function writeFixture(dir) {
fs.writeFileSync(path.join(dir, 's.css'), CSS);
fs.writeFileSync(path.join(dir, 'p.html'), HTML);
}
function runDetect(cwd, detectRel, htmlRel = 'p.html') {
const detectPath = path.join(cwd, detectRel);
return spawnSync(
process.execPath,
[detectPath, '--json', '--no-config', '--no-design-system', htmlRel],
{ cwd, encoding: 'utf8' },
);
}
function assertFullEngine(result) {
assert.doesNotMatch(result.stderr, /DEGRADED/);
const findings = JSON.parse(result.stdout);
assert.ok(findings.some((item) => item.antipattern === 'gradient-text'));
assert.equal(result.status, 2);
}
function assertDegraded(result) {
assert.match(result.stderr, /DEGRADED/);
assert.equal(result.status, 1);
JSON.parse(result.stdout);
}
const tempDirs = [];
after(() => {
for (const dir of tempDirs) fs.rmSync(dir, { recursive: true, force: true });
});
describe('static HTML parsers in skill/plugin installs', () => {
it('resolves parsers from a skill-shaped scripts/ tree with no node_modules', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'impeccable-skill-detect-'));
tempDirs.push(tmp);
fs.mkdirSync(path.join(tmp, 'scripts'), { recursive: true });
fs.copyFileSync(skillDetect, path.join(tmp, 'scripts', 'detect.mjs'));
copyDir(engineDir, path.join(tmp, 'scripts', 'detector'));
copyDetectorExternalDeps(path.join(tmp, 'scripts'));
writeFixture(tmp);
assertFullEngine(runDetect(tmp, path.join('scripts', 'detect.mjs')));
});
it('resolves parsers from a plugin cache skills/impeccable/scripts/ tree', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'impeccable-plugin-detect-'));
tempDirs.push(tmp);
const scriptsDir = path.join(tmp, 'skills', 'impeccable', 'scripts');
fs.mkdirSync(scriptsDir, { recursive: true });
fs.copyFileSync(skillDetect, path.join(scriptsDir, 'detect.mjs'));
copyDir(engineDir, path.join(scriptsDir, 'detector'));
copyDetectorExternalDeps(scriptsDir);
writeFixture(tmp);
assertFullEngine(runDetect(tmp, path.join('skills', 'impeccable', 'scripts', 'detect.mjs')));
});
it('exits 1 when the vendor bundle is missing', () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'impeccable-skill-degraded-'));
tempDirs.push(tmp);
fs.mkdirSync(path.join(tmp, 'scripts'), { recursive: true });
fs.copyFileSync(skillDetect, path.join(tmp, 'scripts', 'detect.mjs'));
copyDir(engineDir, path.join(tmp, 'scripts', 'detector'));
copyDetectorExternalDeps(path.join(tmp, 'scripts'));
fs.unlinkSync(path.join(tmp, 'scripts', 'detector', 'vendor', 'static-html-parsers.mjs'));
writeFixture(tmp);
assertDegraded(runDetect(tmp, path.join('scripts', 'detect.mjs')));
});
});