Update class mechanics, stat rulesets, and CoA reference catalogs
This commit is contained in:
1 parent
b16821e92e
commit
39458d5de6
221 files changed
+2584962
-568
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { resolveArchiveDescriptions } from "./flight-text.mjs";
|
||||
|
||||
function textRow(id, text) {
|
||||
return `${id}:T${Buffer.byteLength(text, "utf8").toString(16)},${text}`;
|
||||
}
|
||||
|
||||
function flightPush(value) {
|
||||
return `<script>self.__next_f.push(${JSON.stringify(value)});</script>`;
|
||||
}
|
||||
|
||||
function builderHtml(...chunks) {
|
||||
return [flightPush([0]), ...chunks.map((chunk) => flightPush([1, chunk]))].join("");
|
||||
}
|
||||
|
||||
test("resolves exact Flight text references across split chunks using UTF-8 byte lengths", () => {
|
||||
const firstText = "Mycelial ring: café 🌱\nsecond line";
|
||||
const secondText = "Spirit stable — restored";
|
||||
const stream = `${textRow("17f", firstText)}${textRow("180", secondText)}`;
|
||||
const splitA = stream.slice(0, 8);
|
||||
const splitB = stream.slice(8, 36);
|
||||
const splitC = stream.slice(36);
|
||||
const classes = [{
|
||||
classId: 12,
|
||||
untouched: { note: "unchanged" },
|
||||
nodes: [
|
||||
{
|
||||
id: 7221,
|
||||
description: "$17F",
|
||||
rankDescriptions: [{ rank: 1, spellId: 1, description: "$180" }],
|
||||
spellId: 1,
|
||||
},
|
||||
{
|
||||
id: 7222,
|
||||
description: "$$180",
|
||||
rankDescriptions: [{ rank: 1, spellId: 2, description: "Literal $d placeholder" }],
|
||||
},
|
||||
],
|
||||
}];
|
||||
|
||||
const resolved = resolveArchiveDescriptions(classes, builderHtml(splitA, splitB, splitC));
|
||||
assert.notEqual(resolved, classes);
|
||||
assert.notEqual(resolved[0], classes[0]);
|
||||
assert.equal(resolved[0].nodes[0].description, firstText);
|
||||
assert.equal(resolved[0].nodes[0].rankDescriptions[0].description, secondText);
|
||||
assert.equal(resolved[0].nodes[0].spellId, 1);
|
||||
assert.deepEqual(resolved[0].untouched, classes[0].untouched);
|
||||
assert.equal(resolved[0].nodes[1].description, "$$180");
|
||||
assert.equal(resolved[0].nodes[1].rankDescriptions[0].description, "Literal $d placeholder");
|
||||
assert.equal(classes[0].nodes[0].description, "$17F");
|
||||
assert.equal(classes[0].nodes[0].rankDescriptions[0].description, "$180");
|
||||
});
|
||||
|
||||
test("resolves node descriptions inside nested tab collections and preserves escaped dollar text", () => {
|
||||
const stream = textRow("abc", "\u003cstrong\u003eArchive text\u003c/strong\u003e");
|
||||
const classes = [{
|
||||
classId: 13,
|
||||
tabs: [{ tabId: 1, nodes: [{ id: 1, description: "$abc", rankDescriptions: [] }] }],
|
||||
}];
|
||||
const resolved = resolveArchiveDescriptions(classes, builderHtml(stream));
|
||||
assert.equal(resolved[0].tabs[0].nodes[0].description, "<strong>Archive text</strong>");
|
||||
});
|
||||
|
||||
test("rejects missing exact references with their source field", () => {
|
||||
const classes = [{ classId: 12, nodes: [{ id: 7221, description: "$dead", rankDescriptions: [] }] }];
|
||||
assert.throws(
|
||||
() => resolveArchiveDescriptions(classes, builderHtml(textRow("1", "one"))),
|
||||
/description reference \$dead at classes\[0\]\.nodes\[0\]\.description has no Flight text row/,
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects truncated UTF-8 text records and malformed Flight push JSON", () => {
|
||||
const classes = [{ classId: 12, nodes: [] }];
|
||||
assert.throws(() => resolveArchiveDescriptions(classes, builderHtml("1:T20,short")), /text row 1 declares 20 bytes but is truncated/);
|
||||
assert.throws(() => resolveArchiveDescriptions(classes, '<script>self.__next_f.push([1,"unterminated]);</script>'), /push argument array is unterminated/);
|
||||
});
|
||||
Reference in new issue
Block a user