- Add 11:07 Chinese-narrated lesson video, VTT, poster and 13 chapter screenshots. - Regenerate 20-render WebGL2 matrix and 15 operation captures on RTX 4060. - Fix WASM staging for non-ASCII import filenames (猴头.glb); rebuild web_engine. - Harden lesson video recording pipeline; fix stale lesson-link E2E selector. - Add RE PBR derived libraries, lesson page, evidence and status report. Co-Authored-By: Claude Code <noreply@anthropic.com>
78 lines
3.8 KiB
TypeScript
78 lines
3.8 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
const PBR_FIXTURE = path.resolve(HERE, "../../../tests/files/web/m12_glb_main_v1/pbr.blend");
|
|
|
|
test.use({ viewport: { width: 1600, height: 1000 } });
|
|
|
|
const objectCount = async (page: import("@playwright/test").Page): Promise<number> => {
|
|
const text = await page.getByTestId("scene-stats").innerText();
|
|
return Number(/Objects (\d+)/.exec(text)?.[1] ?? "0");
|
|
};
|
|
|
|
async function openShading(page: import("@playwright/test").Page) {
|
|
await page.goto("/?ui=web");
|
|
await page.locator('[data-testid="blend-file-input"]').setInputFiles(PBR_FIXTURE);
|
|
await expect(page.locator(".blender-app")).toHaveAttribute("data-project-id", /.+/, { timeout: 60_000 });
|
|
await expect(page.getByText(/Engine: SceneIR/).first()).toBeVisible({ timeout: 60_000 });
|
|
const webTools = page.locator('[data-testid="native-web-tools-toggle"]');
|
|
if (await webTools.count()) await webTools.click();
|
|
await page.getByRole("button", { name: "Shading", exact: true }).click({ force: true });
|
|
await expect(page.locator(".pbr-asset-library")).toBeVisible({ timeout: 30_000 });
|
|
await expect(page.locator(".pbr-asset-card")).toHaveCount(20, { timeout: 60_000 });
|
|
}
|
|
|
|
test("PBR asset library seeds OPFS, appends a model, assigns a material and renders", async ({ page }) => {
|
|
test.setTimeout(300_000);
|
|
await openShading(page);
|
|
|
|
await expect.poll(async () => page.locator(".pbr-asset-thumb img").evaluateAll((images) =>
|
|
images.filter((image) => (image as HTMLImageElement).naturalWidth > 0).length), { timeout: 30_000 }).toBe(20);
|
|
|
|
const before = await objectCount(page);
|
|
await page.locator(".pbr-asset-card", { hasText: "PBR Cube" }).first().dblclick();
|
|
await expect.poll(() => objectCount(page), { timeout: 30_000 }).toBeGreaterThan(before);
|
|
|
|
await page.locator(".pbr-asset-card", { hasText: "PBR Metal Brushed" }).first().dblclick();
|
|
await expect.poll(async () => page.locator('select[aria-label^="材质槽"]').first().inputValue(), { timeout: 30_000 })
|
|
.toBe("material:PBR Metal Brushed");
|
|
await expect.poll(async () => page.locator(".material-preview-status").innerText(), { timeout: 30_000 }).toContain("COMPILED");
|
|
|
|
await page.getByRole("button", { name: "Rendering", exact: true }).click();
|
|
await page.getByRole("button", { name: "渲染 PBR", exact: true }).click();
|
|
await expect.poll(async () => page.locator(".pbr-render-result img").count(), { timeout: 90_000 }).toBe(1);
|
|
await expect.poll(async () => page.locator(".pbr-render-result img").first().evaluate((image) =>
|
|
(image as HTMLImageElement).naturalWidth), { timeout: 30_000 }).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("seeded PBR asset catalog and previews survive a page reload", async ({ page }) => {
|
|
test.setTimeout(240_000);
|
|
await openShading(page);
|
|
|
|
await page.reload();
|
|
await expect(page.locator(".blender-app")).toBeVisible({ timeout: 60_000 });
|
|
const persisted = await page.evaluate(async () => {
|
|
const { StorageClient } = await import("/src/storage/StorageClient.ts");
|
|
const client = new StorageClient();
|
|
try {
|
|
const catalog = await client.getAssetCatalog();
|
|
const preview = catalog.manifest?.assets.find((asset) => asset.preview)?.preview;
|
|
const bytes = preview ? await client.readAssetCatalogPreview("asset-library", preview.sha256) : null;
|
|
return {
|
|
assets: catalog.manifest?.assets.length ?? 0,
|
|
revision: catalog.manifest?.revision ?? 0,
|
|
seedId: catalog.seed?.seedId ?? "",
|
|
previewBytes: bytes?.data.byteLength ?? 0,
|
|
};
|
|
}
|
|
finally {
|
|
client.terminate();
|
|
}
|
|
});
|
|
expect(persisted.assets).toBe(20);
|
|
expect(persisted.seedId).toBe("pbr-essentials-v1");
|
|
expect(persisted.previewBytes).toBeGreaterThan(0);
|
|
});
|