feat(web-platform): release V0.9.3 全模块界面重构
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled

This commit is contained in:
2026-09-08 17:57:57 +08:00
parent 831d0b95bb
commit 13e35be98b
124 changed files with 5182 additions and 2141 deletions
+106
View File
@@ -0,0 +1,106 @@
import { expect, test, type Locator } from '@playwright/test';
async function expectWithinViewport(locator: Locator, width: number, height: number) {
await expect(locator).toBeVisible();
await expect
.poll(async () => {
const box = await locator.boundingBox();
return Boolean(
box &&
box.x >= 7 &&
box.y >= 7 &&
box.x + box.width <= width - 7 &&
box.y + box.height <= height - 7,
);
})
.toBe(true);
}
for (const theme of ['dark', 'light']) {
for (const [width, height] of [
[1920, 1080],
[1440, 900],
[1366, 768],
[1024, 768],
[768, 800],
]) {
test(`共享浮层边界与主题 ${theme} ${width}×${height}`, async ({ page }) => {
await page.setViewportSize({ width, height });
await page.addInitScript(
(value) => localStorage.setItem('mujoco-platform-theme', value),
theme,
);
await page.goto('/');
const trigger = page.getByRole('button', { name: /隐藏工程面板|显示工程面板/ });
await trigger.focus();
const tooltip = page.getByRole('tooltip');
await expectWithinViewport(tooltip, width, height);
await expect(trigger).toHaveAttribute(
'aria-describedby',
(await tooltip.getAttribute('id')) as string,
);
expect(
await tooltip.evaluate((node) => node.closest('.theme-dark, .theme-light')?.className),
).toContain(`theme-${theme}`);
await page.keyboard.press('Escape');
await expect(tooltip).toHaveCount(0);
await page.getByRole('button', { name: /FPS \d/ }).click();
const popover = page.getByRole('dialog', { name: '性能详情' });
await expectWithinViewport(popover, width, height);
await page.screenshot({
path: test.info().outputPath(`performance-${theme}-${width}.png`),
fullPage: true,
});
await page.setViewportSize({ width: width - 40, height: height - 40 });
await expectWithinViewport(popover, width - 40, height - 40);
await page.keyboard.press('Escape');
await expect(popover).toHaveCount(0);
});
}
}
test('边缘 hover 提示可移入,disabled 控件说明可经键盘访问', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto('/');
const theme = page.getByRole('button', { name: '打开命令面板' });
await theme.hover();
const tooltip = page.getByRole('tooltip');
await expectWithinViewport(tooltip, 1440, 900);
await tooltip.hover();
await expect(tooltip).toBeVisible();
await page.keyboard.press('Escape');
await expect(tooltip).toHaveCount(0);
const step = page.getByRole('button', { name: '单步', exact: true });
await expect(step).toBeDisabled();
await step.locator('..').focus();
await expect(tooltip).toHaveText('单步(暂停时可用)');
expect(await theme.evaluate((node) => node.parentElement?.hasAttribute('tabindex'))).toBe(false);
});
test('全屏中的对话框 Tooltip 不裁剪且 Escape 仅关闭顶层', async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto('/');
await page.getByRole('button', { name: '更多工作台操作' }).click();
await page.getByRole('menuitem', { name: '进入全屏' }).click();
await page.getByRole('button', { name: '更多工作台操作' }).click();
await page.getByRole('menuitem', { name: '工作台设置' }).click();
const dialog = page.getByRole('dialog', { name: '工作台设置' });
await expect(dialog).toBeVisible();
const close = dialog.getByRole('button', { name: '关闭', exact: true });
await close.focus();
const tooltip = page.getByRole('tooltip');
await expectWithinViewport(tooltip, 1440, 900);
expect(await tooltip.evaluate((node) => document.fullscreenElement?.contains(node))).toBe(true);
expect(await tooltip.evaluate((node) => Boolean(node.closest('[role="dialog"]')))).toBe(false);
await page.screenshot({
path: test.info().outputPath('fullscreen-dialog-tooltip.png'),
fullPage: true,
});
await page.keyboard.press('Escape');
await expect(tooltip).toHaveCount(0);
await expect(dialog).toBeVisible();
await page.keyboard.press('Escape');
await expect(dialog).toHaveCount(0);
await page.getByRole('button', { name: '更多工作台操作' }).click();
await page.getByRole('menuitem', { name: '退出全屏' }).click();
});