MiniCPM5-2B-WebGPU-Pi-HTTP / app /tests /local-folder-agent-live.mjs
Mike0021's picture
Guard concurrent local previews and publish folder validation
da0d085 verified
Raw History Blame Contribute Delete
4.64 kB
import { chromium } from 'playwright';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
const root = new URL('../..', import.meta.url).pathname;
const url = process.argv[2] || 'https://mike0021-minicpm5-2b-webgpu-pi-http.static.hf.space/';
const result = { date: new Date().toISOString(), url, errors: [], prompt: 'Read app.js and change the exported greeting from Hello to Bonjour. Leave other files unchanged and verify the change.', handle: 'Real OPFS directory handle passed through the native worker protocol; model and tools are not mocked.' };
const context = await chromium.launchPersistentContext(root + '/validation/http-browser-profile', {
executablePath: '/usr/bin/chromium', headless: true, viewport: { width: 1440, height: 1000 },
args: ['--enable-unsafe-webgpu', '--use-angle=vulkan', '--enable-features=Vulkan', '--disable-vulkan-surface'],
});
const page = await context.newPage(); page.setDefaultTimeout(120_000);
page.on('pageerror', e => result.errors.push(e.message));
const idle = () => page.waitForFunction(() => window.browserPi && !window.browserPi.busy);
let timer;
try {
await page.goto(url); await page.waitForFunction(() => window.browserPi); await page.evaluate(() => window.browserPi.ready); await idle();
await page.evaluate(async () => {
const root = await navigator.storage.getDirectory();
try { await root.removeEntry('local-agent-check', { recursive: true }); } catch (e) { if (e.name !== 'NotFoundError') throw e; }
window.testFolder = await root.getDirectoryHandle('local-agent-check', { create: true });
for (const [name, content] of [['README.md', '# Example JavaScript project\n'], ['app.js', 'export const greeting = "Hello";\n']]) {
const stream = await (await window.testFolder.getFileHandle(name, { create: true })).createWritable(); await stream.write(content); await stream.close();
}
window.showDirectoryPicker = async () => window.testFolder;
});
await page.locator('#open-folder').click(); await page.locator('#local-folder-bar').waitFor({ state: 'visible' }); await idle(); await page.waitForTimeout(300);
const startLoad = Date.now(); await page.evaluate(() => window.browserPi.load()); result.loadMs = Date.now() - startLoad; await idle(); await page.waitForTimeout(300);
const started = Date.now();
const pending = page.evaluate(prompt => window.browserPi.prompt(prompt), result.prompt);
try { await Promise.race([pending, new Promise((_, reject) => { timer = setTimeout(() => reject(Error('Model trial exceeded 120 seconds')), 120000); })]); }
catch (error) { await page.evaluate(() => window.browserPi.rpc('stop')); await pending.catch(() => {}); throw error; }
finally { clearTimeout(timer); }
result.elapsedMs = Date.now() - started; await idle();
const state = await page.evaluate(() => window.browserPi.rpc('snapshot')); result.messages = state.messages;
result.patches = await page.evaluate(() => window.browserPi.rpc('local_changes'));
assert.equal(result.patches.length, 1); assert.equal(result.patches[0].path, '/app.js');
assert.match(Buffer.from(result.patches[0].after, 'base64').toString(), /greeting\s*=\s*["']Bonjour["']/);
result.diskBeforeApply = await page.evaluate(async () => (await (await window.testFolder.getFileHandle('app.js')).getFile()).text());
assert.equal(result.diskBeforeApply, 'export const greeting = "Hello";\n');
await page.locator('#review-changes').click(); await page.locator('#local-review').waitFor({ state: 'visible' });
await page.screenshot({ path: root + '/validation/local-folder-agent-review.png' });
await page.locator('#review-apply').click(); await page.locator('#local-review').waitFor({ state: 'hidden' }); await idle();
result.diskAfterApply = await page.evaluate(async () => (await (await window.testFolder.getFileHandle('app.js')).getFile()).text());
assert.equal(result.diskAfterApply, Buffer.from(result.patches[0].after, 'base64').toString());
result.final = state.messages.filter(m => m.role === 'assistant').at(-1)?.content.filter(c => c.type === 'text').map(c => c.text).join('\n');
assert.deepEqual(result.errors, []); result.passed = true;
await page.locator('#disconnect-folder').click(); await idle();
console.log(JSON.stringify({ passed: true, loadMs: result.loadMs, elapsedMs: result.elapsedMs, final: result.final, changes: result.patches.length }));
} catch (error) { result.failure = error.stack; console.error(error); process.exitCode = 1; }
finally { clearTimeout(timer); await fs.writeFile(root + '/validation/local-folder-agent-live.json', JSON.stringify(result, null, 2)); await context.close(); }