Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion scripts/check/lib/readmeMigrationCount.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import fs from "node:fs";
import path from "node:path";
import { parseFragment } from "parse5";

function normalizeDigits(value) {
return value.replace(/[٠-٩۰-۹]/g, (digit) => String(digit.charCodeAt(0) % 16));
}

function readTextContent(node) {
if (node.nodeName === "#text") return node.value;
return (node.childNodes ?? []).map(readTextContent).join("");
}

function validateReadmeMigrationCount(content, expected) {
const rows = [...content.matchAll(/<tr\b[^>]*>[\s\S]*?<\/tr>/g)].filter(([row]) =>
row.includes("better-sqlite3")
Expand All @@ -16,7 +22,7 @@ function validateReadmeMigrationCount(content, expected) {
// The prose after it has exactly two counts: domain modules, then migrations.
// Wording and numeral placement within each phrase vary across languages.
const tail = rows[0][0].match(/—([\s\S]*?)<\/td>/)?.[1] ?? "";
const counts = normalizeDigits(tail.replace(/<[^>]*>/g, "")).match(/\d+/g) ?? [];
const counts = normalizeDigits(readTextContent(parseFragment(tail))).match(/\d+/g) ?? [];
if (counts.length !== 2) {
return { ok: false, detail: "expected module and migration counts in database row" };
}
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/readme-migration-count-14574.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,25 @@ test("stale Persian digits fail without reinterpreting the module count", () =>
assert.equal(validate(row(`${persian(actual)} ماژول، ${persian(actual - 1)} مهاجرت`)).ok, false);
});

test("quoted HTML attributes do not become visible migration counts", () => {
const content = row(
`<span title="comparison > 999">137</span> modules, <b>${actual}</b> migrations`
);
assert.equal(validate(content).ok, true);
});

test("HTML character references are decoded before checking the visible count", () => {
const encoded = String(actual).replace(/\d/g, (digit) => `&#${digit.charCodeAt(0)};`);
assert.equal(validate(row(`&#x31;&#x33;&#x37; modules, ${encoded} migrations`)).ok, true);
});

test("a current count hidden in an attribute or comment cannot mask stale visible text", () => {
const content = row(
`<span title="${actual}">137 modules, ${actual - 1} migrations</span><!-- ${actual} -->`
);
assert.equal(validate(content).ok, false);
});

test("missing, duplicated or ambiguous database claims fail closed", () => {
for (const content of [
`There are ${actual} migrations elsewhere.`,
Expand Down
Loading