62d6bd211b
CI / check (push) Has been cancelled
Print generic datasheets - every option a unit could take, the way the official cards read - instead of a record of one particular list. The three Python scripts that used to rewrite the .ros file before upload are now transforms in src/transforms/, running in the browser between DOMParser and the roster parser. Most of each script was machinery for preserving the file byte for byte on the way back to disk; in the browser the document is never serialised, so only the domain logic came across. Because they run on the parsed document rather than the uploaded file, flipping a transform off rebuilds from the original XML with no re-upload. Saved rosters therefore hold the raw roster XML rather than the parsed object, under new localStorage keys. The scripts' output over four 11th-edition rosters is checked in as test fixtures, so the port is measured against an independent implementation that was verified by printing the cards; scripts/update-fixtures.mjs refuses to overwrite those without --force. Two parser call sites now use :scope> rather than repeating the scope element's own name, which means the same thing in a browser and also works under jsdom, where the old form matched nothing. Also: served at the root rather than a GitHub Pages subpath, PostHog analytics removed, and deploy/ carries an nginx site for scribe.luxick.de plus an rsync deploy script. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
89 lines
2.9 KiB
JavaScript
89 lines
2.9 KiB
JavaScript
#!/usr/bin/env node
|
|
//
|
|
// Write the expected-output fixtures for a roster added to
|
|
// src/transforms/__fixtures__/.
|
|
//
|
|
// node scripts/update-fixtures.mjs # only writes what is missing
|
|
// node scripts/update-fixtures.mjs --force # re-baselines everything
|
|
//
|
|
// Drop a new `<name>.ros` in that directory and run this to generate the four
|
|
// `<name>.{merge,strip,convert,all}.ros` files the test suite compares against.
|
|
// Read the diff before committing: this records whatever the transforms do
|
|
// today, so it cannot tell a fix from a regression.
|
|
//
|
|
// Existing fixtures are left alone unless --force is given, and that guard is
|
|
// the point rather than a convenience. The fixtures that came with this repo
|
|
// were produced by a separate Python implementation and verified by printing
|
|
// the cards; the test suite is worth something because it checks the transforms
|
|
// against that independent output. Re-baselining a fixture from the JS replaces
|
|
// it with the very thing under test, and the comparison becomes a tautology
|
|
// that passes no matter what the code does.
|
|
|
|
import { readFileSync, readdirSync, existsSync, writeFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { JSDOM } from "jsdom";
|
|
|
|
import {
|
|
convertChoiceAbilities,
|
|
defaultConfig,
|
|
mergeDuplicateUnits,
|
|
removeLeaderAbilities,
|
|
} from "../src/transforms/index.js";
|
|
|
|
const FIXTURES = join(
|
|
dirname(fileURLToPath(import.meta.url)),
|
|
"..",
|
|
"src",
|
|
"transforms",
|
|
"__fixtures__",
|
|
);
|
|
|
|
const STEPS = {
|
|
merge: (doc) => mergeDuplicateUnits(doc, defaultConfig),
|
|
strip: (doc) => removeLeaderAbilities(doc, defaultConfig),
|
|
convert: (doc) => convertChoiceAbilities(doc, defaultConfig),
|
|
};
|
|
|
|
// Chained, in the order the app runs them; see src/transforms/index.js.
|
|
const PIPELINE = ["merge", "strip", "convert"];
|
|
|
|
const force = process.argv.includes("--force");
|
|
const { window } = new JSDOM();
|
|
|
|
const inputs = readdirSync(FIXTURES)
|
|
.filter((name) => name.endsWith(".ros") && name.split(".").length === 2)
|
|
.map((name) => name.replace(/\.ros$/, ""));
|
|
|
|
let written = 0;
|
|
let kept = 0;
|
|
|
|
for (const input of inputs) {
|
|
const xml = readFileSync(join(FIXTURES, `${input}.ros`), "utf8");
|
|
|
|
const outputs = { ...STEPS, all: null };
|
|
for (const label of Object.keys(outputs)) {
|
|
const path = join(FIXTURES, `${input}.${label}.ros`);
|
|
if (existsSync(path) && !force) {
|
|
kept++;
|
|
continue;
|
|
}
|
|
|
|
const doc = new window.DOMParser().parseFromString(xml, "text/xml");
|
|
if (label === "all") {
|
|
for (const step of PIPELINE) STEPS[step](doc);
|
|
} else {
|
|
STEPS[label](doc);
|
|
}
|
|
|
|
writeFileSync(path, new window.XMLSerializer().serializeToString(doc), "utf8");
|
|
console.log(`wrote ${input}.${label}.ros`);
|
|
written++;
|
|
}
|
|
}
|
|
|
|
console.log(`\n${written} written, ${kept} left alone.`);
|
|
if (kept > 0 && !force) {
|
|
console.log("Pass --force to re-baseline the rest, but read the header first.");
|
|
}
|