93 lines
3.7 KiB
JavaScript
93 lines
3.7 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
|
|
const dataUrl = new URL("../data/", import.meta.url);
|
|
|
|
async function loadManifest() {
|
|
return JSON.parse(await readFile(new URL("fixtures.json", dataUrl), "utf8"));
|
|
}
|
|
|
|
test("fixture manifest covers every screenshot exactly once", async () => {
|
|
const manifest = await loadManifest();
|
|
const files = (await readdir(dataUrl)).filter((file) => file.toLowerCase().endsWith(".jpg")).sort();
|
|
const declared = manifest.cases.map((fixture) => fixture.file).sort();
|
|
|
|
assert.equal(manifest.version, 1);
|
|
assert.deepEqual(manifest.frame, { width: 1560, height: 1360 });
|
|
assert.equal(new Set(manifest.cases.map((fixture) => fixture.id)).size, manifest.cases.length);
|
|
assert.deepEqual(declared, files);
|
|
});
|
|
|
|
test("fixture groups have deterministic order and reviewed slot transitions", async () => {
|
|
const { cases } = await loadManifest();
|
|
const groups = new Map();
|
|
cases.forEach((fixture) => {
|
|
const group = groups.get(fixture.groupId) || [];
|
|
group.push(fixture);
|
|
groups.set(fixture.groupId, group);
|
|
});
|
|
|
|
for (const fixtures of groups.values()) {
|
|
const orders = fixtures.map((fixture) => fixture.order).sort((left, right) => left - right);
|
|
assert.deepEqual(orders, [...Array(fixtures.length).keys()]);
|
|
|
|
for (const fixture of fixtures) {
|
|
const expected = fixture.expected;
|
|
const occupied = expected.occupiedSlots;
|
|
const classified = [...expected.pendingSlots, ...expected.foundSlots].sort((a, b) => a - b);
|
|
assert.deepEqual(classified, occupied);
|
|
assert.equal(new Set(classified).size, classified.length);
|
|
assert.ok(classified.every((slot) => Number.isInteger(slot) && slot >= 0 && slot < 18));
|
|
if (expected.item) {
|
|
assert.ok(fixture.catalogAssetId);
|
|
assert.ok(expected.foundSlots.includes(expected.hoveredSlot));
|
|
assert.ok(expected.item.name && expected.item.displayName);
|
|
assert.ok(Number.isSafeInteger(expected.item.quantity) && expected.item.quantity >= 0);
|
|
assert.ok(Number.isSafeInteger(expected.item.priceAdena) && expected.item.priceAdena > 0);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test("contextual tooltip fixtures own their initial shop state", async () => {
|
|
const { cases } = await loadManifest();
|
|
const contextual = cases.filter((fixture) => fixture.mode === "contextual");
|
|
assert.ok(contextual.length > 0);
|
|
contextual.forEach((fixture) => {
|
|
assert.equal(fixture.initialState.side, fixture.expected.side);
|
|
assert.equal(fixture.initialState.merchant, fixture.expected.merchant);
|
|
assert.ok(fixture.initialState.activeShopKey);
|
|
});
|
|
});
|
|
|
|
test("fixture calibration and every declared catalog asset are committed", async () => {
|
|
const manifest = await loadManifest();
|
|
const calibration = JSON.parse(await readFile(new URL(manifest.calibration, dataUrl), "utf8"));
|
|
const catalog = JSON.parse(
|
|
await readFile(new URL("fixture-assets/catalog.json", dataUrl), "utf8"),
|
|
);
|
|
|
|
assert.deepEqual(calibration.frame, manifest.frame);
|
|
for (const field of [
|
|
"saleAnchor",
|
|
"storeHeaderRegion",
|
|
"tooltipSearchRegion",
|
|
"tooltipAnchor",
|
|
"itemNameRegion",
|
|
"itemPriceRegion",
|
|
"sellItemGridRegion",
|
|
"buyItemGridRegion",
|
|
]) {
|
|
assert.ok(calibration[field], `missing fixture calibration field: ${field}`);
|
|
}
|
|
await access(new URL(calibration.saleAnchor.image, dataUrl));
|
|
await access(new URL(calibration.tooltipAnchor.image, dataUrl));
|
|
|
|
for (const fixture of manifest.cases.filter((entry) => entry.catalogAssetId)) {
|
|
const item = catalog.items[fixture.catalogAssetId];
|
|
assert.ok(item, `missing catalog fixture: ${fixture.catalogAssetId}`);
|
|
await access(new URL(item.iconUrl.replace(/^\//, "../"), dataUrl));
|
|
}
|
|
});
|