mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
1090ed59b7
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com>
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
// Global stylesheet (Tailwind + design tokens) is imported by the Next.js
|
|
// layouts in the real app. Tests render the page in isolation, bypassing the
|
|
// layout, so without this import Tailwind classes resolve to nothing — the
|
|
// page collapses to unstyled HTML and stacked elements end up overlapping
|
|
// the graph nodes, blocking Playwright clicks. Pull the stylesheet directly
|
|
// so the test bundle gets the same CSS the production page receives.
|
|
import "@/styles/globals.css";
|
|
|
|
import { afterAll, afterEach, beforeAll, vi } from "vitest";
|
|
|
|
import { worker } from "./__tests__/msw/worker";
|
|
|
|
// Server Actions ("use server") are bundled by Vite as plain async functions
|
|
// — the directive is a Next.js compiler concept, not part of Vite. When the
|
|
// page invokes one, it runs in the browser and reaches `auth()` from
|
|
// next-auth, which calls `next/headers` (request-scoped AsyncLocalStorage
|
|
// only set up by Next's request handler) and throws "headers was called
|
|
// outside a request scope". That kills every action before it can hit
|
|
// MSW. Stub `auth.config` with a fake session so the action proceeds to
|
|
// `fetch()` and MSW takes over.
|
|
vi.mock("@/auth.config", () => ({
|
|
auth: vi.fn(() => Promise.resolve({ accessToken: "test-access-token" })),
|
|
signIn: vi.fn(),
|
|
signOut: vi.fn(),
|
|
handlers: {},
|
|
}));
|
|
|
|
// Next.js's App Router context (`useRouter`, `useSearchParams`, `usePathname`)
|
|
// is not available in vitest browser — there's no Next runtime mounting the
|
|
// providers. We back the hooks with the real `window.location` so navigating
|
|
// via `history.replaceState` in tests is enough to drive the page.
|
|
vi.mock("next/navigation", () => {
|
|
const router = {
|
|
push: vi.fn(),
|
|
replace: vi.fn(),
|
|
back: vi.fn(),
|
|
forward: vi.fn(),
|
|
refresh: vi.fn(),
|
|
prefetch: vi.fn(() => Promise.resolve()),
|
|
};
|
|
return {
|
|
useSearchParams: () => new URLSearchParams(window.location.search),
|
|
useRouter: () => router,
|
|
usePathname: () => window.location.pathname,
|
|
useParams: () => ({}),
|
|
redirect: vi.fn(),
|
|
notFound: vi.fn(),
|
|
};
|
|
});
|
|
|
|
beforeAll(async () => {
|
|
await worker.start({
|
|
serviceWorker: { url: "/mockServiceWorker.js" },
|
|
onUnhandledRequest: "error",
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
worker.resetHandlers();
|
|
});
|
|
|
|
afterAll(() => {
|
|
worker.stop();
|
|
});
|
|
|
|
// React Flow's pan/drag handlers dispatch pointer events that access
|
|
// `event.view.document` on the node. When user-event synthesises these
|
|
// events the `view` property can be null, producing harmless
|
|
// "Cannot read properties of null (reading 'document')" errors.
|
|
// Swallow only that specific unhandled error; everything else propagates.
|
|
const isReactFlowNullViewError = (reason: unknown): boolean => {
|
|
const message =
|
|
reason instanceof Error
|
|
? reason.message
|
|
: typeof reason === "string"
|
|
? reason
|
|
: "";
|
|
return message.includes(
|
|
"Cannot read properties of null (reading 'document')",
|
|
);
|
|
};
|
|
|
|
window.addEventListener("error", (event) => {
|
|
if (isReactFlowNullViewError(event.error)) {
|
|
event.preventDefault();
|
|
event.stopImmediatePropagation();
|
|
}
|
|
});
|
|
|
|
window.addEventListener("unhandledrejection", (event) => {
|
|
if (isReactFlowNullViewError(event.reason)) {
|
|
event.preventDefault();
|
|
}
|
|
});
|