mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
Run *.browser.test.tsx in headless Chromium through the Playwright provider. The shared helper in tests/browser/ mounts the app's own providers, stubs only the fetch boundary, and shims next/navigation onto the page's real location and history.
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
/**
|
|
* The page's address, for URL-state tests in the browser project.
|
|
*
|
|
* Each test file runs in its own page, and `setup.ts` puts the address the
|
|
* page started on back after every test, so a test may write the URL freely.
|
|
* Only the query and hash are ever changed: the path is the test runner's
|
|
* own, and a component that writes state keeps the path it found.
|
|
*/
|
|
import { resetUrlStateForTests } from "@/lib/url-state";
|
|
|
|
export interface HistoryRecord {
|
|
/** Every `pushState` and `replaceState` since `recordHistory()`, in order. */
|
|
writes: Array<{ mode: "push" | "replace"; search: string }>;
|
|
pushes: () => string[];
|
|
replaces: () => string[];
|
|
}
|
|
|
|
let restoreHistory: (() => void) | null = null;
|
|
|
|
/**
|
|
* Record history writes without changing them: the real methods still run,
|
|
* so the page's location moves exactly as it would in the app.
|
|
*/
|
|
export function recordHistory(): HistoryRecord {
|
|
restoreHistory?.();
|
|
const { history } = window;
|
|
const push = history.pushState.bind(history);
|
|
const replace = history.replaceState.bind(history);
|
|
const writes: HistoryRecord["writes"] = [];
|
|
const searchOf = (url: string | URL | null | undefined) =>
|
|
url == null
|
|
? window.location.search
|
|
: new URL(url, window.location.href).search;
|
|
history.pushState = (data, unused, url) => {
|
|
push(data, unused, url);
|
|
writes.push({ mode: "push", search: searchOf(url) });
|
|
};
|
|
history.replaceState = (data, unused, url) => {
|
|
replace(data, unused, url);
|
|
writes.push({ mode: "replace", search: searchOf(url) });
|
|
};
|
|
restoreHistory = () => {
|
|
history.pushState = push;
|
|
history.replaceState = replace;
|
|
restoreHistory = null;
|
|
};
|
|
return {
|
|
writes,
|
|
pushes: () => writes.filter((w) => w.mode === "push").map((w) => w.search),
|
|
replaces: () =>
|
|
writes.filter((w) => w.mode === "replace").map((w) => w.search),
|
|
};
|
|
}
|
|
|
|
/** Start the test on `search` (`""` for a bare path). */
|
|
export function setSearch(search: string): void {
|
|
const query = search === "" || search.startsWith("?") ? search : `?${search}`;
|
|
window.history.replaceState(null, "", `${window.location.pathname}${query}`);
|
|
}
|
|
|
|
/**
|
|
* Change the query the way the person would from outside the component:
|
|
* Back, Forward or an edited address. The router hears `popstate`.
|
|
*/
|
|
export function navigateExternally(search: string): void {
|
|
setSearch(search);
|
|
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
}
|
|
|
|
/** The current query string, `?`-prefixed or empty. */
|
|
export function currentSearch(): string {
|
|
return window.location.search;
|
|
}
|
|
|
|
let initialHref: string | null = null;
|
|
|
|
/** Called by `setup.ts` before each test. */
|
|
export function rememberAddress(): void {
|
|
initialHref = window.location.href;
|
|
}
|
|
|
|
/** Called by `setup.ts` after each test. */
|
|
export function restoreAddress(): void {
|
|
restoreHistory?.();
|
|
resetUrlStateForTests();
|
|
if (initialHref !== null && window.location.href !== initialHref) {
|
|
window.history.replaceState(null, "", initialHref);
|
|
}
|
|
}
|