Testing
Adapter
A deterministic memory adapter with its own back and forward stack.
- Package
- @queryweave/testing
- Runtime
- Any ECMAScript runtime
- Depends on
- @queryweave/core
- Framework required
- No
The memory adapter holds the query string in memory, with a history stack you move through with
back and forward. canGoBack and canGoForward tell you whether each move is possible. It notifies on every change,
and an optional guard can refuse or redirect a write the way a router guard would.
Like every adapter, it leaves decoding, validation, and defaults to the model, and it adds nothing of its own. That makes it the reference implementation of the adapter contract: if a behavior shows up with the memory adapter, it came from the engine.
Install
Section titled “Install”pnpm add -D @queryweave/testingExample
Section titled “Example”import { createQueryRuntime } from "@queryweave/core";import { createMemoryQueryAdapter } from "@queryweave/testing";import { expect, test } from "vitest";
test("resetting the page keeps the search term", async () => { const adapter = createMemoryQueryAdapter({ initial: "?search=vue&page=3" }); const runtime = createQueryRuntime({ model: products, adapter });
await runtime.reset(["page"]);
expect(adapter.current()).toBe("search=vue"); expect(runtime.read().values.page).toBe(1);});adapter.current() returns the stored query as a string, which makes assertions read like the URL
a user would see.
The surface
Section titled “The surface”const adapter = createMemoryQueryAdapter({ initial: "?page=2" });
adapter.read(); // QueryOutput — what the runtime consumesadapter.entries(); // the same entries, as a convenienceadapter.current(); // "page=2" — formatted, no leading "?"
adapter.push(entries); // adds a history entryadapter.replace(entries); // rewrites the current one
adapter.back();adapter.forward();adapter.canGoBack();adapter.canGoForward();
adapter.dispose();Testing history semantics
Section titled “Testing history semantics”The stack behaves like a browser’s, which is what makes push and replace testable without a browser:
test("replace does not grow the history", async () => { const adapter = createMemoryQueryAdapter(); const runtime = createQueryRuntime({ model: products, adapter });
await runtime.update({ page: 2 }); // push await runtime.update({ page: 3 }, { navigation: "replace" });
expect(adapter.canGoBack()).toBe(true); adapter.back(); expect(adapter.canGoBack()).toBe(false); // only one entry was added expect(runtime.read().values.page).toBe(1);});Pushing after going back truncates the forward entries, exactly as a browser does.
Testing notifications
Section titled “Testing notifications”test("one transition notifies once", async () => { const adapter = createMemoryQueryAdapter(); const runtime = createQueryRuntime({ model: products, adapter }); const seen: number[] = [];
runtime.subscribe((snapshot) => { seen.push(snapshot.values.page); });
await runtime.transaction((draft) => { draft.search = "vue"; draft.page = 2; });
expect(seen).toStrictEqual([2]);});Testing a refusing environment
Section titled “Testing a refusing environment”test("a refused page change leaves the state alone", async () => { const adapter = createMemoryQueryAdapter({ initial: "?page=2", guard: (next) => next.some(([k, v]) => k === "page" && v === "9") ? { outcome: "refused" } : undefined, }); const runtime = createQueryRuntime({ model: products, adapter });
const result = await runtime.update({ page: 9 }); expect(result.outcome).toBe("refused"); expect(runtime.read().values.page).toBe(2);});The memory adapter navigates synchronously, which real adapters do not. The runtime serializes transitions either way, so ordering assertions hold; timing assertions belong in the browser and router suites.
Edge cases
Section titled “Edge cases”backat the start andforwardat the end do nothing. They do not throw and do not notify.- After
dispose, every mutating call throws.readandentrieskeep working. - Entries are copied on write, so a caller cannot mutate the stack afterwards.
- The initial value accepts any
QueryInput— a string, entries, or an object.