Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
35 lines
4.0 KiB
TypeScript
35 lines
4.0 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import crypto from 'node:crypto';
|
|
import type { WebEngineLODResult } from '../../protocol/web-engine';
|
|
const root = path.resolve(import.meta.dirname, '../../..');
|
|
const baseline = path.join(root, 'tests/golden/WBV2-P1424-001/lod-current-main-20261021');
|
|
const evidence = process.env.WBV2_EVIDENCE_DIR ?? baseline;
|
|
const candidate = process.env.WBV2_PRODUCTION_CANDIDATE ?? path.join(root, 'web/dist');
|
|
const hash = (file: string) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
test('concave reader uses native desktop triangulation', async ({ page, browser }) => {
|
|
const oracle = JSON.parse(fs.readFileSync(path.join(baseline, 'desktop-concave.json'), 'utf8'));
|
|
const fixture = path.join(root, oracle.fixture); expect(hash(fixture)).toBe(oracle.fixtureSha256);
|
|
const record = { started: Date.now(), chrome: browser.version(), viewport: page.viewportSize(), fixture: oracle.fixture, fixtureSha256: hash(fixture), failures: [] as string[], consoleErrors: [] as string[], modules: [] as { url: string; sha256: string }[], isolated: false, observed: null as unknown };
|
|
page.on('requestfailed', request => record.failures.push(`${request.url()}: ${request.failure()?.errorText}`));
|
|
page.on('request', request => { if (new URL(request.url()).pathname.startsWith('/src/')) record.failures.push(`NON_PRODUCTION_SOURCE ${request.url()}`); });
|
|
page.on('console', message => { if (message.type() === 'error') record.consoleErrors.push(message.text()); }); page.on('pageerror', error => record.consoleErrors.push(error.message));
|
|
page.on('response', response => { const url = new URL(response.url()).pathname; if (response.status() >= 400) record.failures.push(`${response.status()} ${url}`); if (/\.wasm$|wasm-module-manifest\.json$/.test(url)) record.modules.push({ url, sha256: hash(path.join(candidate, url)) }); });
|
|
let passed = false;
|
|
try {
|
|
await page.addInitScript(() => { const Native = window.Worker; window.Worker = class extends Native { constructor(url: string | URL, options?: WorkerOptions) { super(url, options); this.addEventListener('message', ({ data }) => { if (data.result?.lod) (window as unknown as { __concaveLOD: unknown }).__concaveLOD = data.result.lod; }); } }; });
|
|
await page.goto('/?ui=web'); record.isolated = await page.evaluate(() => crossOriginIsolated); expect(record.isolated).toBe(true);
|
|
await page.getByTestId('blend-file-input').setInputFiles(fixture);
|
|
await expect(page.getByText('ConcaveNotch', { exact: true })).toBeVisible({ timeout: 60_000 });
|
|
const before = await page.getByTestId('scene-stats').textContent(); await page.getByRole('button', { name: '生成 LOD' }).click();
|
|
await expect(page.getByTestId('engine-status')).toContainText('cache ready', { timeout: 90_000 });
|
|
const observed = await page.evaluate(() => { const lod = (window as unknown as { __concaveLOD: WebEngineLODResult }).__concaveLOD; return { revision: lod.manifest.sourceMeshRevision, unavailable: lod.errors?.length ?? 0, levels: lod.levels.map(level => ({ triangles: level.outputTriangleCount, positions: Array.from(new Float32Array(level.geometryBuffers[0].positions)), indices: Array.from(new Uint32Array(level.geometryBuffers[0].indices)) })) }; });
|
|
record.observed = observed; expect(observed.levels).toHaveLength(1);
|
|
expect(observed.levels[0]).toEqual({ triangles: oracle.triangles, positions: oracle.positions, indices: oracle.indices });
|
|
expect(observed.unavailable).toBe(2); await expect(page.getByTestId('engine-status')).toContainText('2 unavailable level(s)');
|
|
expect(await page.getByTestId('scene-stats').textContent()).toBe(before);
|
|
expect(record.failures).toEqual([]); expect(record.consoleErrors).toEqual([]); passed = true;
|
|
} finally { fs.writeFileSync(path.join(evidence, `${record.started}-concave-reader.json`), JSON.stringify({ ...record, passed, exitCode: passed ? 0 : 1, durationMs: Date.now() - record.started, testSourceSha256: hash(import.meta.filename) }, null, 2)); }
|
|
});
|