* test(#4657): add failing-first coverage for the ui probe's text_en channel Mirrors the #3717/#4156 test shape onto the UI adapter: a failing-first proposeConsiderations regression (Danish text + English text_en must classify as its English equivalent, not land in the #1110 unclassified sentinel), proposeElements/analyzeCoverage/CLI end-to-end pairs, fail-closed text_en validation cases (empty/whitespace/non-string, unconditional under an elements override), a ui-phase.md Step 9.5 workflow-prose contract test, a reference-doc Inputs parity test, and a fast-check property proving any cue-matching prose classifies identically under a cue-free Danish rendering plus text_en. All new assertions are RED until src/ui-consideration-probe.cts and the workflow/reference docs are updated. * fix(#4657): give the ui consideration probe a text_en language channel Element gains an optional text_en; classifyElement's own signature stays untouched (a locked, directly-tested export) and the text_en ?? text selection is pushed to the two classification call sites (proposeConsiderations, proposeElements) instead. text_en is validated fail-closed: an empty or whitespace-only value throws rather than silently winning the ?? fallback and degrading classification to zero kinds. Mirrors #3717/#4156 onto the UI adapter: ui-phase.md Step 9.5 gains the Non-English projects section (mirroring spec-phase Step 5.5) and the ELEMENTS_JSON shape comment documents the field with both zero-applicable guard arms named; the reference doc's Inputs section, the PROBE.ui CONTEXT predicate (with both derived indexes regenerated), and the nav-override test expectation stay in sync. The ui-phase contract test carries the site-scoped allow-test-rule marker and its cluster is registered in the test-file-count allowlist ratchet. Emitted-Drift-Ack-Growth: ui-phase.md — Non-English text_en section, ELEMENTS_JSON shape comment, and two-arm guard wording (#4657) * docs(#4657): backfill changeset PR number --------- Co-authored-by: sim <sim@local>
78 lines
4.1 KiB
JavaScript
78 lines
4.1 KiB
JavaScript
// allow-test-rule: source-text-is-the-product (see #1867) — the rendered reference doc's taxonomy table IS the runtime contract; this pins its bijection to the code (docs-parity, ADR-456 exception matrix)
|
|
// Asserts gsd-core/references/ui-consideration-probe.md keeps its taxonomy id column in
|
|
// sync with the source-of-truth UI_TAXONOMY (built .cjs), and that the closed compiled
|
|
// taxonomy stays DISJOINT from the open-prose domain-probes.md bank (the mixed-axis boundary,
|
|
// ADPT-02). The comparison is on PARSED table ids and PARSED `##` headings, never a raw
|
|
// full-text substring match — a reformat that preserves the data does not fail; semantic drift does.
|
|
'use strict';
|
|
process.env.GSD_TEST_MODE = '1';
|
|
|
|
const { test, describe } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const uc = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'ui-consideration-probe.cjs'));
|
|
const docPath = path.join(__dirname, '..', 'gsd-core', 'references', 'ui-consideration-probe.md');
|
|
const domainPath = path.join(__dirname, '..', 'gsd-core', 'references', 'domain-probes.md');
|
|
const templatePath = path.join(__dirname, '..', 'gsd-core', 'templates', 'UI-SPEC.md');
|
|
|
|
// Extract the first-column ids from the `## Taxonomy` markdown table (skips the `id` header
|
|
// row and the `|----|` separator; an id is a lowercase-hyphen token).
|
|
function docTaxonomyIds(md) {
|
|
const section = md.split(/^## Taxonomy/m)[1].split(/^## /m)[0];
|
|
return section.split('\n')
|
|
.filter((l) => l.trim().startsWith('|'))
|
|
.map((l) => l.split('|')[1].trim())
|
|
.filter((c) => /^[a-z][a-z0-9-]*$/.test(c) && c !== 'id');
|
|
}
|
|
|
|
// The `##` topic headings of the open-prose bank (lower-cased).
|
|
function domainTopics(md) {
|
|
return md.split('\n')
|
|
.filter((l) => /^## /.test(l))
|
|
.map((l) => l.replace(/^## /, '').trim().toLowerCase());
|
|
}
|
|
|
|
describe('ui-consideration-probe doc/code parity (ADPT-02)', () => {
|
|
test('reference doc exists', () => {
|
|
assert.ok(fs.existsSync(docPath), `${docPath} must exist`);
|
|
});
|
|
|
|
test('doc taxonomy ids deep-equal the code UI_TAXONOMY ids, in order (doc == code)', () => {
|
|
const md = fs.readFileSync(docPath, 'utf8');
|
|
assert.deepEqual(docTaxonomyIds(md), uc.UI_TAXONOMY.map((c) => c.id));
|
|
});
|
|
|
|
test('no taxonomy id overlaps a domain-probes.md open-prose topic (closed/open disjointness)', () => {
|
|
const topics = domainTopics(fs.readFileSync(domainPath, 'utf8'));
|
|
for (const id of uc.UI_TAXONOMY.map((c) => c.id)) {
|
|
assert.ok(!topics.includes(id), `taxonomy id "${id}" must not overlap a domain-probes.md topic`);
|
|
}
|
|
});
|
|
|
|
test('the doc names domain-probes.md as the companion open-prose bank (links, does not duplicate)', () => {
|
|
const md = fs.readFileSync(docPath, 'utf8');
|
|
assert.match(md, /domain-probes\.md/);
|
|
});
|
|
|
|
test('Inputs section documents the text_en translation channel (#4657)', () => {
|
|
// PARSED from the `## Inputs` section only (never a whole-file substring) — a reformat
|
|
// that preserves the data does not fail; a dropped field does.
|
|
const md = fs.readFileSync(docPath, 'utf8');
|
|
const inputs = md.split(/^## Inputs/m)[1].split(/^## /m)[0];
|
|
assert.match(inputs, /text_en/, 'Inputs must document the optional text_en field');
|
|
assert.match(inputs, /English/, 'Inputs must say text_en is the classifier-facing English rendering');
|
|
});
|
|
});
|
|
|
|
describe('UI-SPEC template `## UI Considerations` section (WIRE-02 SC3 de-dup)', () => {
|
|
// PARSED `##` headings only (never a raw copy substring) — a reformat that preserves the data
|
|
// does not fail; a missing/merged section does. Reuses the domainTopics() heading parser.
|
|
test('template ## headings include BOTH `UI Considerations` and `Copywriting Contract` as distinct sections', () => {
|
|
const headings = domainTopics(fs.readFileSync(templatePath, 'utf8'));
|
|
assert.ok(headings.includes('ui considerations'), 'template must gain a ## UI Considerations section');
|
|
assert.ok(headings.includes('copywriting contract'), 'template must retain the distinct ## Copywriting Contract section (de-dup, not a rename)');
|
|
});
|
|
});
|