- [warn 1] Document the is_agent operator setup so it survives plan deletion: added an AI-agent block to .env.example (use a DEDICATED account, set is_agent via SQL, never flag a human/shared account) + a CHANGELOG "Added" entry. - [warn 2] Test the badge deep-link side effects: ai-agent-badge.test.tsx now renders inside an explicit jotai store, clicks the badge, and asserts the active chat id, window-open, cleared draft, closed history modal, AND that stopPropagation keeps a parent onClick from firing. - [suggestion 3] Hoist the window.matchMedia stub into vitest.setup.ts and drop the duplicated beforeAll block from the three test files (ai-agent-badge, comment-list-item, role-cards). - [suggestion 4] Merge the two near-duplicate "non-clickable" cases via it.each. - [follow-up 6] Introduce a single ProvenanceSource = 'user' | 'agent' type in jwt-payload.ts and reference it from AuthProvenanceData, JwtPayload/ JwtCollabPayload, and resolveSource() — so a typo can't slip through as a bare string. (Server auth chain; client IComment mirroring left as a follow-up.) Follow-up 5 (shared agentSourceFields write-stamp helper) is deferred as the review marked it — the 6 REST sites use varied shapes (create-spread vs resolve-conditional-null vs page move), so it's a separate focused refactor. Tests: client badge/comment/role-cards suites 11/11 pass; server auth+comment suites 62 pass; typecheck clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
// Vitest global setup (test-infra only — no production app source).
|
|
//
|
|
// Under Node 25 / jsdom 25 / vitest 4 the jsdom `localStorage` exposed on the
|
|
// global is not a usable Storage: its methods (`setItem`/`getItem`/...) are not
|
|
// callable, so any code touching `localStorage` throws `... is not a function`.
|
|
// Production code such as `isHtmlEmbedFeatureEnabled()` reads
|
|
// `localStorage.getItem("currentUser")`, which made dependent tests fail.
|
|
//
|
|
// We install a correct in-memory Storage stub on the global BEFORE tests run so
|
|
// the Web Storage contract holds: string coercion of keys/values, `null` for
|
|
// missing keys, working `length`/`key(index)`, and `clear()`.
|
|
import { vi } from "vitest";
|
|
|
|
// Minimal, spec-faithful in-memory implementation of the Web Storage API.
|
|
function createStorage(): Storage {
|
|
let store = new Map<string, string>();
|
|
|
|
const storage: Storage = {
|
|
get length(): number {
|
|
return store.size;
|
|
},
|
|
clear(): void {
|
|
store = new Map<string, string>();
|
|
},
|
|
getItem(key: string): string | null {
|
|
// Missing keys must return `null`, not `undefined`.
|
|
const value = store.get(String(key));
|
|
return value === undefined ? null : value;
|
|
},
|
|
setItem(key: string, value: string): void {
|
|
// Web Storage coerces both key and value to strings.
|
|
store.set(String(key), String(value));
|
|
},
|
|
removeItem(key: string): void {
|
|
store.delete(String(key));
|
|
},
|
|
key(index: number): string | null {
|
|
// Insertion order matches Map iteration order; out-of-range => null.
|
|
const keys = Array.from(store.keys());
|
|
return index >= 0 && index < keys.length ? keys[index] : null;
|
|
},
|
|
};
|
|
|
|
return storage;
|
|
}
|
|
|
|
// Install on the jsdom global. `vi.stubGlobal` also reflects onto `window`
|
|
// (jsdom shares `globalThis` and `window`), so both `localStorage` and
|
|
// `window.localStorage` resolve to the same working stub.
|
|
vi.stubGlobal("localStorage", createStorage());
|
|
vi.stubGlobal("sessionStorage", createStorage());
|
|
|
|
// MantineProvider (and other components) read `window.matchMedia` on mount, which
|
|
// jsdom does not implement. Provide a minimal stub here so any test rendering
|
|
// Mantine works without re-stubbing matchMedia in every file.
|
|
vi.stubGlobal("matchMedia", (query: string) => ({
|
|
matches: false,
|
|
media: query,
|
|
onchange: null,
|
|
addListener: vi.fn(),
|
|
removeListener: vi.fn(),
|
|
addEventListener: vi.fn(),
|
|
removeEventListener: vi.fn(),
|
|
dispatchEvent: vi.fn(),
|
|
}));
|