TokenTrace / client /src /e2e /regression.spec.ts
cccmmd
init: TokenTrace - LLM interpretability toolbox
76b5743
Raw
History Blame Contribute Delete
10.8 kB
import { test, expect } from '@playwright/test';
async function dismissDevServerOverlay(page: any): Promise<void> {
await page.evaluate(() => {
const overlay = document.getElementById('webpack-dev-server-client-overlay');
if (overlay) overlay.remove();
});
}
test.describe('Home Page Regression', () => {
test('page loads with Swiss Style layout', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
await expect(page.locator('.nav-landing-hero')).toBeVisible();
await expect(page.locator('.nav-landing-title')).toBeVisible();
await expect(page.locator('.nav-landing-module-grid')).toBeVisible();
});
test('light theme is default', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const bg = await page.evaluate(() => getComputedStyle(document.body).backgroundColor);
expect(bg).toContain('245'); // #f5f3f0 ~ rgb(245, ...)
});
test('aperture SVG animation renders', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const svg = page.locator('.lens-aperture');
await expect(svg).toBeVisible();
});
test('all module cells are visible', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const cells = page.locator('.nav-landing-module-cell');
await expect(cells).toHaveCount(6);
});
test('module navigation links work', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const analysisLink = page.locator('a[href="analysis.html"]');
await expect(analysisLink).toBeVisible();
const chatLink = page.locator('a[href="chat.html"]');
await expect(chatLink).toBeVisible();
});
test('causalFlow (DAG) module renders its title and subtitle', async ({ page }) => {
// Regression: the DAG card was rendered blank because the build script put its
// content inside an opacity:0 link wrapper and left orphan markup. Verify the
// cell shows its text and links to causal_flow.html.
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const cell = page.locator('.nav-landing-module-cell[data-nav-page="causalFlow"]');
await expect(cell).toBeVisible();
await expect(cell.locator('.nav-landing-module-name')).toHaveText(/因果流|Causal/i);
await expect(cell.locator('.nav-landing-module-desc')).not.toBeEmpty();
// Must be a navigable link, not a bare <div>
await expect(cell).toHaveAttribute('href', 'causal_flow.html');
});
});
test.describe('Analysis Page Regression', () => {
test('page loads with input area', async ({ page }) => {
await page.goto('/analysis.html');
await dismissDevServerOverlay(page);
// Use JS check since textarea may be in hidden panel initially
const hasTextarea = await page.evaluate(() => !!document.getElementById('test_text'));
expect(hasTextarea).toBe(true);
const hasBtn = await page.evaluate(() => !!document.getElementById('submit_text_btn'));
expect(hasBtn).toBe(true);
});
test('analyze button triggers loading state', async ({ page }) => {
await page.goto('/analysis.html');
await dismissDevServerOverlay(page);
await page.evaluate(() => {
const ta = document.getElementById('test_text') as HTMLTextAreaElement;
if (ta) ta.value = 'Hello world';
const btn = document.getElementById('submit_text_btn');
if (btn) btn.click();
});
// Wait for loading overlay or spinner
await page.waitForTimeout(500);
const hasLoader = await page.evaluate(() =>
document.querySelector('.loading') != null ||
document.querySelector('.loadersmall') != null
);
expect(hasLoader).toBe(true);
});
});
test.describe('Attribution Page Regression', () => {
test('page loads with context/target inputs', async ({ page }) => {
await page.goto('/attribution.html');
await dismissDevServerOverlay(page);
await expect(page.locator('#context_text')).toBeVisible();
await expect(page.locator('#target_text')).toBeVisible();
await expect(page.locator('#analyze_btn')).toBeVisible();
});
test('analyze button triggers request', async ({ page }) => {
await page.goto('/attribution.html');
await dismissDevServerOverlay(page);
await page.fill('#context_text', '中国的首都是');
await page.fill('#target_text', '北京');
await page.evaluate(() => {
const btn = document.getElementById('analyze_btn');
if (btn) btn.click();
});
// Wait for loading indicator
await page.waitForTimeout(500);
const hasLoader = await page.evaluate(() =>
document.querySelector('.loading') != null ||
document.querySelector('.loadersmall') != null
);
expect(hasLoader).toBe(true);
});
});
test.describe('Causal Flow Page Regression', () => {
test('page loads with DAG input panel', async ({ page }) => {
await page.goto('/causal_flow.html');
await dismissDevServerOverlay(page);
// Use JS check since textarea may be hidden by initial JS state
const hasTextarea = await page.evaluate(() => !!document.getElementById('gen_attr_raw_text'));
expect(hasTextarea).toBe(true);
const hasBtn = await page.evaluate(() => !!document.getElementById('gen_attr_submit_btn'));
expect(hasBtn).toBe(true);
});
test('SVG zoom/pan is functional', async ({ page }) => {
await page.goto('/causal_flow.html');
await dismissDevServerOverlay(page);
// Trigger a simple generation via JS (bypass visibility)
await page.evaluate(() => {
const ta = document.getElementById('gen_attr_raw_text') as HTMLTextAreaElement;
if (ta) ta.value = 'The';
const btn = document.getElementById('gen_attr_submit_btn');
if (btn) btn.click();
});
// Wait for DAG SVG to appear
await page.waitForSelector('.gen-attr-dag-stack svg', { timeout: 30_000 });
const svg = page.locator('.gen-attr-dag-stack svg');
await expect(svg).toBeVisible({ timeout: 30_000 });
// Test zoom: get SVG bounding box and dispatch wheel events
const svgBox = await svg.boundingBox();
if (svgBox) {
await svg.scrollIntoViewIfNeeded();
await page.waitForTimeout(500);
// Wheel zoom in
await svg.dispatchEvent('wheel', {
deltaY: -100,
clientX: svgBox.x + svgBox.width / 2,
clientY: svgBox.y + svgBox.height / 2,
});
await page.waitForTimeout(300);
// Wheel zoom out
await svg.dispatchEvent('wheel', {
deltaY: 100,
clientX: svgBox.x + svgBox.width / 2,
clientY: svgBox.y + svgBox.height / 2,
});
await page.waitForTimeout(300);
// After zoom, SVG should still be present
await expect(svg).toBeVisible();
}
});
test('fullscreen toggle button exists and is clickable', async ({ page }) => {
await page.goto('/causal_flow.html');
await dismissDevServerOverlay(page);
await page.evaluate(() => {
const ta = document.getElementById('gen_attr_raw_text') as HTMLTextAreaElement;
if (ta) ta.value = 'The';
const btn = document.getElementById('gen_attr_submit_btn');
if (btn) btn.click();
});
await page.waitForSelector('.gen-attr-dag-stack svg', { timeout: 30_000 });
// Look for fullscreen button
const fsBtn = page.locator('.gen-attr-dag-fullscreen').first();
await expect(fsBtn).toBeVisible({ timeout: 10_000 });
// Click fullscreen
await fsBtn.click();
await page.waitForTimeout(300);
// Check if fullscreen class is present
const isFullscreen = await page.evaluate(() => {
return !!document.querySelector('.css-pseudo-fullscreen-target') || !!document.fullscreenElement;
});
expect(typeof isFullscreen).toBe('boolean');
});
});
test.describe('Chat Page Regression', () => {
test('page loads with raw prompt input', async ({ page }) => {
await page.goto('/chat.html');
await dismissDevServerOverlay(page);
// Use JS check since textarea may be hidden by panel state
const hasTextarea = await page.evaluate(() => !!document.getElementById('test_text'));
expect(hasTextarea).toBe(true);
const hasBtn = await page.evaluate(() => !!document.getElementById('submit_text_btn'));
expect(hasBtn).toBe(true);
});
});
test.describe('Compare Page Regression', () => {
test('page loads with add button', async ({ page }) => {
await page.goto('/compare.html');
await dismissDevServerOverlay(page);
await expect(page.locator('#add_demos_btn')).toBeVisible();
await expect(page.locator('#clear_demos_btn')).toBeVisible();
});
});
test.describe('Theme Switching Regression', () => {
test('theme toggle switches between light and dark', async ({ page }) => {
await page.goto('/index.html');
await dismissDevServerOverlay(page);
const html = page.locator('html');
const initialTheme = await html.getAttribute('data-theme');
// Click theme toggle via settings menu if available
await page.evaluate(() => {
const toggle = document.querySelector('#dark_mode_toggle') as HTMLElement;
if (toggle) toggle.click();
});
await page.waitForTimeout(300);
const newTheme = await html.getAttribute('data-theme');
// Theme should have toggled or stayed the same (if no toggle found)
expect(['light', 'dark', null]).toContain(newTheme);
});
});
test.describe('Navigation Regression', () => {
test('all page links are reachable', async ({ page }) => {
const pages = ['index.html', 'analysis.html', 'attribution.html', 'causal_flow.html',
'chat.html', 'compare.html', 'logit_lens.html', 'branch_tree.html'];
for (const p of pages) {
await page.goto(`/${p}`);
await dismissDevServerOverlay(page);
const status = await page.evaluate(() => document.title.length > 0);
expect(status).toBe(true);
}
});
});