返回 DeepSeek-Reasonix
composer-wiring-smoke.mjs
根目录 / desktop / electron / scripts / composer-wiring-smoke.mjs
1 // Actual packaged renderer + matching Go host, with disposable data and no provider calls.
2 import assert from "node:assert/strict";
3 import {mkdtempSync,mkdirSync,writeFileSync} from "node:fs";
4 import {tmpdir} from "node:os";
5 import path from "node:path";
6 import {_electron as electron} from "playwright";
7 const executablePath=process.argv[2];
8 if(!executablePath)throw Error("usage: composer-wiring-smoke.mjs <packaged executable>");
9 const home=mkdtempSync(path.join(tmpdir(),"reasonix-composer-wiring-"));
10 const evidence=process.env.REASONIX_WIRING_EVIDENCE || "/tmp/reasonix-composer-wiring-evidence";
11 mkdirSync(evidence,{recursive:true});
12 const env={...process.env,REASONIX_HOME:home,REASONIX_STATE_HOME:home,REASONIX_CACHE_HOME:path.join(home,"cache")};
13 delete env.REASONIX_DEV;delete env.REASONIX_DESKTOP_SERVICE;
14 let shell;
15 const rpc=(page,command,...args)=>page.evaluate(({command,args})=>window.reasonixDesktop.invoke(command,args),{command,args});
16 async function launch(){
17 shell=await electron.launch({executablePath,env,timeout:60000});
18 const page=await shell.firstWindow();
19 await page.waitForURL("reasonix://app/index.html",{timeout:60000});
20 await page.locator(".app").waitFor({timeout:60000});
21 await page.addLocatorHandler(page.locator(".management-screen__back:visible"),async b=>b.click());
22 await page.locator(".sidebar__quick-action:visible").first().click({trial:true,timeout:60000});
23 return page;
24 }
25 try {
26 let page=await launch();
27 await page.locator(".sidebar__quick-action:visible").first().click();
28 const composer=page.locator("textarea.composer__input:not([aria-hidden=true])");
29 await composer.waitFor();
30 await page.waitForFunction(()=>document.querySelector("textarea.composer__input:not([aria-hidden=true])")?.disabled===false);
31 await composer.fill("Keep this image across restart 中文");
32 // Valid source used by the host fixtures; use the renderer's normal paste path.
33 const image="iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
34 await composer.evaluate((element,data)=>{
35 const file=new File([Uint8Array.from(atob(data),c=>c.charCodeAt(0))],"persisted.png",{type:"image/png"});
36 const transfer=new DataTransfer();transfer.items.add(file);
37 element.dispatchEvent(new ClipboardEvent("paste",{bubbles:true,cancelable:true,clipboardData:transfer}));
38 },image);
39 await page.locator(".composer-context__item").waitFor();
40 const active=(await rpc(page,"ListTabs")).find(tab=>tab.active);
41 assert.ok(active?.sessionId);
42 const ref={hostId:"local",sessionId:active.sessionId};
43 let saved;
44 const deadline=Date.now()+30000;
45 do {
46 saved=JSON.parse((await rpc(page,"GetSessionComposerState",ref)).contentJson);
47 if(saved.attachments?.[0]?.recoveryPath)break;
48 await new Promise(resolve=>setTimeout(resolve,100));
49 } while(Date.now()<deadline);
50 assert.ok(saved.attachments?.[0]?.recoveryPath,"wait for the host's acknowledged attachment source");
51 for(let i=0;i<3;i++)assert.deepEqual(JSON.parse((await rpc(page,"GetSessionComposerState",ref)).contentJson),saved,"acknowledged data remains visible to repeated RPC reads");
52 assert.equal(saved.attachments[0].draftId,undefined);
53 assert.equal(saved.attachments[0].previewUrl,undefined);
54 await shell.close();shell=undefined;
55 page=await launch();
56 await page.locator(".composer-context__item img").waitFor({timeout:30000});
57 await page.waitForFunction(()=>Array.from(document.querySelectorAll(".composer-context__item img")).some(img=>img.complete && img.naturalWidth>0));
58 assert.equal(await page.locator("textarea.composer__input:not([aria-hidden=true])").inputValue(),saved.text);
59 const current=(await rpc(page,"ListTabs")).find(tab=>tab.sessionId===ref.sessionId);
60 const captured=await rpc(page,"CaptureAttachmentTarget",{kind:"session",tabId:current.id,session:ref});
61 const data=await rpc(page,"AttachmentDataURLForTarget",captured.token,saved.attachments[0].recoveryPath);
62 const restored=await rpc(page,"StageImageForTarget",captured.token,"restart-image-check","persisted.png","image/png",data);
63 assert.ok(restored.draftId,"restored image must acquire a fresh runtime credential");
64 assert.match(await rpc(page,"ReadDraftImageForTarget",captured.token,restored.draftId),/^data:image\//);
65 await rpc(page,"ReleaseAttachmentTarget",captured.token);
66 await page.screenshot({path:path.join(evidence,"restored-image.png")});
67 await shell.close();shell=undefined;
68 writeFileSync(path.join(evidence,"results.json"),JSON.stringify({home,executablePath,passed:["actual manual New click","actual renderer image paste","durable source without transient credential","normal packaged exit and restart","restored text and image preview","fresh typed image credential after restart"]},null,2));
69 console.log(`PASS packaged composer wiring: ${evidence}`);
70 } catch(error) {
71 if(shell){const page=await shell.firstWindow();await page.screenshot({path:path.join(evidence,"failure.png")});writeFileSync(path.join(evidence,"failure.txt"),await page.locator("body").innerText());}
72 throw error;
73 } finally {if(shell)await shell.close();}
74
74 lines Plain Text