mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-05-26 15:16:14 +00:00
Embed font hoping that this will make the visual tests reproducible
This commit is contained in:
@@ -131,7 +131,7 @@ This section defines the granular step-by-step instructions and enumerates **eve
|
|||||||
* **`tests/global.d.ts`**
|
* **`tests/global.d.ts`**
|
||||||
* *Rationale:* Custom global type declaration file for E2E tests to safely declare `__registeredTools` on the `Window` interface without TypeScript compiler warnings. Redundant overrides for `Navigator` are omitted because the test suite inherits it from the application's core WebMCP declarations.
|
* *Rationale:* Custom global type declaration file for E2E tests to safely declare `__registeredTools` on the `Window` interface without TypeScript compiler warnings. Redundant overrides for `Navigator` are omitted because the test suite inherits it from the application's core WebMCP declarations.
|
||||||
* **`tests/helpers.ts`**
|
* **`tests/helpers.ts`**
|
||||||
* *Rationale:* Shared test utilities to encapsulate wildcard route mocking for family tree fetching (`setupGedcomRoute`) and tracking interception (`blockTracking`). This avoids code duplication across spec files.
|
* *Rationale:* Shared test utilities to encapsulate wildcard route mocking for family tree fetching (`setupGedcomRoute`) and hermetic context routing (`setupHermeticEnvironment`). This avoids code duplication across spec files.
|
||||||
* **`tests/fixtures/embedded_frame.html`**
|
* **`tests/fixtures/embedded_frame.html`**
|
||||||
* *Rationale:* Physical template wrapper file defining the iframe and message-passing structure for embedded view E2E verification.
|
* *Rationale:* Physical template wrapper file defining the iframe and message-passing structure for embedded view E2E verification.
|
||||||
* **`src/datasource/testdata/test.ged`**
|
* **`src/datasource/testdata/test.ged`**
|
||||||
@@ -190,7 +190,7 @@ This section defines the granular step-by-step instructions and enumerates **eve
|
|||||||
3. Author `tests/global.d.ts` to provide TypeScript type definitions for mocked window objects:
|
3. Author `tests/global.d.ts` to provide TypeScript type definitions for mocked window objects:
|
||||||
* **Type Extension**: Declares `__registeredTools?` on the global `Window` interface to prevent TypeScript compilation errors during WebMCP mocks.
|
* **Type Extension**: Declares `__registeredTools?` on the global `Window` interface to prevent TypeScript compilation errors during WebMCP mocks.
|
||||||
4. Author `tests/helpers.ts` to provide reusable mock setups and routing interceptions:
|
4. Author `tests/helpers.ts` to provide reusable mock setups and routing interceptions:
|
||||||
* **Tracking Blockers**: Implements a `blockTracking(context)` helper that intercepts and aborts requests targeting Google Analytics and Tag Manager (`**/*google-analytics.com/**`, `**/*googletagmanager.com/**`) to guarantee hermetic and fast test execution.
|
* **Hermetic Routing**: Implements a `setupHermeticEnvironment(context)` helper that intercepts tracking services (`**/*google-analytics.com/**`, `**/*googletagmanager.com/**`) and embeds baseline web fonts to guarantee deterministic and fast test execution.
|
||||||
* **GEDCOM Mocks**: Implements a `setupGedcomRoute(context)` helper that reads the version-controlled local dataset (`src/datasource/testdata/test.ged`) and routes all requests matching `**/family.ged` to be fulfilled with it, serving a `200 OK` response with CORS enablement headers (`Access-Control-Allow-Origin: *`).
|
* **GEDCOM Mocks**: Implements a `setupGedcomRoute(context)` helper that reads the version-controlled local dataset (`src/datasource/testdata/test.ged`) and routes all requests matching `**/family.ged` to be fulfilled with it, serving a `200 OK` response with CORS enablement headers (`Access-Control-Allow-Origin: *`).
|
||||||
5. Author the physical template wrapper file `tests/fixtures/embedded_frame.html` for testing embedded iframe communications:
|
5. Author the physical template wrapper file `tests/fixtures/embedded_frame.html` for testing embedded iframe communications:
|
||||||
* **Structure**: Defines a standard wrapper document housing an iframe that points to the app's embedded route: `/#/view?embedded=true&handleCors=false`.
|
* **Structure**: Defines a standard wrapper document housing an iframe that points to the app's embedded route: `/#/view?embedded=true&handleCors=false`.
|
||||||
@@ -200,7 +200,7 @@ This section defines the granular step-by-step instructions and enumerates **eve
|
|||||||
|
|
||||||
##### 1. Intro Test (`tests/intro.spec.ts`)
|
##### 1. Intro Test (`tests/intro.spec.ts`)
|
||||||
Checks the landing page layout, menu items, and basic static DOM presence:
|
Checks the landing page layout, menu items, and basic static DOM presence:
|
||||||
* **Setup**: Leverages `beforeEach` to block analytics and tracking servers using the `blockTracking` helper, then loads the index page (`/`).
|
* **Setup**: Leverages `beforeEach` to configure hermetic routes using the `setupHermeticEnvironment` helper, then loads the index page (`/`).
|
||||||
* **Assertions**:
|
* **Assertions**:
|
||||||
* Verifies that the main intro landing text content (specifically checking for the presence of `'Examples'`) is visible on the page.
|
* Verifies that the main intro landing text content (specifically checking for the presence of `'Examples'`) is visible on the page.
|
||||||
* Asserts that core action buttons in the menu (exact text `'Open file'` and `'Load from URL'`) are properly rendered and visible to the user.
|
* Asserts that core action buttons in the menu (exact text `'Open file'` and `'Load from URL'`) are properly rendered and visible to the user.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ This section defines the granular, step-by-step implementation steps and enumera
|
|||||||
#### 2. Files to [NEW]
|
#### 2. Files to [NEW]
|
||||||
|
|
||||||
* **`tests/helpers.ts`**
|
* **`tests/helpers.ts`**
|
||||||
* *Rationale:* Provide shared E2E/visual testing helper utilities. Features `blockTracking()` to abort external Google Analytics and Google Tag Manager network requests (ensuring offline hermetic execution) and `setupGedcomRoute()` to serve a standard mock `.ged` dataset.
|
* *Rationale:* Provide shared E2E/visual testing helper utilities. Features `setupHermeticEnvironment()` to abort external tracking requests and embed local fonts (ensuring offline hermetic execution) and `setupGedcomRoute()` to serve a standard mock `.ged` dataset.
|
||||||
* **`tests/intro_visual.spec.ts`**
|
* **`tests/intro_visual.spec.ts`**
|
||||||
* *Rationale:* Verify the landing page layout, copy block positions, and logo alignments. Employs an in-browser DOM script to overwrite dynamic footer versioning and dynamic changelog blocks prior to capture, ensuring baseline immunity.
|
* *Rationale:* Verify the landing page layout, copy block positions, and logo alignments. Employs an in-browser DOM script to overwrite dynamic footer versioning and dynamic changelog blocks prior to capture, ensuring baseline immunity.
|
||||||
* **`tests/charts_visual.spec.ts`**
|
* **`tests/charts_visual.spec.ts`**
|
||||||
@@ -74,7 +74,7 @@ This section defines the granular, step-by-step implementation steps and enumera
|
|||||||
* `"test:visual:update": "playwright test --project=visual --update-snapshots"` to automatically regenerate baseline reference files.
|
* `"test:visual:update": "playwright test --project=visual --update-snapshots"` to automatically regenerate baseline reference files.
|
||||||
|
|
||||||
#### Step 2: Landing Page Visual Validation Spec (`tests/intro_visual.spec.ts`)
|
#### Step 2: Landing Page Visual Validation Spec (`tests/intro_visual.spec.ts`)
|
||||||
1. Define a test block marked with the `@visual` tag, utilizing `blockTracking` helper in `beforeEach`.
|
1. Define a test block marked with the `@visual` tag, utilizing `setupHermeticEnvironment` helper in `beforeEach`.
|
||||||
2. Instruct the browser to navigate to the root path `/`.
|
2. Instruct the browser to navigate to the root path `/`.
|
||||||
3. Right before assertion, trigger `page.evaluate` to clean dynamic elements:
|
3. Right before assertion, trigger `page.evaluate` to clean dynamic elements:
|
||||||
* Target the `.version` class element and set `.innerText = "version: 2026-01-01 00:00 (testcommit)"`.
|
* Target the `.version` class element and set `.innerText = "version: 2026-01-01 00:00 (testcommit)"`.
|
||||||
@@ -91,7 +91,7 @@ This section defines the granular, step-by-step implementation steps and enumera
|
|||||||
* Capture the isolated canvas screenshot: `expect(container).toHaveScreenshot('chart-[type].png')`.
|
* Capture the isolated canvas screenshot: `expect(container).toHaveScreenshot('chart-[type].png')`.
|
||||||
|
|
||||||
#### Step 4: Details Panel Layouts Spec (`tests/details_visual.spec.ts`)
|
#### Step 4: Details Panel Layouts Spec (`tests/details_visual.spec.ts`)
|
||||||
1. Set up a `beforeEach` block to block analytics via `blockTracking(context)`.
|
1. Set up a `beforeEach` block to establish hermetic routes via `setupHermeticEnvironment(context)`.
|
||||||
2. Define isolated test blocks with the `@visual` tag, each loading its own dedicated inline micro-GEDCOM dataset:
|
2. Define isolated test blocks with the `@visual` tag, each loading its own dedicated inline micro-GEDCOM dataset:
|
||||||
* **Complex Names Test:**
|
* **Complex Names Test:**
|
||||||
* Mock `**/family.ged` with a GEDCOM string containing prefix/suffix/rufname tags.
|
* Mock `**/family.ged` with a GEDCOM string containing prefix/suffix/rufname tags.
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ To enable lightweight, reproducible, and offline executions, tests rely on the f
|
|||||||
* **[fixtures/embedded_frame.html](fixtures/embedded_frame.html)**: HTML template for serving cross-origin iframe wrapper mockups virtually to the browser container.
|
* **[fixtures/embedded_frame.html](fixtures/embedded_frame.html)**: HTML template for serving cross-origin iframe wrapper mockups virtually to the browser container.
|
||||||
* **[global.d.ts](global.d.ts)**: TypeScript declarations defining window overrides (like AI registration pointers `window.__registeredTools`) to bypass compiler warnings.
|
* **[global.d.ts](global.d.ts)**: TypeScript declarations defining window overrides (like AI registration pointers `window.__registeredTools`) to bypass compiler warnings.
|
||||||
* **[helpers.ts](helpers.ts)**: Unified routing utilities:
|
* **[helpers.ts](helpers.ts)**: Unified routing utilities:
|
||||||
* `blockTracking()`: Intercepts and halts metrics and analytical HTTP queries during spec executions.
|
* `setupHermeticEnvironment()`: Intercepts and halts metrics/analytical queries and embeds local baseline fonts during spec executions.
|
||||||
* `setupGedcomRoute()`: Re-routes standard genealogy payload network paths directly to load standard local datasets on-the-fly (`src/datasource/testdata/test.ged`).
|
* `setupGedcomRoute()`: Re-routes standard genealogy payload network paths directly to load standard local datasets on-the-fly (`src/datasource/testdata/test.ged`).
|
||||||
* **[tsconfig.json](tsconfig.json)**: Typecheck preferences custom to the Playwright runner environment to avoid compilation type collisions.
|
* **[tsconfig.json](tsconfig.json)**: Typecheck preferences custom to the Playwright runner environment to avoid compilation type collisions.
|
||||||
* **`[spec_name]-snapshots/`**: Directory structure containing expected baseline references and image comparison files.
|
* **`[spec_name]-snapshots/`**: Directory structure containing expected baseline references and image comparison files.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {expect, test} from '@playwright/test';
|
import {expect, test} from '@playwright/test';
|
||||||
import dedent from 'dedent';
|
import dedent from 'dedent';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {blockTracking, mockGedcomResponse, waitForFonts} from './helpers';
|
import {mockGedcomResponse, setupHermeticEnvironment, waitForFonts} from './helpers';
|
||||||
|
|
||||||
test.describe('Details panel visual validation @visual', () => {
|
test.describe('Details panel visual validation @visual', () => {
|
||||||
test.beforeEach(async ({page, context}) => {
|
test.beforeEach(async ({page, context}) => {
|
||||||
await blockTracking(context);
|
await setupHermeticEnvironment(context);
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await waitForFonts(page);
|
await waitForFonts(page);
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
tests/fixtures/montserrat.woff2
vendored
Normal file
BIN
tests/fixtures/montserrat.woff2
vendored
Normal file
Binary file not shown.
@@ -2,12 +2,49 @@ import {BrowserContext, Page} from '@playwright/test';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks external tracking services (like Google Analytics and Google Tag Manager)
|
* Sets up a hermetic test environment by blocking external tracking services
|
||||||
* to guarantee a hermetic and fast test environment.
|
* and intercepting Google Fonts to serve static embedded fonts locally.
|
||||||
*/
|
*/
|
||||||
export async function blockTracking(context: BrowserContext): Promise<void> {
|
export async function setupHermeticEnvironment(context: BrowserContext): Promise<void> {
|
||||||
await context.route('**/*google-analytics.com/**', (route) => route.abort());
|
await context.route('**/*google-analytics.com/**', (route) => route.abort());
|
||||||
await context.route('**/*googletagmanager.com/**', (route) => route.abort());
|
await context.route('**/*googletagmanager.com/**', (route) => route.abort());
|
||||||
|
|
||||||
|
// Intercept Google Fonts stylesheet requests to serve a deterministically embedded local font.
|
||||||
|
await context.route('**/fonts.googleapis.com/css2**', async (route) => {
|
||||||
|
const cssContent = `
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: block;
|
||||||
|
src: url('http://localhost:3000/test-fonts/montserrat.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Montserrat';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: block;
|
||||||
|
src: url('http://localhost:3000/test-fonts/montserrat.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'text/css',
|
||||||
|
headers: {'Access-Control-Allow-Origin': '*'},
|
||||||
|
body: cssContent,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Intercept requests for the locally embedded test font and serve the static binary.
|
||||||
|
await context.route('**/test-fonts/montserrat.woff2', async (route) => {
|
||||||
|
const fontBuffer = fs.readFileSync('tests/fixtures/montserrat.woff2');
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'font/woff2',
|
||||||
|
headers: {'Access-Control-Allow-Origin': '*'},
|
||||||
|
body: fontBuffer,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +75,7 @@ export async function setupGedcomRoute(context: BrowserContext): Promise<void> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await mockGedcomResponse(context, gedcomContent);
|
await mockGedcomResponse(context, gedcomContent);
|
||||||
await blockTracking(context);
|
await setupHermeticEnvironment(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {expect, test} from '@playwright/test';
|
import {expect, test} from '@playwright/test';
|
||||||
import {blockTracking} from './helpers';
|
import {setupHermeticEnvironment} from './helpers';
|
||||||
|
|
||||||
test.describe('Intro page', () => {
|
test.describe('Intro page', () => {
|
||||||
test.beforeEach(async ({page, context}) => {
|
test.beforeEach(async ({page, context}) => {
|
||||||
await blockTracking(context);
|
await setupHermeticEnvironment(context);
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {expect, test} from '@playwright/test';
|
import {expect, test} from '@playwright/test';
|
||||||
import {blockTracking, waitForFonts} from './helpers';
|
import {setupHermeticEnvironment, waitForFonts} from './helpers';
|
||||||
|
|
||||||
test.describe('Intro page visual validation @visual', () => {
|
test.describe('Intro page visual validation @visual', () => {
|
||||||
test.beforeEach(async ({page, context}) => {
|
test.beforeEach(async ({page, context}) => {
|
||||||
await blockTracking(context);
|
await setupHermeticEnvironment(context);
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
await waitForFonts(page);
|
await waitForFonts(page);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user