test(voyage): extend playground tests with Group A static-HTML assertions [skip-docs]
Step 28 of v4.3 plan — Wave 7 Group A: 17 new static-HTML grep assertions covering SC1 10-element checklist (one test per element), SC3 webkitdirectory + drag-drop attributes, SC6 export-bundle markers (buildAnnotatedMarkdown, filename pattern, Blob + clipboard flows), and SC7 tag-level no-CDN checks (every <script src> + <link href> must be local). Test count: 672 → 689 pass / 0 fail / 2 skipped.
This commit is contained in:
parent
b88c120680
commit
deca35a28f
1 changed files with 148 additions and 0 deletions
|
|
@ -502,3 +502,151 @@ test('voyage-playground.html loadProjectDirectory wires isProjectPathSafe filter
|
|||
// aria-live announce must fire when something is filtered
|
||||
assert.match(text, /announce\(filteredCount/, 'filteredCount announce required');
|
||||
});
|
||||
|
||||
// =====================================================================
|
||||
// v4.3 Step 28 — Group A static-HTML assertions (Wave 7).
|
||||
//
|
||||
// SC1 10-element checklist (one test per element), SC3 webkitdirectory +
|
||||
// drag-drop attributes, SC6 export-bundle markers, SC7 no-CDN tag-level
|
||||
// checks. All assertions read voyage-playground.html as text — no DOM,
|
||||
// no browser. The HTML is FROZEN in Session 6; if any assertion fails
|
||||
// the test must be adjusted to reflect actual state, not the HTML.
|
||||
// =====================================================================
|
||||
|
||||
// --- SC1 element 1 — header / app-shell topbar -----------------------
|
||||
test('SC1.1 header — app-shell topbar with breadcrumb (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /class="app-header__breadcrumb"/, 'breadcrumb required');
|
||||
assert.match(text, /aria-label="Brødsmuler"/, 'breadcrumb aria-label required');
|
||||
});
|
||||
|
||||
// --- SC1 element 2 — breadcrumb interactive return path --------------
|
||||
test('SC1.2 breadcrumb — clickable returns to dashboard (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /breadcrumb-click/, 'breadcrumb-click handler required');
|
||||
});
|
||||
|
||||
// --- SC1 element 3 — theme bootstrap IIFE ----------------------------
|
||||
test('SC1.3 theme bootstrap — IIFE sets data-theme + colorScheme (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /<html[^>]+data-theme="dark"/, 'html data-theme=dark required');
|
||||
assert.match(text, /prefers-color-scheme:\s*dark/, 'OS preference fallback required');
|
||||
assert.match(text, /setAttribute\('data-theme'/, 'data-theme setter required');
|
||||
});
|
||||
|
||||
// --- SC1 element 4 — onboarding-grid (redefined as fleet-grid) -------
|
||||
// Per scope-guardian SC-GAP-1 (Assumptions row #21): voyage redefines
|
||||
// onboarding-grid as fleet-grid. Operator-signed-off; /trekreview may
|
||||
// flag this for revision.
|
||||
test('SC1.4 onboarding-grid equivalent — fleet-grid pattern (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /class="fleet-grid"/, 'fleet-grid container required');
|
||||
assert.match(text, /fleet-tile/, 'fleet-tile children required');
|
||||
});
|
||||
|
||||
// --- SC1 element 5 — A11Y panel built from DS-primitives -------------
|
||||
test('SC1.5 A11Y panel — guide-panel--info + key-stats + findings (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /guide-panel guide-panel--info/, 'guide-panel--info required');
|
||||
assert.match(text, /class="key-stats"/, 'key-stats severity-grid required');
|
||||
assert.match(text, /class="findings__items"/, 'findings__items list required');
|
||||
assert.match(text, /wireA11yToggle/, 'A11Y wiring function required');
|
||||
});
|
||||
|
||||
// --- SC1 element 6 — screenshots-spor convention ---------------------
|
||||
// Per scope-guardian SC-GAP-2 (Assumptions row #22): hooks + dir convention
|
||||
// instead of inline gallery. Operator-signed-off.
|
||||
test('SC1.6 screenshots-spor — window.__voyage hooks + docs convention (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /window\.__voyage\s*=/, 'window.__voyage namespace required');
|
||||
assert.match(text, /docs\/screenshots\/README\.md/, 'docs/screenshots reference required');
|
||||
// Companion file must exist
|
||||
const SCREENSHOTS_README = join(ROOT, 'docs', 'screenshots', 'README.md');
|
||||
assert.ok(existsSync(SCREENSHOTS_README), 'docs/screenshots/README.md must exist');
|
||||
});
|
||||
|
||||
// --- SC1 element 7 — body typography -----------------------------------
|
||||
test('SC1.7 body typography — DS font-size + family tokens (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /var\(--font-size-/, 'DS font-size token required');
|
||||
assert.match(text, /var\(--font-family-mono\)/, 'DS font-family-mono token required');
|
||||
});
|
||||
|
||||
// --- SC1 element 8 — spacing rhythm ------------------------------------
|
||||
test('SC1.8 spacing rhythm — DS --space-N tokens used (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// Expect at least 5 distinct --space-N references (rhythm, not one-off)
|
||||
const matches = text.match(/var\(--space-\d/g) || [];
|
||||
assert.ok(matches.length >= 5, `expected ≥5 --space-N tokens, got ${matches.length}`);
|
||||
});
|
||||
|
||||
// --- SC1 element 9 — color-token fidelity ------------------------------
|
||||
test('SC1.9 color-token fidelity — voyage-scope tokens + DS colors (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// Voyage-scope tokens were added in Step 1 (DS base.css) and consumed by playground
|
||||
assert.match(text, /badge--scope-voyage|--color-scope-voyage/, 'voyage-scope token usage required');
|
||||
});
|
||||
|
||||
// --- SC1 element 10 — dark-mode parity --------------------------------
|
||||
test('SC1.10 dark-mode parity — explicit dark default + bootstrap (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// html element ships data-theme=dark, theme-bootstrap respects user setting
|
||||
assert.match(text, /<html[^>]+data-theme="dark"/, 'dark default required');
|
||||
assert.match(text, /voyage-theme|voyage_theme/, 'theme persistence key required');
|
||||
});
|
||||
|
||||
// --- SC3 — webkitdirectory + drag-drop attribute presence -------------
|
||||
test('SC3 webkitdirectory — input declares directory attribute (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// The input element on line 849 has webkitdirectory as an HTML attribute
|
||||
assert.match(text, /\bwebkitdirectory\b/, 'webkitdirectory attribute required');
|
||||
});
|
||||
|
||||
test('SC3 drag-drop — webkitGetAsEntry recursive walk (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /webkitGetAsEntry/, 'webkitGetAsEntry recursive entry-point required');
|
||||
assert.match(text, /addEventListener\('dragenter/, 'dragenter handler required');
|
||||
assert.match(text, /addEventListener\('dragover/, 'dragover handler required');
|
||||
assert.match(text, /addEventListener\('dragleave/, 'dragleave handler required');
|
||||
});
|
||||
|
||||
// --- SC6 export-bundle markers ----------------------------------------
|
||||
test('SC6 export — buildAnnotatedMarkdown function exists (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /function\s+buildAnnotatedMarkdown\s*\(/, 'buildAnnotatedMarkdown required');
|
||||
});
|
||||
|
||||
test('SC6 export — download filename pattern annotated-{target}.md (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /'annotated-'\s*\+\s*target\s*\+\s*'\.md'/, 'filename pattern required');
|
||||
});
|
||||
|
||||
test('SC6 export — Blob + clipboard.writeText flows wired (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
assert.match(text, /new\s+Blob\(/, 'Blob construction required');
|
||||
assert.match(text, /clipboard\.writeText/, 'clipboard copy flow required');
|
||||
});
|
||||
|
||||
// --- SC7 no-CDN tag-level checks ---------------------------------------
|
||||
test('SC7 no-CDN — every <script src=...> is local (./lib/* etc) (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
// Match all <script src="..."> attribute values
|
||||
const scriptSrcs = [...text.matchAll(/<script\b[^>]*\bsrc\s*=\s*"([^"]+)"/g)].map((m) => m[1]);
|
||||
for (const src of scriptSrcs) {
|
||||
assert.ok(
|
||||
!/^https?:\/\//.test(src) && !/^\/\//.test(src),
|
||||
`script src="${src}" must be local (no http/https/protocol-relative)`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('SC7 no-CDN — every <link href=...> is local (v4.3 Step 28)', () => {
|
||||
const text = readFileSync(HTML, 'utf-8');
|
||||
const linkHrefs = [...text.matchAll(/<link\b[^>]*\bhref\s*=\s*"([^"]+)"/g)].map((m) => m[1]);
|
||||
for (const href of linkHrefs) {
|
||||
assert.ok(
|
||||
!/^https?:\/\//.test(href) && !/^\/\//.test(href),
|
||||
`link href="${href}" must be local (no http/https/protocol-relative)`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue