nte-optimizer/tests/router.test.ts
goober 141bb2c751 feat: NTE gear optimizer web app
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>
2026-08-21 11:38:17 +03:00

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");
});
});