phase 16 implementation
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fail, pass, repositoryRoot } from './lib.mjs';
|
||||
|
||||
function parseCsv(text) {
|
||||
const rows = [];
|
||||
let row = [];
|
||||
let field = '';
|
||||
let quoted = false;
|
||||
for (let index = 0; index < text.length; index += 1) {
|
||||
const character = text[index];
|
||||
if (quoted) {
|
||||
if (character === '"' && text[index + 1] === '"') {
|
||||
field += '"';
|
||||
index += 1;
|
||||
} else if (character === '"') {
|
||||
quoted = false;
|
||||
} else {
|
||||
field += character;
|
||||
}
|
||||
} else if (character === '"') {
|
||||
quoted = true;
|
||||
} else if (character === ',') {
|
||||
row.push(field);
|
||||
field = '';
|
||||
} else if (character === '\n') {
|
||||
row.push(field.replace(/\r$/, ''));
|
||||
rows.push(row);
|
||||
row = [];
|
||||
field = '';
|
||||
} else {
|
||||
field += character;
|
||||
}
|
||||
}
|
||||
if (quoted) throw new Error('Unterminated CSV quote.');
|
||||
if (field || row.length) {
|
||||
row.push(field.replace(/\r$/, ''));
|
||||
rows.push(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
const failures = [];
|
||||
for (const file of [
|
||||
'docs/IMPLEMENTATION_ISSUE_REGISTER_v1.4.csv',
|
||||
'docs/quality/PHASE_14_MANUAL_QA_RESULTS_v1.0.csv',
|
||||
]) {
|
||||
const rows = parseCsv(await readFile(path.join(repositoryRoot, file), 'utf8'));
|
||||
const expected = rows[0]?.length ?? 0;
|
||||
for (const [index, row] of rows.entries()) {
|
||||
if (row.length !== expected) {
|
||||
failures.push(`${file}:${index + 1} has ${row.length} columns; expected ${expected}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const contentType = await readFile(
|
||||
path.join(repositoryRoot, 'src/lib/security/content-type.ts'),
|
||||
'utf8',
|
||||
);
|
||||
const route = await readFile(path.join(repositoryRoot, 'src/app/api/demo/route.ts'), 'utf8');
|
||||
if (!contentType.includes("=== 'application/json'")) {
|
||||
failures.push('Exact JSON media-type comparison is missing.');
|
||||
}
|
||||
if (!route.includes('isApplicationJson(contentType)')) {
|
||||
failures.push('Demo API does not use the exact JSON media-type helper.');
|
||||
}
|
||||
if (route.includes("startsWith('application/json')")) {
|
||||
failures.push('Demo API still accepts look-alike JSON media types by prefix.');
|
||||
}
|
||||
|
||||
const componentVisual = await readFile(
|
||||
path.join(repositoryRoot, 'tests/visual/component-system.visual.spec.ts'),
|
||||
'utf8',
|
||||
);
|
||||
const integrationVisual = await readFile(
|
||||
path.join(repositoryRoot, 'tests/visual/integrations.visual.spec.ts'),
|
||||
'utf8',
|
||||
);
|
||||
if (!componentVisual.includes("hpc.theme.preference")) {
|
||||
failures.push('Component visual test does not use the canonical theme storage key.');
|
||||
}
|
||||
if (componentVisual.includes('rdg-theme-preference')) {
|
||||
failures.push('Component visual test still uses the stale theme storage key.');
|
||||
}
|
||||
for (const [name, text] of [
|
||||
['component-system.visual.spec.ts', componentVisual],
|
||||
['integrations.visual.spec.ts', integrationVisual],
|
||||
]) {
|
||||
if (!text.includes("project.name !== 'chromium'")) {
|
||||
failures.push(`${name} is not restricted to canonical Chromium snapshots.`);
|
||||
}
|
||||
}
|
||||
|
||||
const workflow = await readFile(path.join(repositoryRoot, '.github/workflows/ci.yml'), 'utf8');
|
||||
for (const command of [
|
||||
'pnpm test:integration',
|
||||
'pnpm test:security',
|
||||
'pnpm test:privacy',
|
||||
'pnpm test:browser',
|
||||
'pnpm test:a11y',
|
||||
'pnpm test:visual',
|
||||
]) {
|
||||
if (!workflow.includes(command)) failures.push(`CI omits required command: ${command}`);
|
||||
}
|
||||
|
||||
if (failures.length) fail(failures);
|
||||
else
|
||||
pass(
|
||||
'Phase 15 evidence CSVs, exact media-type control, deterministic visual setup, and CI test gates are present.',
|
||||
);
|
||||
Reference in New Issue
Block a user