ktg-plugin-marketplace/plugins/config-audit/tests/lib/forbidden-words-data.test.mjs
Kjell Tore Guttormsen 8c07fe3493 feat(humanizer): forbidden-words data file (tier1/2/3)
Wave 1 / Step 1 of v5.1.0 plain-language UX humanizer.

tests/lint-forbidden-words.json defines the SC-3 forbidden-words
vocabulary used by the lint runner (Wave 4 / Step 8) and the
humanizer-data translation guard (Wave 1 / Step 2).

- Tier 1: 19 absolute prohibitions (failure if matched in default
  output) — sourced from Microsoft Writing Style Guide, Federal
  Plain Language, GOV.UK, Google Developer Style, Apple HIG.
- Tier 2: 24 strong-avoidance terms (warning if matched) — same
  sources plus Mailchimp.
- Tier 3: 12 domain-specific jargon terms (failure if matched in
  default output, allowed in --raw and --json paths) — sourced
  from research/03 jargon table.

Counts diverge from plan.md (18/21/11) — JSON tracks the brief's
verbatim lists at research/03 lines 200-202 plus tier3 hook entry
from the brief's table. Plan revision noted in audit-doc.

Test: 10 cases verifying parse, count, schema completeness, spot
checks per tier, no cross-tier duplicates. All pass.
Regression: 645/645 tests (635 + 10 new).

Project: .claude/projects/2026-05-01-config-audit-ux-redesign/
2026-05-01 16:53:37 +02:00

97 lines
3.8 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const DATA_PATH = resolve(__dirname, '..', 'lint-forbidden-words.json');
async function loadData() {
const raw = await readFile(DATA_PATH, 'utf8');
return JSON.parse(raw);
}
test('forbidden-words JSON parses successfully', async () => {
const data = await loadData();
assert.equal(typeof data, 'object');
assert.ok(data !== null);
});
test('top-level keys present (tier1, tier2, tier3)', async () => {
const data = await loadData();
assert.ok(Array.isArray(data.tier1), 'tier1 must be an array');
assert.ok(Array.isArray(data.tier2), 'tier2 must be an array');
assert.ok(Array.isArray(data.tier3), 'tier3 must be an array');
});
test('tier1 has 19 entries (verbatim from research/03 SC-3 list line 200)', async () => {
const data = await loadData();
assert.equal(data.tier1.length, 19, `expected 19 tier1 entries, got ${data.tier1.length}`);
});
test('tier2 has 24 entries (verbatim from research/03 SC-3 list line 201)', async () => {
const data = await loadData();
assert.equal(data.tier2.length, 24, `expected 24 tier2 entries, got ${data.tier2.length}`);
});
test('tier3 has 12 entries (verbatim from research/03 SC-3 list line 202 + hook)', async () => {
const data = await loadData();
assert.equal(data.tier3.length, 12, `expected 12 tier3 entries, got ${data.tier3.length}`);
});
test('every entry has required fields (word, replacement, source, tier)', async () => {
const data = await loadData();
for (const tierName of ['tier1', 'tier2', 'tier3']) {
for (const entry of data[tierName]) {
assert.ok(typeof entry.word === 'string' && entry.word.length > 0,
`${tierName} entry missing 'word': ${JSON.stringify(entry)}`);
assert.ok(typeof entry.replacement === 'string' && entry.replacement.length > 0,
`${tierName} entry "${entry.word}" missing 'replacement'`);
assert.ok(typeof entry.source === 'string' && entry.source.length > 0,
`${tierName} entry "${entry.word}" missing 'source'`);
assert.ok(entry.tier === Number(tierName.replace('tier', '')),
`${tierName} entry "${entry.word}" has wrong tier: ${entry.tier}`);
}
}
});
test('tier1 spot-check — required absolute prohibitions present', async () => {
const data = await loadData();
const words = data.tier1.map((e) => e.word);
for (const required of ['utilize', 'leverage', 'facilitate', 'terminate', 'abort', 'invalid', 'illegal', 'failed to', 'fatal', 'in order to']) {
assert.ok(words.includes(required), `tier1 missing required word: ${required}`);
}
});
test('tier2 spot-check — condescending words present', async () => {
const data = await loadData();
const words = data.tier2.map((e) => e.word);
for (const required of ['simply', 'just', 'obviously', 'clearly']) {
assert.ok(words.includes(required), `tier2 missing required word: ${required}`);
}
});
test('tier3 spot-check — domain-specific jargon present', async () => {
const data = await loadData();
const words = data.tier3.map((e) => e.word);
for (const required of ['CLAUDE.md', '@import', 'MCP', 'hook', 'frontmatter']) {
assert.ok(words.includes(required), `tier3 missing required word: ${required}`);
}
});
test('no duplicate words across tiers', async () => {
const data = await loadData();
const allWords = [
...data.tier1.map((e) => e.word),
...data.tier2.map((e) => e.word),
...data.tier3.map((e) => e.word),
];
const seen = new Set();
const dupes = [];
for (const w of allWords) {
if (seen.has(w)) dupes.push(w);
seen.add(w);
}
assert.equal(dupes.length, 0, `duplicate forbidden words across tiers: ${dupes.join(', ')}`);
});