Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3de96f9d7b |
@@ -15,7 +15,7 @@ function subscribeCompact(listener: () => void) {
|
|||||||
media.addEventListener("change", listener);
|
media.addEventListener("change", listener);
|
||||||
return () => media.removeEventListener("change", listener);
|
return () => media.removeEventListener("change", listener);
|
||||||
}
|
}
|
||||||
const rowClass = "min-h-8 gap-2 text-xs active:bg-accent";
|
const rowClass = "min-h-8 gap-2 text-xs transition-none active:bg-accent";
|
||||||
|
|
||||||
export function PresentationHistoryMenu({ entries, selectedId, mergedEntry, mergedEntries, onSelect }: PresentationHistoryControls) {
|
export function PresentationHistoryMenu({ entries, selectedId, mergedEntry, mergedEntries, onSelect }: PresentationHistoryControls) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|||||||
@@ -6,6 +6,40 @@ test.beforeEach(async ({ page }) => {
|
|||||||
await prepareWorkbench(page);
|
await prepareWorkbench(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const width of [1440, 1024, 375, 320]) {
|
||||||
|
test(`header hides and restores the expanded timeline at ${width}px`, async ({ page }, testInfo) => {
|
||||||
|
await page.setViewportSize({ width, height: 900 });
|
||||||
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||||
|
const header = page.locator(".workbench-top-bar");
|
||||||
|
const timeline = page.getByRole("region", {
|
||||||
|
name: width >= 1024 ? "运行时间轴" : "移动端运行时间轴摘要", exact: true
|
||||||
|
});
|
||||||
|
const hide = header.getByRole("button", { name: "隐藏运行时间轴", exact: true });
|
||||||
|
await expect(timeline).toBeVisible();
|
||||||
|
await expect(hide).toHaveAttribute("aria-pressed", "true");
|
||||||
|
const bounds = (await hide.boundingBox())!;
|
||||||
|
expect(bounds.x).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(bounds.x + bounds.width).toBeLessThanOrEqual(width);
|
||||||
|
if (width >= 1024) await timeline.getByRole("button", { name: "展开时间轴", exact: true }).click();
|
||||||
|
else {
|
||||||
|
await timeline.getByRole("button", { name: "打开完整运行时间轴", exact: true }).click();
|
||||||
|
await expect(page.getByRole("region", { name: "运行时间轴抽屉", exact: true })).toBeVisible();
|
||||||
|
}
|
||||||
|
await hide.click();
|
||||||
|
await expect(timeline).toBeHidden();
|
||||||
|
await expect(page.getByRole("region", { name: "运行时间轴抽屉", exact: true })).toBeHidden();
|
||||||
|
const show = header.getByRole("button", { name: "显示运行时间轴", exact: true });
|
||||||
|
await expect(show).toHaveAttribute("aria-pressed", "false");
|
||||||
|
await show.click();
|
||||||
|
await expect(timeline).toBeVisible();
|
||||||
|
await expect(hide).toHaveAttribute("aria-pressed", "true");
|
||||||
|
if (width >= 1024) await expect(timeline.getByRole("button", { name: "收起时间轴", exact: true })).toBeVisible();
|
||||||
|
await testInfo.attach(`header-timeline-${width}`, {
|
||||||
|
body: await header.screenshot(), contentType: "image/png"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test("desktop controls share focus, hover, press and open states", async ({ page }) => {
|
test("desktop controls share focus, hover, press and open states", async ({ page }) => {
|
||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||||
|
|||||||
@@ -1,9 +1,25 @@
|
|||||||
import { expect, test, type Page, type TestInfo } from "@playwright/test";
|
import { expect, test, type Locator, type Page, type TestInfo } from "@playwright/test";
|
||||||
import { investigationFixture, mockInvestigationPresentation, selectInvestigation, showInvestigationMap } from "./support/mock-investigation-presentation";
|
import { investigationFixture, mockInvestigationPresentation, selectInvestigation, showInvestigationMap } from "./support/mock-investigation-presentation";
|
||||||
|
import { readThemeColor } from "./support/theme-colors";
|
||||||
|
|
||||||
const chart = (id: string, title: string) => ({ action: "open_chart", chart_id: id, evidence_refs: ["synthetic:reading"],
|
const chart = (id: string, title: string) => ({ action: "open_chart", chart_id: id, evidence_refs: ["synthetic:reading"],
|
||||||
chart: { title, chart_type: "line", x_data: ["08:00", "08:15"], series: [{ name: "压力(m)", data: [30, 28] }] } });
|
chart: { title, chart_type: "line", x_data: ["08:00", "08:15"], series: [{ name: "压力(m)", data: [30, 28] }] } });
|
||||||
|
|
||||||
|
async function sampleFastHoverBackground(items: Locator[]) {
|
||||||
|
const samples: Array<{ actual: string; expected: string }> = [];
|
||||||
|
for (let repeat = 0; repeat < 3; repeat++) {
|
||||||
|
for (const item of items) {
|
||||||
|
const expected = await readThemeColor(item, "--action-selection-soft");
|
||||||
|
await item.hover();
|
||||||
|
const actual = await item.evaluate((node) => new Promise<string>((resolve) => {
|
||||||
|
requestAnimationFrame(() => resolve(getComputedStyle(node).backgroundColor));
|
||||||
|
}));
|
||||||
|
samples.push({ actual, expected });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
async function captureMenus(page: Page, testInfo: TestInfo, name: string) {
|
async function captureMenus(page: Page, testInfo: TestInfo, name: string) {
|
||||||
for (const menu of await page.getByRole("menu").all()) {
|
for (const menu of await page.getByRole("menu").all()) {
|
||||||
await menu.evaluate((node) => Promise.all(node.getAnimations().map((animation) => animation.finished.catch(() => undefined))));
|
await menu.evaluate((node) => Promise.all(node.getAnimations().map((animation) => animation.finished.catch(() => undefined))));
|
||||||
@@ -56,8 +72,18 @@ test("hover keeps an expanded category stable across its title, arrow and origin
|
|||||||
const arrow = root.getByRole("menuitem", { name: "展开图表步骤", exact: true });
|
const arrow = root.getByRole("menuitem", { name: "展开图表步骤", exact: true });
|
||||||
const title = root.getByRole("menuitem", { name: "图表", exact: true });
|
const title = root.getByRole("menuitem", { name: "图表", exact: true });
|
||||||
const steps = page.getByRole("menu", { name: "图表展示步骤", exact: true });
|
const steps = page.getByRole("menu", { name: "图表展示步骤", exact: true });
|
||||||
|
const firstLevel = await sampleFastHoverBackground([title, root.getByRole("menuitem", { name: "覆盖层", exact: true })]);
|
||||||
await arrow.hover();
|
await arrow.hover();
|
||||||
await expect(steps).toBeVisible();
|
await expect(steps).toBeVisible();
|
||||||
|
const secondLevel = await sampleFastHoverBackground([
|
||||||
|
steps.getByRole("menuitem", { name: "1. 测点 P1 压力趋势", exact: true }),
|
||||||
|
steps.getByRole("menuitem", { name: "2. 测点 P2 压力趋势", exact: true })
|
||||||
|
]);
|
||||||
|
const backgrounds = [...firstLevel, ...secondLevel];
|
||||||
|
await testInfo.attach("fast-hover-backgrounds", { body: JSON.stringify({ firstLevel, secondLevel }), contentType: "application/json" });
|
||||||
|
expect(backgrounds).toHaveLength(12);
|
||||||
|
expect(backgrounds.every((sample) => sample.expected !== "rgba(0, 0, 0, 0)")).toBe(true);
|
||||||
|
expect(backgrounds.map((sample) => sample.actual)).toEqual(backgrounds.map((sample) => sample.expected));
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
const changes: string[] = [];
|
const changes: string[] = [];
|
||||||
const observer = new MutationObserver((records) => {
|
const observer = new MutationObserver((records) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user