feat(ui): add app shell, router, and base path handling

Base path comes from BUN_PUBLIC_BASE_PATH so the same bundle serves both
a root Docker deploy and a GitHub Pages subpath.

Deviation from the task-16 brief: does not install @happy-dom/global-registrator,
does not create test/setup.ts, and does not add a [test] preload to bunfig.toml.
A global DOM preload would make src/domain/smoke.test.ts's "importable without a
DOM" assertion vacuously true, silently retiring the check that guards the
domain-layer-stays-pure invariant. Nothing in this task needs a DOM: basePath.test.ts
is pure string logic, and the shell render test was intentionally omitted by the
brief itself. A later task will register happy-dom per-file where a DOM is
genuinely required.
This commit is contained in:
Lucas Winther
2026-09-01 20:17:57 +02:00
parent 69bf4393fb
commit 7edcc7f31e
14 changed files with 168 additions and 14 deletions
+23
View File
@@ -0,0 +1,23 @@
import { test, expect } from "bun:test";
import { withBase, normalizeBase } from "./basePath.ts";
test("normalizeBase defaults to root", () => {
expect(normalizeBase(undefined)).toBe("/");
expect(normalizeBase("")).toBe("/");
});
test("normalizeBase adds the leading and trailing slash", () => {
expect(normalizeBase("budget2.0")).toBe("/budget2.0/");
expect(normalizeBase("/budget2.0")).toBe("/budget2.0/");
expect(normalizeBase("/budget2.0/")).toBe("/budget2.0/");
});
test("withBase joins without doubling slashes", () => {
expect(withBase("/manifest.webmanifest", "/budget2.0/")).toBe(
"/budget2.0/manifest.webmanifest",
);
expect(withBase("manifest.webmanifest", "/budget2.0/")).toBe(
"/budget2.0/manifest.webmanifest",
);
expect(withBase("/sw.js", "/")).toBe("/sw.js");
});