Download app/tests/first-message.mjs from Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP: direct link, hf CLI and curl.
- Browser
- Download file 8.2 kB
-
https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/resolve/main/app/tests/first-message.mjs
- Command line
-
hf download hf://spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/app/tests/first-message.mjs
-
curl -L -o first-message.mjs https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi-HTTP/resolve/main/app/tests/first-message.mjs
8.2 kB
| import { chromium } from 'playwright'; | |
| import fs from 'node:fs/promises'; | |
| import assert from 'node:assert/strict'; | |
| const url = process.argv[2] ?? 'http://127.0.0.1:4173/?source=local'; | |
| const label = process.argv[3] ?? 'agent-first-message'; | |
| const result = { date: new Date().toISOString(), url, checks: [], errors: [] }; | |
| const gpuArgs = ['--enable-unsafe-webgpu', '--use-angle=vulkan', '--enable-features=Vulkan', '--disable-vulkan-surface']; | |
| let context, browser, page; | |
| async function open() { | |
| page = await context.newPage(); page.on('pageerror', e => result.errors.push(e.stack)); | |
| context.setDefaultTimeout(180_000); | |
| await page.goto(url); await page.waitForFunction(() => window.browserPi?.terminal); | |
| await page.evaluate(() => window.browserPi.ready); | |
| await page.evaluate(() => document.querySelector('#new-chat').onclick()); | |
| } | |
| async function send(text, expectModal = true) { | |
| await page.locator('.xterm-helper-textarea').focus(); | |
| if (text) await page.keyboard.type(text); | |
| await page.locator('#send').click(); | |
| if (expectModal) await page.waitForFunction(() => document.querySelector('#model-dialog').open); | |
| else await page.waitForFunction(() => window.browserPi.busy || window.browserPi.loaded); | |
| } | |
| async function draft(text) { | |
| await page.waitForFunction(text => !document.querySelector('#model-dialog').open && window.browserPi.terminal.text === text, text); | |
| } | |
| try { | |
| // Real unsupported-GPU path: no fake model, worker, progress or failure events. | |
| browser = await chromium.launch({ executablePath: '/usr/bin/chromium', headless: true, args: ['--disable-gpu', '--disable-software-rasterizer'] }); | |
| context = await browser.newContext({ viewport: { width: 1440, height: 1000 }, colorScheme: 'light' }); | |
| const requests = []; context.on('request', r => requests.push(r.url())); | |
| await open(); | |
| const prompt = 'Create a file called hello.txt containing Hello.'; | |
| await send(prompt); | |
| assert.equal(await page.locator('#model-preview').textContent(), prompt); | |
| assert(await page.locator('#model-confirm').evaluate(el => el === document.activeElement)); | |
| assert.equal(await page.evaluate(() => window.browserPi.loaded), false); | |
| assert.equal(requests.filter(u => /\.onnx|manifest\.json|tokenizer/.test(u)).length, 0); | |
| result.checks.push('First Send opens an accessible modal; no model download before confirmation'); | |
| await page.locator('#model-cancel').focus(); | |
| await page.keyboard.press('Tab'); assert(await page.locator('.setup-model .model-org').evaluate(el => el === document.activeElement)); | |
| await page.keyboard.press('Shift+Tab'); assert(await page.locator('#model-cancel').evaluate(el => el === document.activeElement)); | |
| await page.locator('#model-confirm').focus(); | |
| for (const [width,height] of [[1440,1000],[850,640],[390,844],[320,568],[390,420]]) { | |
| await page.setViewportSize({width,height}); | |
| const layout = await page.evaluate(() => { | |
| const rect=document.querySelector('#model-dialog').getBoundingClientRect(); | |
| const actions=document.querySelector('.setup-actions').getBoundingClientRect(); | |
| const selectors=['html','header','.model-panel','.workspace','.editor-area','.workspace > .terminal','#model-dialog','#model-preview']; | |
| return { inBounds:rect.x>=0 && rect.y>=0 && rect.right<=innerWidth && rect.bottom<=innerHeight && actions.top>=rect.top && actions.bottom<=rect.bottom, | |
| overflow:document.documentElement.scrollWidth>innerWidth, | |
| dark:getComputedStyle(document.documentElement).colorScheme==='dark', | |
| surfaces:selectors.map(s=>({selector:s,bg:getComputedStyle(document.querySelector(s)).backgroundColor})) }; | |
| }); | |
| assert(layout.inBounds && !layout.overflow && layout.dark); | |
| for(const surface of layout.surfaces) assert(surface.bg.match(/\d+/g).slice(0,3).every(n=>Number(n)<80),JSON.stringify(surface)); | |
| if([1440,390,320].includes(width)) await page.screenshot({path:`../validation/${label}-modal-${width}x${height}.png`,fullPage:true}); | |
| } | |
| result.checks.push('Focus stays in the modal; entire app remains dark under a light OS preference; five viewport sizes fit'); | |
| await page.keyboard.press('Escape'); await draft(prompt); | |
| assert.equal(await page.locator('#notice').textContent(),''); | |
| await send(); await page.locator('#model-confirm').click(); | |
| await page.waitForFunction(()=>!document.querySelector('#model-error').hidden); | |
| assert.match(await page.locator('#model-error').textContent(),/WebGPU/); | |
| assert.equal(await page.locator('#model-confirm').textContent(),'Try again'); | |
| await page.locator('#model-confirm').click(); | |
| await page.waitForFunction(()=>!document.querySelector('#model-error').hidden && !document.querySelector('#model-confirm').disabled); | |
| await page.screenshot({path:`../validation/${label}-error.png`,fullPage:true}); | |
| await page.locator('#model-cancel').click(); await draft(prompt); | |
| assert.equal((await page.evaluate(()=>window.browserPi.rpc('snapshot'))).messages.length,0); | |
| result.checks.push('Escape and Cancel restore the draft; real unsupported-GPU error supports retry without sending'); | |
| await context.close(); await browser.close(); context=undefined; browser=undefined; | |
| context = await chromium.launchPersistentContext(new URL('../../validation/agent-browser-profile',import.meta.url).pathname, | |
| { executablePath:'/usr/bin/chromium', headless:true, viewport:{width:1440,height:1000}, args:gpuArgs }); | |
| await open(); | |
| const nonce='FIRST-'+crypto.randomUUID().slice(0,8); | |
| await page.evaluate(nonce=>window.browserPi.rpc('write',{path:'/workspace/first-message.txt',content:nonce}),nonce); | |
| const task='Read first-message.txt using the read tool and tell me its exact contents.'; | |
| const cached = await page.evaluate(()=>window.browserPi.rpc('cache_status')); | |
| await send(task, !cached); | |
| if (cached) await page.locator('#stop').click(); | |
| else { | |
| await page.locator('#model-confirm').click(); | |
| await page.waitForFunction(()=>window.browserPi.busy && !document.querySelector('#model-dialog').open && !document.querySelector('#progress').hidden); | |
| await page.locator('#stop').click(); | |
| } | |
| await draft(task); | |
| await page.waitForFunction(()=>!window.browserPi.busy); | |
| assert.equal((await page.evaluate(()=>window.browserPi.rpc('snapshot'))).messages.length,0); | |
| result.checks.push('Cancelling real model setup waits for cleanup and restores the unsent message'); | |
| const start=Date.now(); | |
| const cachedOnRetry = await page.evaluate(()=>window.browserPi.rpc('cache_status')); | |
| await send(undefined, !cachedOnRetry); | |
| if (!cachedOnRetry) await page.locator('#model-confirm').click(); | |
| await page.waitForFunction(()=>window.browserPi.loaded && window.browserPi.diagnostics.events.includes('agent_end') && !window.browserPi.busy); | |
| result.elapsedMs=Date.now()-start; | |
| const state=await page.evaluate(()=>window.browserPi.rpc('snapshot')); | |
| assert.equal(state.messages.filter(m=>m.role==='user').length,1); | |
| assert.equal(state.messages.find(m=>m.role==='user').content[0].text,task); | |
| assert(state.messages.some(m=>m.role==='toolResult' && m.toolName==='read' && !m.isError)); | |
| assert(state.messages.filter(m=>m.role==='assistant').at(-1).content.some(c=>c.text?.includes(nonce))); | |
| assert.equal(await page.evaluate(()=>window.browserPi.terminal.text),''); | |
| assert.equal(await page.locator('#model-dialog').evaluate(el=>el.open),false); | |
| result.messages=state.messages; | |
| result.checks.push('Cached auto-start or download confirmation loads the actual model, sends exactly once and answers with an unseen file value'); | |
| await page.screenshot({path:`../validation/${label}-desktop.png`,fullPage:true}); | |
| await page.setViewportSize({width:390,height:844}); | |
| await page.screenshot({path:`../validation/${label}-mobile.png`,fullPage:true}); | |
| assert.equal(result.errors.length,0); result.passed=true; | |
| console.log(JSON.stringify({passed:true,checks:result.checks,elapsedMs:result.elapsedMs})); | |
| } catch(error) { result.failure=error.stack; await page?.screenshot({path:`../validation/${label}-failure.png`,fullPage:true}); throw error; } | |
| finally { await fs.writeFile(`../validation/${label}.json`,JSON.stringify(result,null,2)); await context?.close(); await browser?.close(); } | |