68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
import { mkdirSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
|
|
export interface TransactionalDatabase {
|
|
tx<T>(callback: (transaction: TransactionalDatabase) => Promise<T>): Promise<T>;
|
|
}
|
|
|
|
const ROLLBACK = Symbol("wrnexus-test-rollback");
|
|
|
|
/** Run test work in a real transaction and always force rollback. */
|
|
export async function withDatabaseRollback<T>(
|
|
db: TransactionalDatabase,
|
|
run: (transaction: TransactionalDatabase) => T | Promise<T>,
|
|
): Promise<T> {
|
|
let output!: T;
|
|
try {
|
|
await db.tx(async (transaction) => {
|
|
output = await run(transaction);
|
|
throw ROLLBACK;
|
|
});
|
|
} catch (error) {
|
|
if (error !== ROLLBACK) throw error;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
export function createFactory<T extends Record<string, unknown>>(build: (sequence: number) => T) {
|
|
let sequence = 0;
|
|
return {
|
|
build(overrides: Partial<T> = {}): T {
|
|
return { ...build(++sequence), ...overrides };
|
|
},
|
|
buildMany(count: number, overrides: Partial<T> = {}): T[] {
|
|
if (!Number.isInteger(count) || count < 0 || count > 10_000)
|
|
throw new RangeError("Factory count must be between 0 and 10000");
|
|
return Array.from({ length: count }, () => ({ ...build(++sequence), ...overrides }));
|
|
},
|
|
reset(): void {
|
|
sequence = 0;
|
|
},
|
|
};
|
|
}
|
|
|
|
export interface BrowserArtifactPage {
|
|
screenshot(options: { path: string; fullPage?: boolean }): Promise<unknown>;
|
|
context(): { tracing?: { stop(options: { path: string }): Promise<unknown> } };
|
|
}
|
|
|
|
export async function captureBrowserArtifacts(
|
|
page: BrowserArtifactPage,
|
|
testName: string,
|
|
options: { root?: string; screenshot?: boolean; trace?: boolean } = {},
|
|
): Promise<{ screenshot?: string; trace?: string }> {
|
|
const safe = testName.replace(/[^A-Za-z0-9_.-]+/g, "-").slice(0, 120) || "test";
|
|
const root = resolve(options.root ?? join("test-results", "wrnexus"));
|
|
mkdirSync(root, { recursive: true });
|
|
const output: { screenshot?: string; trace?: string } = {};
|
|
if (options.screenshot !== false) {
|
|
output.screenshot = join(root, `${safe}.png`);
|
|
await page.screenshot({ path: output.screenshot, fullPage: true });
|
|
}
|
|
if (options.trace && page.context().tracing) {
|
|
output.trace = join(root, `${safe}.zip`);
|
|
await page.context().tracing!.stop({ path: output.trace });
|
|
}
|
|
return output;
|
|
}
|