57 lines
4.3 KiB
JavaScript
57 lines
4.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { DEFAULT_SETTINGS } from '../lib/defaults.js';
|
|
const { createDialogController } = await import('../dialog/controller.js').catch(()=>({}));
|
|
const { showActionDialog } = await import('../background/dialogs.js').catch(()=>({}));
|
|
const payload = source=>({request:{actionId:'translate',source,settings:{...DEFAULT_SETTINGS}},preparation:{kind:'language'}});
|
|
function fixture(data=payload('hello')) {
|
|
const opened=[];let closed=0;let state;
|
|
const controller=createDialogController({payload:data,open:async(url)=>opened.push(url),render:s=>{state=s;},close:()=>closed++});
|
|
return {controller,opened,get state(){return state;},get closed(){return closed;}};
|
|
}
|
|
test('dialog validates language, cancel never launches, source snapshots are independent',async()=>{
|
|
assert.equal(typeof createDialogController,'function');
|
|
const a=fixture(),b=fixture(payload('second'));
|
|
await a.controller.submit(' ');assert.equal(a.opened.length,0);assert.match(a.state.message,/language/i);
|
|
a.controller.cancel();await a.controller.submit('Spanish');assert.equal(a.opened.length,0);assert.equal(a.closed,1);
|
|
await b.controller.submit('French');assert.equal(b.opened.length,1);assert.ok(new URL(b.opened[0]).searchParams.get('q').includes('second'));assert.equal(b.closed,1);
|
|
});
|
|
test('long translation requires a separate explicit shortening confirmation',async()=>{
|
|
assert.equal(typeof createDialogController,'function');
|
|
const f=fixture(payload('x'.repeat(30000)));
|
|
await f.controller.submit('Spanish');assert.equal(f.opened.length,0);assert.equal(f.state.kind,'shortened');
|
|
await f.controller.submit();assert.equal(f.opened.length,1);assert.ok(f.opened[0].length<=8000);
|
|
});
|
|
test('pending launch blocks double submit and failure permits retry',async()=>{
|
|
assert.equal(typeof createDialogController,'function');
|
|
let count=0,release,state,closed=0;
|
|
const controller=createDialogController({payload:payload('hello'),render:s=>state=s,close:()=>closed++,open:()=>{count++;return new Promise((resolve,reject)=>release={resolve,reject});}});
|
|
const pending=controller.submit('Spanish');await controller.submit('Spanish');assert.equal(count,1);
|
|
release.reject(Error('fail'));await pending;assert.equal(closed,0);assert.match(state.message,/try again/i);
|
|
const retry=controller.submit();release.resolve();await retry;assert.equal(count,2);assert.equal(closed,1);
|
|
});
|
|
function event() { const listeners=new Set();return {addListener:f=>listeners.add(f),removeListener:f=>listeners.delete(f),emit:(...args)=>{for(const f of [...listeners])f(...args);},get size(){return listeners.size;}}; }
|
|
function browserFixture() {
|
|
let id=0;const tabs=[];
|
|
return {tabs,runtime:{id:'extension',getURL:p=>'moz-extension://test/'+p,onConnect:event()},windows:{create:async({url})=>{const tab={id:++id};tabs.push({url,...tab});return {tabs:[tab]};}},tabsApi:{onRemoved:event()}};
|
|
}
|
|
function connect(b,tab,index=0) {
|
|
const messages=[];
|
|
const port={name:'action-dialog:'+new URL(tab.url).searchParams.get('request'),sender:{id:'extension',url:tab.url,tab:{id:tab.id+index}},onMessage:event(),onDisconnect:event(),postMessage:v=>messages.push(v),disconnect(){this.onDisconnect.emit();}};
|
|
b.runtime.onConnect.emit(port);return {port,messages};
|
|
}
|
|
test('handoff scopes data to matching dialog tab and releases background after acknowledgment',async()=>{
|
|
assert.equal(typeof showActionDialog,'function');
|
|
const b=browserFixture();b.tabs=b.tabsApi;
|
|
// Capture opened tabs outside the browser API object.
|
|
const opened=[];let id=0;b.windows.create=async({url})=>{const tab={id:++id,url};opened.push(tab);return {tabs:[tab]};};
|
|
const first=showActionDialog(b,payload('first')),second=showActionDialog(b,payload('second'));
|
|
await new Promise(r=>setImmediate(r));
|
|
const bad=connect(b,opened[0],100);await new Promise(r=>setImmediate(r));assert.equal(bad.messages.length,0);
|
|
const a=connect(b,opened[0]),c=connect(b,opened[1]);await new Promise(r=>setImmediate(r));
|
|
assert.equal(a.messages[0].payload.request.source,'first');assert.equal(c.messages[0].payload.request.source,'second');
|
|
a.port.onMessage.emit({type:'received'});c.port.onMessage.emit({type:'received'});await Promise.all([first,second]);
|
|
assert.equal(b.runtime.onConnect.size,0);assert.equal(b.tabs.onRemoved.size,0);
|
|
assert.equal(a.messages[0].payload.request.source,'first');
|
|
});
|