-
Notifications
You must be signed in to change notification settings - Fork 247
Expand file tree
/
Copy pathagent-mocks.ts
More file actions
99 lines (84 loc) · 2.81 KB
/
Copy pathagent-mocks.ts
File metadata and controls
99 lines (84 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Dev-only HTTP mocking surface for agent-driven testing.
//
// Activated when the dev build sees `?mock=1` on the URL. Once active, exposes:
// window.__rivetMock(pattern, { status, body, method?, delayMs? })
// window.__rivetClearMocks()
//
// `delayMs` holds the response open before replying, which keeps the triggering
// query in its pending state long enough to inspect loading skeletons.
//
// `pattern` is an MSW path matcher (e.g. "*/actors/:id/kv/keys/*"). Mocks are
// persisted to sessionStorage so they survive page reloads, which is the
// common agent-test workflow (set mock, reload to retrigger queries).
import type { HttpHandler } from "msw";
type MockMethod = "get" | "post" | "put" | "delete" | "patch";
type MockSpec = {
status: number;
body?: unknown;
method?: MockMethod;
// Delay the response by this many milliseconds before replying. Use this to
// hold a query in its pending state long enough to inspect loading skeletons.
delayMs?: number;
};
type StoredMock = MockSpec & { pattern: string };
const STORAGE_KEY = "__rivetAgentMocks";
declare global {
interface Window {
__rivetMock?: (pattern: string, spec: MockSpec) => void;
__rivetClearMocks?: () => void;
}
}
function readStoredMocks(): StoredMock[] {
try {
const raw = sessionStorage.getItem(STORAGE_KEY);
if (!raw) return [];
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
}
function writeStoredMocks(mocks: StoredMock[]) {
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(mocks));
}
export async function maybeStartAgentMocks() {
if (!import.meta.env.DEV) return;
const params = new URLSearchParams(window.location.search);
if (params.get("mock") !== "1") return;
const { setupWorker } = await import("msw/browser");
const { http, HttpResponse, delay } = await import("msw");
const buildHandler = (m: StoredMock): HttpHandler => {
const method = m.method ?? "get";
return http[method](m.pattern, async () => {
if (m.delayMs) await delay(m.delayMs);
return HttpResponse.json(m.body ?? null, { status: m.status });
});
};
const initial = readStoredMocks();
const worker = setupWorker(...initial.map(buildHandler));
await worker.start({
onUnhandledRequest: "bypass",
quiet: true,
});
window.__rivetMock = (pattern, spec) => {
const stored: StoredMock = { pattern, ...spec };
const next = [
...readStoredMocks().filter(
(m) =>
m.pattern !== pattern ||
(m.method ?? "get") !== (spec.method ?? "get"),
),
stored,
];
writeStoredMocks(next);
worker.use(buildHandler(stored));
};
window.__rivetClearMocks = () => {
writeStoredMocks([]);
worker.resetHandlers();
};
// eslint-disable-next-line no-console
console.info(
`[agent-mocks] active (${initial.length} restored). Use window.__rivetMock(pattern, { status, body }).`,
);
}