Files
santifer--career-ops/tests/providers/_html-entities.test.mjs
T
wehub-resource-sync d083df1fdb
CodeQL Analysis / Analyze (javascript-typescript) (push) Failing after 2s
Web CI / web typecheck + build (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
CodeQL Analysis / Analyze (go) (push) Failing after 16s
chore: import upstream snapshot with attribution
2026-07-13 12:02:43 +08:00

46 lines
2.9 KiB
JavaScript

// tests/providers/_html-entities.test.mjs — direct coverage for the shared
// entity decoder (providers/_html-entities.mjs), extracted out of
// deutschebahn.mjs / hecklerkoch.mjs so the numeric-entity guard can't drift
// out of sync between copies again (#1555). CodeRabbit asked for this as a
// dedicated, provider-independent test for a "safety-critical" shared module.
import { pass, fail, ROOT } from '../helpers.mjs';
import { join } from 'path';
import { pathToFileURL } from 'url';
console.log('\nProvider — _html-entities (shared HTML entity decoder)');
try {
const { decodeEntities } = await import(pathToFileURL(join(ROOT, 'providers/_html-entities.mjs')).href);
if (decodeEntities('Program & Release') === 'Program & Release') pass('decodeEntities decodes named entities (&)');
else fail(`named entity wrong: ${JSON.stringify(decodeEntities('Program & Release'))}`);
if (decodeEntities('für Zürich') === 'für Zürich') pass('decodeEntities decodes decimal and hex numeric entities');
else fail(`numeric entity wrong: ${JSON.stringify(decodeEntities('für Zürich'))}`);
if (decodeEntities('Zürich') === 'Zürich') pass('decodeEntities decodes an uppercase hex marker (&#X..;)');
else fail(`uppercase hex entity wrong: ${JSON.stringify(decodeEntities('Zürich'))}`);
// A numeric entity above 0x10FFFF is the one that actually throws
// RangeError out of String.fromCodePoint; a lone surrogate half
// (0xD800-0xDFFF) does not throw by itself but is rejected defensively
// since it isn't a valid Unicode scalar value. Both must degrade to the
// literal source text rather than crash.
if (decodeEntities('Huge�Entity') === 'Huge�Entity') pass('decodeEntities degrades an out-of-range numeric entity (no RangeError crash)');
else fail(`out-of-range entity wrong: ${JSON.stringify(decodeEntities('Huge�Entity'))}`);
if (decodeEntities('Bad�Entity') === 'Bad�Entity') pass('decodeEntities degrades a lone surrogate half');
else fail(`surrogate entity wrong: ${JSON.stringify(decodeEntities('Bad�Entity'))}`);
if (decodeEntities('Negative&#-1;Entity') === 'Negative&#-1;Entity') pass('decodeEntities leaves a non-matching negative entity untouched');
else fail(`negative entity wrong: ${JSON.stringify(decodeEntities('Negative&#-1;Entity'))}`);
// Regression: the hex-charset alternative used to match decimal-looking
// bodies too ("#x?[0-9a-fA-F]+"), so parseInt(…, 10) silently stopped at
// the first hex letter and dropped the rest — "&#1a2;" decoded to "\x01"
// and swallowed "a2" instead of passing the malformed entity through.
if (decodeEntities('X&#1a2;Y') === 'X&#1a2;Y') pass('decodeEntities does not let hex letters leak into the decimal branch');
else fail(`decimal/hex leak regression: ${JSON.stringify(decodeEntities('X&#1a2;Y'))}`);
} catch (e) {
fail(`_html-entities tests crashed: ${e.message}`);
}