Advance M8-M11 parity workflows
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-17 04:37:07 -04:00
parent 7c16b279ae
commit 0fe8d2bb56
324 changed files with 31920 additions and 863 deletions

View File

@@ -0,0 +1,100 @@
import AxeBuilder from "@axe-core/playwright";
import { expect, test, type Locator, type Page } from "@playwright/test";
import path from "node:path";
const basicBlend = path.resolve(import.meta.dirname, "../../../tests/files/web/basic_scene.blend");
const app = (page: Page) => page.locator("main.blender-app");
async function focusWithKeyboard(page: Page, target: Locator, backwards = false): Promise<void> {
for (let index = 0; index < 80; index += 1) {
if (await target.evaluate((element) => element === document.activeElement)) return;
await page.keyboard.press(backwards ? "Shift+Tab" : "Tab");
}
throw new Error(`Keyboard focus did not reach ${await target.getAttribute("aria-label") ?? await target.textContent() ?? "target"}`);
}
async function expectVisibleKeyboardFocus(target: Locator): Promise<void> {
await expect(target).toBeFocused();
await expect.poll(() => target.evaluate((element) => {
const style = getComputedStyle(element);
return style.outlineStyle !== "none" && Number.parseFloat(style.outlineWidth) >= 2;
})).toBe(true);
}
async function expectNoSeriousAccessibilityViolations(page: Page, checkpoint: string): Promise<void> {
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
.analyze();
const violations = results.violations
.filter((violation) => violation.impact === "critical" || violation.impact === "serious")
.map((violation) => ({
id: violation.id,
impact: violation.impact,
help: violation.help,
targets: violation.nodes.map((node) => node.target.join(" ")),
}));
expect(violations, `${checkpoint}: ${JSON.stringify(violations, null, 2)}`).toEqual([]);
}
test("M7-16 completes the P0 project loop without pointer input and passes the accessibility gate", async ({ page }) => {
test.setTimeout(120_000);
await page.goto("/");
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
await expectNoSeriousAccessibilityViolations(page, "empty project");
const fileMenu = page.getByRole("button", { name: "文件", exact: true });
await focusWithKeyboard(page, fileMenu);
await expectVisibleKeyboardFocus(fileMenu);
await page.keyboard.press("Enter");
await expect(page.getByRole("menu", { name: "文件" })).toBeVisible();
await expect(page.getByRole("menuitem", { name: "打开" })).toBeFocused();
const fileChooserPromise = page.waitForEvent("filechooser");
await page.keyboard.press("Enter");
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(basicBlend);
await expect(page.getByText("BasicCube", { exact: true })).toBeVisible({ timeout: 30_000 });
await expect(app(page)).toHaveAttribute("data-user-action-open-status", "SUCCEEDED");
await expect(page.getByTestId("scene-stats")).toContainText("Objects 3");
await page.keyboard.press("F3");
const search = page.getByRole("textbox", { name: "搜索操作" });
await expectVisibleKeyboardFocus(search);
await page.keyboard.insertText("Add Cube");
await page.keyboard.press("Enter");
await expect(page.getByTestId("scene-stats")).toContainText("Objects 4");
await expect(app(page)).toHaveAttribute("data-dirty", "true");
const blendDownload = page.waitForEvent("download");
await page.keyboard.press("Control+s");
await expect((await blendDownload).suggestedFilename()).toBe("blender-web.blend");
await expect(app(page)).toHaveAttribute("data-user-action-save-status", "SUCCEEDED");
await expect(app(page)).toHaveAttribute("data-dirty", "false");
await page.keyboard.press("F3");
await expect(search).toBeFocused();
await page.keyboard.insertText("Close Project");
await page.keyboard.press("Enter");
await expect(page.getByTestId("scene-stats")).toContainText("Objects 0");
await page.reload();
await expect(page.getByTestId("engine-status")).toContainText("ready, open a .blend file", { timeout: 20_000 });
const recentProjects = page.getByRole("combobox", { name: "最近项目" });
await focusWithKeyboard(page, recentProjects);
await expectVisibleKeyboardFocus(recentProjects);
await page.keyboard.press("ArrowDown");
await page.keyboard.press("Enter");
await expect(page.getByTestId("scene-stats")).toContainText("Objects 4", { timeout: 30_000 });
await expect(app(page)).toHaveAttribute("data-dirty", "false");
await expectNoSeriousAccessibilityViolations(page, "reopened project");
const renderMenu = page.getByRole("button", { name: "渲染", exact: true });
await focusWithKeyboard(page, renderMenu, true);
await expectVisibleKeyboardFocus(renderMenu);
await page.keyboard.press("Enter");
await expect(page.getByRole("menuitem", { name: "导出 GLB" })).toBeFocused();
const glbDownload = page.waitForEvent("download");
await page.keyboard.press("Enter");
await expect((await glbDownload).suggestedFilename()).toBe("blender-web.glb");
await expect(app(page)).toHaveAttribute("data-user-action-export-status", "SUCCEEDED");
});