78 lines
3.3 KiB
JavaScript
78 lines
3.3 KiB
JavaScript
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/);
|
|
});
|