test: add comprehensive automated test coverage
- Vitest multi-project setup (unit / integration / component) - Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL override), migrations applied per worker; production code untouched - Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme, marquee, media, portfolio helpers, plus architecture-rule tests - Integration: Prisma data layer, API routes, and all server actions - Component: UI primitives and form components (jsdom + Testing Library) - 371 tests passing
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
/**
|
||||
* Shared mocks for the Next.js runtime pieces that server actions depend on.
|
||||
* Action tests wire these in with `vi.mock(...)` at the top of each file, e.g.:
|
||||
*
|
||||
* vi.mock("next/navigation", async () => ({
|
||||
* redirect: (await import("@/tests/helpers/next-mocks")).redirect,
|
||||
* }));
|
||||
*/
|
||||
|
||||
export class RedirectError extends Error {
|
||||
readonly digest = "NEXT_REDIRECT";
|
||||
readonly __isRedirect = true;
|
||||
constructor(public url: string) {
|
||||
super(`NEXT_REDIRECT:${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function redirect(url: string): never {
|
||||
throw new RedirectError(url);
|
||||
}
|
||||
|
||||
export function isRedirectError(error: unknown): error is RedirectError {
|
||||
return (
|
||||
error instanceof RedirectError ||
|
||||
(typeof error === "object" && error !== null && (error as { __isRedirect?: boolean }).__isRedirect === true)
|
||||
);
|
||||
}
|
||||
|
||||
export const revalidatePath = vi.fn();
|
||||
|
||||
export const adminAuth = { authenticated: true };
|
||||
export const clearAdminSessionCookie = vi.fn(async () => {});
|
||||
export async function isAdminAuthenticated(): Promise<boolean> {
|
||||
return adminAuth.authenticated;
|
||||
}
|
||||
|
||||
/** Run an action and return the URL it redirected to (or throw if it didn't). */
|
||||
export async function captureRedirect(run: () => Promise<unknown>): Promise<string> {
|
||||
try {
|
||||
await run();
|
||||
} catch (error) {
|
||||
if (isRedirectError(error)) {
|
||||
return error.url;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw new Error("Expected the action to redirect, but it returned normally.");
|
||||
}
|
||||
|
||||
export function resetNextMocks() {
|
||||
revalidatePath.mockClear();
|
||||
clearAdminSessionCookie.mockClear();
|
||||
adminAuth.authenticated = true;
|
||||
}
|
||||
|
||||
/** Build a FormData from a flat record (strings and Files). */
|
||||
export function formDataFrom(fields: Record<string, string | File | undefined>): FormData {
|
||||
const formData = new FormData();
|
||||
for (const [key, value] of Object.entries(fields)) {
|
||||
if (value !== undefined) {
|
||||
formData.set(key, value);
|
||||
}
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
Reference in New Issue
Block a user