Review found the hint value (yes|no) matched prefixes ('nope'/'not' read as
'no') and the hint regex was unanchored, so a mid-line prose mention like 'see
**UI hint**: no above' was treated as the authoritative metadata line. Line-
anchor (^ + m flag) so only a real hint line counts; word-boundary on the value
so nope/not do not mean no. Adds coverage for hint:yes over a pure-backend body
and the nope fall-through.
126 lines
5.1 KiB
JavaScript
126 lines
5.1 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Characterization tests for the UI safety gate module.
|
|
* Locks checkUiPresence behaviour and UI_TOKENS export shape.
|
|
*/
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const {
|
|
checkUiPresence,
|
|
UI_TOKENS,
|
|
} = require('../gsd-core/bin/lib/ui-safety-gate.cjs');
|
|
|
|
describe('UI_TOKENS', () => {
|
|
test('is an array containing expected token strings', () => {
|
|
assert.ok(Array.isArray(UI_TOKENS));
|
|
assert.ok(UI_TOKENS.includes('UI'));
|
|
assert.ok(UI_TOKENS.includes('frontend'));
|
|
assert.ok(UI_TOKENS.includes('component'));
|
|
assert.ok(UI_TOKENS.length > 0);
|
|
});
|
|
});
|
|
|
|
describe('checkUiPresence', () => {
|
|
test('returns { hasUI: false, tokens: [] } for non-string input', () => {
|
|
assert.deepStrictEqual(checkUiPresence(42), { hasUI: false, tokens: [] });
|
|
assert.deepStrictEqual(checkUiPresence(null), { hasUI: false, tokens: [] });
|
|
});
|
|
|
|
test('returns false for empty string', () => {
|
|
assert.deepStrictEqual(checkUiPresence(''), { hasUI: false, tokens: [] });
|
|
});
|
|
|
|
test('detects standalone UI token (case-insensitive)', () => {
|
|
const result = checkUiPresence('This task involves UI work');
|
|
assert.ok(result.hasUI);
|
|
assert.ok(result.tokens.includes('ui'));
|
|
});
|
|
|
|
test('detects frontend token', () => {
|
|
const result = checkUiPresence('Build a frontend component');
|
|
assert.ok(result.hasUI);
|
|
assert.ok(result.tokens.includes('frontend'));
|
|
});
|
|
|
|
test('does NOT match interior of alphanumeric word (bug #3706)', () => {
|
|
// "Requirements" contains "ui" interior — must NOT match
|
|
const result = checkUiPresence('Requirements analysis');
|
|
assert.ok(!result.hasUI, 'Requirements should not trigger UI gate');
|
|
|
|
// "microfrontend" is all-alphanumeric — must NOT match
|
|
const result2 = checkUiPresence('microfrontend architecture');
|
|
assert.ok(!result2.hasUI, 'microfrontend should not trigger UI gate');
|
|
});
|
|
|
|
test('matches token separated by hyphen (word boundary)', () => {
|
|
// "micro-frontend" — "frontend" is at a word boundary after "-"
|
|
const result = checkUiPresence('micro-frontend design');
|
|
assert.ok(result.hasUI);
|
|
assert.ok(result.tokens.includes('frontend'));
|
|
});
|
|
|
|
test('normalises CRLF line endings', () => {
|
|
const result = checkUiPresence('Phase 1\r\nBuild a form\r\nDone');
|
|
assert.ok(result.hasUI);
|
|
assert.ok(result.tokens.includes('form'));
|
|
});
|
|
|
|
test('deduplicates repeated tokens', () => {
|
|
const result = checkUiPresence('UI component and another UI widget');
|
|
// "ui" should only appear once in tokens
|
|
const uiCount = result.tokens.filter((t) => t === 'ui').length;
|
|
assert.strictEqual(uiCount, 1);
|
|
});
|
|
|
|
test('detects multiple distinct tokens', () => {
|
|
const result = checkUiPresence('Build a dashboard with a form');
|
|
assert.ok(result.hasUI);
|
|
assert.ok(result.tokens.includes('dashboard'));
|
|
assert.ok(result.tokens.includes('form'));
|
|
});
|
|
|
|
// ── #2150: the `**UI hint**: yes|no` metadata line must not false-positive ──
|
|
|
|
test('#2150 `**UI hint**: no` is authoritative non-frontend (no false positive)', () => {
|
|
const result = checkUiPresence('**UI hint**: no\n\nBackend-only spike for RBAC/Entra.\n');
|
|
assert.strictEqual(result.hasUI, false,
|
|
'a phase that explicitly declares UI hint: no must not be flagged as UI');
|
|
assert.deepStrictEqual(result.tokens, []);
|
|
});
|
|
|
|
test('#2150 `**UI hint**: yes` is authoritative frontend', () => {
|
|
const result = checkUiPresence('**UI hint**: yes\n\nRefactor the login screen layout.\n');
|
|
assert.strictEqual(result.hasUI, true,
|
|
'a phase that explicitly declares UI hint: yes must be flagged as UI');
|
|
});
|
|
|
|
test('#2150 `**UI hint**: yes` over a pure-backend body still flags UI', () => {
|
|
const result = checkUiPresence('**UI hint**: yes\n\nBackend API refactor.\n');
|
|
assert.strictEqual(result.hasUI, true, 'hint:yes is authoritative even with no UI tokens');
|
|
assert.deepStrictEqual(result.tokens, []);
|
|
});
|
|
|
|
test('#2150 hint value is whole-word matched (nope/not do not mean no)', () => {
|
|
const result = checkUiPresence('**UI hint**: nope\n\nBuild a dashboard component.\n');
|
|
assert.strictEqual(result.hasUI, true,
|
|
'a malformed hint value like "nope" must not be read as "no"; fall through to token-sniffing');
|
|
});
|
|
|
|
test('#2150 a hint line without yes/no is stripped (bare UI token does not fire)', () => {
|
|
// A malformed hint (`UI hint: maybe`) must not false-positive on the bare
|
|
// `UI` token in the line itself; other UI tokens elsewhere still detect.
|
|
const result = checkUiPresence('**UI hint**: maybe\n\nBackend REST API only.\n');
|
|
assert.strictEqual(result.hasUI, false,
|
|
'the bare UI token in a UI hint line must not count as a UI indicator');
|
|
});
|
|
|
|
test('#2150 hint: no overrides even genuine UI language elsewhere', () => {
|
|
// The explicit declaration is authoritative — the author owns it.
|
|
const result = checkUiPresence('**UI hint**: no\n\nBuild a dashboard component.\n');
|
|
assert.strictEqual(result.hasUI, false,
|
|
'an explicit UI hint: no overrides token-sniffing');
|
|
});
|
|
});
|