120 lines
4.3 KiB
TypeScript
120 lines
4.3 KiB
TypeScript
|
|
/**
|
|||
|
|
* 综合概览大屏 (/overview) 验收测试。
|
|||
|
|
*
|
|||
|
|
* 与 user-flows.spec.ts 一致:用 addInitScript 注入 cbpoa_token 绕过登录门,
|
|||
|
|
* page.route 拦截 /api/** 使套件 hermetic(无需 :8000)。/wuhan_districts.geojson
|
|||
|
|
* 走真实静态资源(dev server 提供),由 Leaflet 取用。
|
|||
|
|
*/
|
|||
|
|
import { test, expect, Page } from '@playwright/test';
|
|||
|
|
import { TESTIDS } from '../src/utils/testids';
|
|||
|
|
|
|||
|
|
/** 13 区里造两条数据,断言 choropleth 能着色、toggle 能切换。 */
|
|||
|
|
function districtPayload() {
|
|||
|
|
return {
|
|||
|
|
districts: [
|
|||
|
|
{ district: '武昌区', outpatient: 120, inpatient: 30, total: 150, outpatient_ratio: 0.8, inpatient_ratio: 0.2 },
|
|||
|
|
{ district: '江岸', outpatient: 60, inpatient: 10, total: 70, outpatient_ratio: 0.86, inpatient_ratio: 0.14 },
|
|||
|
|
],
|
|||
|
|
total: 220,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function seedAuthAndMockApi(page: Page) {
|
|||
|
|
await page.addInitScript(() => {
|
|||
|
|
localStorage.setItem('cbpoa_token', 'e2e-test-token');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
await page.route('/api/**', (route) => {
|
|||
|
|
const url = route.request().url();
|
|||
|
|
|
|||
|
|
const json = (body: unknown) =>
|
|||
|
|
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(body) });
|
|||
|
|
|
|||
|
|
if (url.includes('/alerts')) {
|
|||
|
|
return json({ alerts: [], total: 0 });
|
|||
|
|
}
|
|||
|
|
if (url.includes('/cases/stats')) {
|
|||
|
|
return json({
|
|||
|
|
total_outpatient: 1000,
|
|||
|
|
total_inpatient: 200,
|
|||
|
|
date_range: { start: '2023-01-01', end: '2023-12-01' },
|
|||
|
|
top_districts: [],
|
|||
|
|
top_diagnoses: [
|
|||
|
|
{ diagnosis: '上呼吸道感染', outpatient: 300, inpatient: 40 },
|
|||
|
|
{ diagnosis: '肺炎', outpatient: 120, inpatient: 80 },
|
|||
|
|
],
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (url.includes('/cases/trend')) {
|
|||
|
|
return json({
|
|||
|
|
trend: [
|
|||
|
|
{ date: '2023-11-01', outpatient: 10, inpatient: 2, total: 12 },
|
|||
|
|
{ date: '2023-11-02', outpatient: 14, inpatient: 3, total: 17 },
|
|||
|
|
],
|
|||
|
|
summary: {
|
|||
|
|
total_outpatient: 24,
|
|||
|
|
total_inpatient: 5,
|
|||
|
|
period_count: 2,
|
|||
|
|
avg_daily_outpatient: 12,
|
|||
|
|
avg_daily_inpatient: 2.5,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (url.includes('/cases/districts')) {
|
|||
|
|
return json(districtPayload());
|
|||
|
|
}
|
|||
|
|
if (url.includes('/risk/stats')) {
|
|||
|
|
return json({ high_risk_count: 7, total_grids: 100, avg_risk: 0.4 });
|
|||
|
|
}
|
|||
|
|
if (url.includes('/environment/pollutants')) {
|
|||
|
|
return json({ data: [{ date: '2023-11-01', AQI: 80 }, { date: '2023-11-02', AQI: 95 }] });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return json({ data: [], items: [], total: 0 });
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test.describe('Overview 大屏', () => {
|
|||
|
|
test.use({ viewport: { width: 1280, height: 900 } });
|
|||
|
|
|
|||
|
|
test.beforeEach(async ({ page }) => {
|
|||
|
|
await seedAuthAndMockApi(page);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('renders kpi-row, choropleth, as-of badge and metric toggle', async ({ page }) => {
|
|||
|
|
await page.goto('/overview');
|
|||
|
|
|
|||
|
|
await expect(page.locator(`[data-testid="${TESTIDS.pageOverview}"]`)).toBeVisible();
|
|||
|
|
await expect(page.locator(`[data-testid="${TESTIDS.kpiRow}"]`)).toBeVisible();
|
|||
|
|
await expect(page.locator(`[data-testid="${TESTIDS.choroplethWrapper}"]`)).toBeVisible();
|
|||
|
|
|
|||
|
|
// Literal honesty badge — exact text.
|
|||
|
|
const badge = page.locator(`[data-testid="${TESTIDS.asofBadge}"]`);
|
|||
|
|
await expect(badge).toBeVisible();
|
|||
|
|
await expect(badge).toHaveText('数据截至2023-12');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('门诊/住院 toggle switches active segment without error', async ({ page }) => {
|
|||
|
|
await page.goto('/overview');
|
|||
|
|
await expect(page.locator(`[data-testid="${TESTIDS.choroplethWrapper}"]`)).toBeVisible();
|
|||
|
|
|
|||
|
|
const outBtn = page.locator(`[data-testid="${TESTIDS.outinpatientToggle}-outpatient"]`);
|
|||
|
|
const inBtn = page.locator(`[data-testid="${TESTIDS.outinpatientToggle}-inpatient"]`);
|
|||
|
|
const allBtn = page.locator(`[data-testid="${TESTIDS.outinpatientToggle}-all"]`);
|
|||
|
|
|
|||
|
|
// Default: 全部 active (primary background).
|
|||
|
|
await expect(allBtn).toHaveClass(/bg-primary/);
|
|||
|
|
|
|||
|
|
await outBtn.click();
|
|||
|
|
await expect(outBtn).toHaveClass(/bg-primary/);
|
|||
|
|
await expect(allBtn).not.toHaveClass(/bg-primary/);
|
|||
|
|
|
|||
|
|
await inBtn.click();
|
|||
|
|
await expect(inBtn).toHaveClass(/bg-primary/);
|
|||
|
|
await expect(outBtn).not.toHaveClass(/bg-primary/);
|
|||
|
|
|
|||
|
|
// Wrapper still mounted after toggling — no render crash.
|
|||
|
|
await expect(page.locator(`[data-testid="${TESTIDS.choroplethWrapper}"]`)).toBeVisible();
|
|||
|
|
});
|
|||
|
|
});
|