chore: checkpoint web Blender migration source, tools and docs
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>
This commit is contained in:
72
web/tests/e2e/console-dispatch-diagnostic.spec.ts
Normal file
72
web/tests/e2e/console-dispatch-diagnostic.spec.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
/* Temporary diagnostic: the console reports no dispatch, no echo and no error.
|
||||
* Count the real DOM nodes before and after switching to the Scripting workspace. */
|
||||
test('console dispatch diagnostic', async ({ page }, info) => {
|
||||
const record: Record<string, unknown> = {};
|
||||
const consoleErrors: string[] = [];
|
||||
record.consoleErrors = consoleErrors;
|
||||
page.on('console', message => { if (message.type() === 'error') consoleErrors.push(message.text().slice(0, 300)); });
|
||||
const probe = () => page.evaluate(() => ({
|
||||
consoleRegion: document.querySelectorAll('[aria-label="Python Console"]').length,
|
||||
wasmConsole: document.querySelectorAll('.wasm-python-console').length,
|
||||
pythonInput: document.querySelectorAll('[aria-label="Python 输入"]').length,
|
||||
promptMarks: (document.body.innerText.match(/>>>/g) ?? []).length,
|
||||
bodyLen: document.body.innerText.length,
|
||||
bodyTail: document.body.innerText.slice(-600),
|
||||
pressedDesktop: document.querySelector('[aria-label="工作区显示"] button')?.getAttribute('aria-pressed') ?? null,
|
||||
pressedWeb: document.querySelectorAll('[aria-label="工作区显示"] button')[1]?.getAttribute('aria-pressed') ?? null,
|
||||
workspaceNodes: document.querySelectorAll('[class*="workspace"]').length,
|
||||
sourceWorkspace: document.querySelectorAll('.wasm-source-workspace').length,
|
||||
editors: Array.from(document.querySelectorAll('[data-editor]')).map(n => n.getAttribute('data-editor')),
|
||||
dataWorkspace: document.querySelector('main')?.getAttribute('data-workspace') ?? null,
|
||||
areas: Array.from(document.querySelectorAll('[data-area-editor], .area')).slice(0,20).map(n => n.className),
|
||||
}));
|
||||
await page.goto('/');
|
||||
await page.getByTestId('blend-file-input').setInputFiles(path.resolve(import.meta.dirname, '../../../tests/files/web/basic_scene.blend'));
|
||||
await expect(page.getByTestId('engine-status')).toContainText(/SceneIR/, { timeout: 60_000 });
|
||||
record.afterLoad = await probe();
|
||||
const webTools = page.getByTestId('native-web-tools-toggle');
|
||||
record.webToolsCount = await webTools.count();
|
||||
await webTools.click({ force: true }).catch(() => {});
|
||||
await page.waitForTimeout(1500);
|
||||
record.afterToolsToggle = await probe();
|
||||
const scriptTab = page.getByRole('button', { name: 'Scripting', exact: true }).first();
|
||||
record.scriptingCount = await page.getByRole('button', { name: 'Scripting', exact: true }).count();
|
||||
record.scriptingDisabled = await scriptTab.isDisabled().catch(() => null);
|
||||
record.scriptingCurrentBefore = await scriptTab.getAttribute('aria-current').catch(() => null);
|
||||
record.statusBefore = await page.getByTestId('engine-status').textContent().catch(() => null);
|
||||
const timeline = [];
|
||||
const snap = async (label: string) => timeline.push({ label, ws: await page.locator('main').getAttribute('data-workspace'), st: await page.getByTestId('engine-status').textContent().catch(() => null) });
|
||||
record.domDispatch = await page.evaluate(() => {
|
||||
const btns = Array.from(document.querySelectorAll('nav[aria-label="工作区"] button'));
|
||||
const target = btns.find(b => (b.textContent ?? '').trim() === 'Scripting');
|
||||
if (!target) return { found: false, names: btns.map(b => (b.textContent ?? '').trim()) };
|
||||
const r = target.getBoundingClientRect();
|
||||
const top = document.elementFromPoint(r.x + r.width / 2, r.y + r.height / 2);
|
||||
const info = { found: true, disabled: (target as HTMLButtonElement).disabled, rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)],
|
||||
topTag: top?.tagName ?? null, topClass: (top as HTMLElement | null)?.className ?? null, topIsTarget: top === target };
|
||||
target.click();
|
||||
return info;
|
||||
});
|
||||
await snap('t+0'); await page.waitForTimeout(400); await snap('t+400');
|
||||
await page.waitForTimeout(800); await snap('t+1200');
|
||||
await page.waitForTimeout(1500); await snap('t+2700');
|
||||
record.timeline = timeline;
|
||||
record.scriptingCurrentAfter = await scriptTab.getAttribute('aria-current').catch(() => null);
|
||||
record.afterScripting = await probe();
|
||||
const input = page.getByLabel('Python 输入', { exact: true }).last();
|
||||
if (await input.count()) {
|
||||
await input.fill('print(1 + 1)');
|
||||
await input.press('Enter');
|
||||
await page.waitForTimeout(4000);
|
||||
record.valueAfterEnter = await input.inputValue();
|
||||
record.afterEnter = await probe();
|
||||
record.alerts = await page.locator('[role="alert"]').allTextContents();
|
||||
}
|
||||
record.engineStatus = await page.getByTestId('engine-status').textContent().catch(() => null);
|
||||
fs.writeFileSync(info.outputPath('console-diagnostic.json'), JSON.stringify(record, null, 1));
|
||||
console.log(JSON.stringify(record, null, 1));
|
||||
});
|
||||
Reference in New Issue
Block a user