Client-side optimizer for Neverness to Everness console builds. Imports a gear export from nte-history-exporter, keeps it in IndexedDB, and solves for the best module placement per character or across a team. - domain/: pure ports of the Python research scripts (shapes, cartridges, board tiling, stats, scoring, set bonuses, Arc effects, sheet prediction). No DOM, no storage imports, so they stay worker-safe and Node-testable. - solver/: single-character solve with branch and bound, plus a three-phase team solve (portfolio, leximin, column generation) over disjoint item sets. Runs in a Worker when one can be constructed, inline otherwise. - db/: three storage tiers selected by use rather than feature detection, fail-closed import validation, and undo that restores both sides of a steal. - ui/: React 19 views for items, characters and teams; the board is drawn as the deliverable rather than a score. - Two build targets: a hosted bundle and a single self-contained nte.html with no external requests. Values the model cannot measure are reported as unavailable rather than guessed: cartridge set bonuses, the level 80 base-stat multiplier, and the distinction between "optimal for this packing" and globally optimal. 145 tests, tsc clean, both build targets clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
29 lines
1 KiB
TypeScript
29 lines
1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { hashFor, parseHash, TABS } from "../src/ui/router.ts";
|
|
|
|
describe("hash routing", () => {
|
|
it("round-trips every tab", () => {
|
|
for (const tab of TABS) {
|
|
expect(parseHash(hashFor(tab))).toEqual({ tab, id: null });
|
|
}
|
|
});
|
|
|
|
it("carries a character id, encoded", () => {
|
|
const hash = hashFor("characters", "Chiichan073");
|
|
expect(parseHash(hash)).toEqual({ tab: "characters", id: "Chiichan073" });
|
|
// A name with a slash must not read as a third path segment.
|
|
expect(parseHash(hashFor("characters", "a/b"))).toEqual({ tab: "characters", id: "a/b" });
|
|
});
|
|
|
|
it("falls back to characters for anything unrecognised", () => {
|
|
expect(parseHash("").tab).toBe("characters");
|
|
expect(parseHash("#/nonsense").tab).toBe("characters");
|
|
expect(parseHash("#").tab).toBe("characters");
|
|
});
|
|
|
|
it("accepts a hash with or without the leading slash", () => {
|
|
expect(parseHash("#team").tab).toBe("team");
|
|
expect(parseHash("#/team").tab).toBe("team");
|
|
});
|
|
});
|