Spaces:
Running on Zero
Running on Zero
Download tests/test_examples.mjs from lsmpp/PixText-Studio: direct link, hf CLI and curl.
- Browser
- Download file 6.77 kB
-
https://huggingface.co/spaces/lsmpp/PixText-Studio/resolve/main/tests/test_examples.mjs
- Command line
-
hf download hf://spaces/lsmpp/PixText-Studio/tests/test_examples.mjs
-
curl -L -o test_examples.mjs https://huggingface.co/spaces/lsmpp/PixText-Studio/resolve/main/tests/test_examples.mjs
6.77 kB
| import assert from 'node:assert/strict'; | |
| import {readFileSync} from 'node:fs'; | |
| import test from 'node:test'; | |
| import vm from 'node:vm'; | |
| const script = readFileSync(new URL('../static/app.js', import.meta.url), 'utf8'); | |
| const presets = Object.fromEntries(['english', 'chinese'].map(name => [name, | |
| JSON.parse(readFileSync(new URL(`../examples/${name}.json`, import.meta.url), 'utf8'))])); | |
| // Run the real editor against a small DOM/fetch adapter; no models or browser packages needed. | |
| function editor() { | |
| const elements = new Map(), requests = [], decoded = []; | |
| let nextId = 0, failExample = null; | |
| const element = id => { | |
| if(!elements.has(id)) elements.set(id, {value:'', dataset:{}, style:{setProperty(){}}, | |
| classList:{toggle(){},add(){},remove(){}}, setAttribute(name,value){this.attributes??={};this.attributes[name]=value;}, removeAttribute(name){delete this.attributes?.[name];}, addEventListener(){}, | |
| getContext(){return {drawImage(){}};}, children:[], focus(){}}); | |
| return elements.get(id); | |
| }; | |
| const examples = ['english','chinese'].map(example => ({...element(example),dataset:{example}})); | |
| const models = ['meanflow','teacher'].map(model => ({...element(model),dataset:{model}})); | |
| const context = vm.createContext({structuredClone, URLSearchParams, console, setTimeout:(fn,ms)=>setTimeout(fn,ms).unref(), clearTimeout, | |
| document:{getElementById:element,addEventListener(){},querySelectorAll(selector){ | |
| if(selector==='[data-example]') return examples; | |
| if(selector==='[data-model]') return models; | |
| return []; | |
| }}, | |
| Image:class {async decode(){decoded.push(this.src);this.complete=true;}}, | |
| location:{pathname:'/',hash:''}, history:{replaceState(){}}, | |
| fetch:async(url,options) => { | |
| if(url==='/api/config') return {ok:true,json:async()=>({transport:'http'})}; | |
| assert.equal(url,'/api/session'); | |
| const {example}=JSON.parse(options.body);requests.push(example); | |
| if(example===failExample){failExample=null;throw new Error('Network unavailable');} | |
| return {ok:true,json:async()=>({id:`session-${++nextId}`,example,width:1024,height:1024, | |
| image:`/examples/${example}-preview.webp?v=release`, | |
| regions:presets[example].map((r,i)=>({...r,id:`example-${i}`,enabled:true,origin:'example'})), | |
| settings:{variant:'meanflow',steps:4,seed:42,guidance:2.6,text_threshold:.5}})}; | |
| }}); | |
| vm.runInContext(script,context); | |
| const run=code=>vm.runInContext(code,context); | |
| return {run,requests,decoded,element,failNext(example){failExample=example;}}; | |
| } | |
| async function ready(app) { | |
| await app.run("Promise.all([prepareExample('english'),prepareExample('chinese')])"); | |
| await new Promise(resolve=>setImmediate(resolve)); | |
| } | |
| test('switching and restoring example edits reuse prepared sessions and decoded images', async()=>{ | |
| const app=editor();await ready(app); | |
| assert.equal(app.requests.length,2); | |
| assert.equal(app.decoded.length,2); | |
| for(const example of ['chinese','english','chinese','english']) { | |
| await app.run(`loadExample('${example}')`); | |
| assert.equal(app.run('state.regions.length'),presets[example].length); | |
| assert.equal(app.element('guidance').value,2.6); | |
| assert.equal(app.element('refineButton').disabled,false); | |
| app.run("state.regions[0].text='edited'; state.regions[0].bbox[0]=0; state.regions[0].enabled=false;"); | |
| } | |
| await app.run("loadExample('english')"); | |
| assert.equal(app.run('state.regions[0].text'),presets.english[0].text); | |
| assert.equal(app.run('state.regions[0].bbox[0]'),presets.english[0].bbox[0]); | |
| assert.equal(app.run('state.regions[0].enabled'),true); | |
| assert.equal(app.requests.length,2); | |
| assert.equal(app.decoded.length,2); | |
| }); | |
| test('concurrent preparation shares one request and an expired session is renewed',async()=>{ | |
| const app=editor();await ready(app); | |
| app.run("exampleCache.get('chinese').expires=0"); | |
| await app.run("Promise.all([prepareExample('chinese'),prepareExample('chinese')])"); | |
| assert.equal(app.requests.filter(x=>x==='chinese').length,2); | |
| }); | |
| test('failed preparation is retryable without replacing the current editor',async()=>{ | |
| const app=editor();await ready(app); | |
| await app.run("loadExample('english')"); | |
| const current=app.run('state.session.id'); | |
| app.run("exampleCache.get('chinese').expires=0");app.failNext('chinese'); | |
| await app.run("loadExample('chinese')"); | |
| assert.equal(app.run('state.session.id'),current); | |
| assert.equal(app.run('state.busy'),false); | |
| await app.run("loadExample('chinese')"); | |
| assert.equal(app.run('state.regions.length'),13); | |
| }); | |
| test('detection immediately shows central activity and keeps unknown progress indeterminate',async()=>{ | |
| const app=editor();await ready(app); | |
| app.run("setBusy(true,'Preparing detection','detect'); updateActivity('Detecting characters and words',.3,true)"); | |
| assert.equal(app.element('jobProgress').hidden,false); | |
| assert.equal(app.element('jobTitle').textContent,'Detecting text'); | |
| assert.equal(app.element('jobPercent').hidden,true); | |
| assert.equal(app.element('progressTrack').attributes['aria-valuenow'],undefined); | |
| assert.equal(app.element('detectButton').attributes['aria-busy'],'true'); | |
| }); | |
| test('refinement displays reported progress, then leaves a nonblocking completion state',async()=>{ | |
| const app=editor();await ready(app); | |
| app.run("setBusy(true,'Waiting for GPU','refine');updateActivity('Refining · step 2 of 4',.55,false)"); | |
| assert.equal(app.element('jobTitle').textContent,'Refining text'); | |
| assert.equal(app.element('jobPercent').textContent,'55%'); | |
| assert.equal(app.element('jobPercent').hidden,false); | |
| assert.equal(app.element('progressTrack').attributes['aria-valuenow'],'55'); | |
| app.run("finishActivity('done','4 regions · 2.7s');setBusy(false)"); | |
| assert.equal(app.element('jobProgress').dataset.state,'done'); | |
| assert.equal(app.element('jobProgress').hidden,false); | |
| assert.equal(app.element('refineButton').attributes['aria-busy'],'false'); | |
| app.run("setBusy(true,'Preparing detection','detect')"); | |
| assert.equal(app.element('jobProgress').dataset.state,'running'); | |
| assert.equal(app.element('jobTitle').textContent,'Detecting text'); | |
| }); | |
| test('a job error remains visible after controls are released and can be dismissed',async()=>{ | |
| const app=editor();await ready(app); | |
| app.run("setBusy(true,'Waiting for GPU','refine');finishActivity('error','GPU quota exceeded');setBusy(false)"); | |
| assert.equal(app.element('jobProgress').hidden,false); | |
| assert.equal(app.element('jobProgress').dataset.state,'error'); | |
| assert.equal(app.element('jobMessage').textContent,'GPU quota exceeded'); | |
| assert.equal(app.element('closeProgress').hidden,false); | |
| app.run('dismissActivity()');assert.equal(app.element('jobProgress').hidden,true); | |
| }); | |