Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
test("top bar uses compact controls on narrow mobile viewports", async ({ page }) => {
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
|
|
await expect(page.locator('meta[name="viewport"]')).toHaveAttribute(
|
|
"content",
|
|
/width=device-width/
|
|
);
|
|
await expect(page.getByRole("button", { name: "切换模拟方案" })).toBeHidden();
|
|
await expect(page.getByRole("button", { name: "切换地图 Dev Panel" })).toBeHidden();
|
|
const alertButton = page.getByRole("button", { name: /查看待办与告警/ });
|
|
const userButton = page.getByRole("button", { name: "打开用户菜单" });
|
|
await expect(alertButton).toBeVisible();
|
|
await expect(userButton).toBeVisible();
|
|
|
|
const [alertBox, userBox] = await Promise.all([
|
|
alertButton.boundingBox(),
|
|
userButton.boundingBox()
|
|
]);
|
|
expect(alertBox).not.toBeNull();
|
|
expect(userBox).not.toBeNull();
|
|
expect(alertBox?.width).toBe(40);
|
|
expect(alertBox?.height).toBe(40);
|
|
expect(userBox?.width).toBe(alertBox?.width);
|
|
expect(userBox?.height).toBe(alertBox?.height);
|
|
|
|
const [alertStyles, userStyles] = await Promise.all([
|
|
alertButton.evaluate((element) => {
|
|
const styles = getComputedStyle(element);
|
|
return { backgroundColor: styles.backgroundColor, borderRadius: styles.borderRadius };
|
|
}),
|
|
userButton.evaluate((element) => {
|
|
const styles = getComputedStyle(element);
|
|
return { backgroundColor: styles.backgroundColor, borderRadius: styles.borderRadius };
|
|
})
|
|
]);
|
|
expect(alertStyles).toEqual(userStyles);
|
|
});
|
|
|
|
test("top bar keeps text horizontal at the desktop breakpoint", async ({ page }) => {
|
|
await page.setViewportSize({ width: 1024, height: 844 });
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
|
|
const header = page.locator("header").first();
|
|
await expect(header).toBeVisible();
|
|
await expect.poll(async () => Math.round((await header.boundingBox())?.height ?? 0)).toBe(56);
|
|
|
|
await expect.poll(async () => header.evaluate((element) => {
|
|
return Array.from(element.querySelectorAll("span"))
|
|
.filter((span) => {
|
|
const style = getComputedStyle(span);
|
|
const rect = span.getBoundingClientRect();
|
|
return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0;
|
|
})
|
|
.map((span) => span.textContent?.trim())
|
|
.filter((text) => text === "任务浮条" || text === "工况任务" || text === "待办与告警").length;
|
|
})).toBe(0);
|
|
});
|