Initial commit: TravelHub multi-tenant flight booking platform
Angular + Go + PostgreSQL sample with strict per-tenant isolation. - Backend: Go API (basic auth, tenant-scoped repository layer, pgx pool) - Frontend: Angular 22 SPA (search, checkout, bookings, dashboard) - Database: schema, seed data and demo bookings migrations - Tests: 311 across Go (integration), Angular (unit) and Playwright (e2e), plus a generated evidence report with PDF export - Security: whitebox VAPT report under security/ Secrets (.env, tokens, DB credentials) are git-ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Evidence capture for passing tests.
|
||||
*
|
||||
* Playwright keeps screenshots and traces only when something fails, which is
|
||||
* the right default for debugging but useless as a record of what the suite
|
||||
* actually proved. This fixture lets a test mark the moments that matter and
|
||||
* record the data it observed, so a successful run produces a readable report
|
||||
* rather than a row of green ticks.
|
||||
*
|
||||
* await evidence.step('Search results for JFK to LHR', { total: 42, fare: 483.3 });
|
||||
*
|
||||
* Each call attaches a screenshot plus a JSON blob. `reporters/evidence.ts`
|
||||
* turns those attachments into the report.
|
||||
*/
|
||||
import { Page, TestInfo } from '@playwright/test';
|
||||
|
||||
/** Attachment name prefixes the reporter looks for. */
|
||||
export const SHOT_PREFIX = 'evidence';
|
||||
export const DATA_PREFIX = 'evidence-data';
|
||||
|
||||
export type Facts = Record<string, unknown>;
|
||||
|
||||
export class Evidence {
|
||||
private sequence = 0;
|
||||
|
||||
constructor(
|
||||
private readonly page: Page,
|
||||
private readonly info: TestInfo,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Records a named moment: a screenshot of the current page, and optionally
|
||||
* the data the test observed there.
|
||||
*/
|
||||
async step(name: string, facts?: Facts): Promise<void> {
|
||||
const seq = this.next();
|
||||
|
||||
const shot = await this.page.screenshot({ fullPage: false, animations: 'disabled' });
|
||||
await this.info.attach(`${SHOT_PREFIX}:${seq}:${name}`, {
|
||||
body: shot,
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
if (facts) {
|
||||
await this.attachFacts(seq, name, facts);
|
||||
}
|
||||
}
|
||||
|
||||
/** Records data with no screenshot -- for values read across several pages. */
|
||||
async note(name: string, facts: Facts): Promise<void> {
|
||||
await this.attachFacts(this.next(), name, facts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the whole scrollable page rather than the viewport. Slower, so it
|
||||
* is reserved for the screens worth showing in full.
|
||||
*/
|
||||
async fullPage(name: string, facts?: Facts): Promise<void> {
|
||||
const seq = this.next();
|
||||
|
||||
const shot = await this.page.screenshot({ fullPage: true, animations: 'disabled' });
|
||||
await this.info.attach(`${SHOT_PREFIX}:${seq}:${name}`, {
|
||||
body: shot,
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
if (facts) {
|
||||
await this.attachFacts(seq, name, facts);
|
||||
}
|
||||
}
|
||||
|
||||
private async attachFacts(seq: string, name: string, facts: Facts): Promise<void> {
|
||||
await this.info.attach(`${DATA_PREFIX}:${seq}:${name}`, {
|
||||
body: Buffer.from(JSON.stringify(facts, null, 2), 'utf8'),
|
||||
contentType: 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
private next(): string {
|
||||
this.sequence += 1;
|
||||
return String(this.sequence).padStart(2, '0');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user