oracle7-chameleon / scripts /accept_saved_behavior_ui.mjs
psiloceyeben's picture
Upload folder using huggingface_hub
09aeaeb verified
Raw History Blame Contribute Delete
6.07 kB
/** Real public-source/local private-counterpart acceptance, not a unit fixture. */
import {chromium,expect} from '@playwright/test';
import {readFile,writeFile,mkdir} from 'node:fs/promises';
import {execFile} from 'node:child_process';
import {promisify} from 'node:util';
import path from 'node:path';
import {createHash} from 'node:crypto';
const run=promisify(execFile),root=process.cwd();
const output=path.join(root,'.local/repository-pilot','ui-acceptance-'+Date.now());
await mkdir(output);
const frozen=JSON.parse(await readFile('.local/repository-pilot/calculator/frozen.json','utf8'));
const source=frozen.repository.source+'/tree/'+frozen.repository.commit_sha;
const contract=JSON.parse(await readFile('docs/contracts/calculator-repair-suite.json','utf8'))[0];
await writeFile(path.join(output,'contract.json'),JSON.stringify(contract));
const browser=await chromium.launch();
let page;
try{
const context=await browser.newContext();page=await context.newPage();
page.setDefaultTimeout(15000);
const errors=[];page.on('pageerror',e=>errors.push(e.message));
await page.goto('http://127.0.0.1:8120/');
await expect(page.getByText('YOUR PRIVATE GUEST SESSION')).toBeVisible();
const auth=await context.request.post('http://127.0.0.1:8120/api/v1/auth/guest',{headers:{Origin:'http://127.0.0.1:8120'}});
const owner=(await auth.json()).owner_id;
const csrf=(await context.cookies()).find(c=>c.name==='chameleon_csrf').value;
const response=await context.request.post('http://127.0.0.1:8120/api/v1/adaptations',{headers:{Origin:'http://127.0.0.1:8120','X-CSRF-Token':csrf,'Idempotency-Key':crypto.randomUUID()},data:{source,source_type:'repository',build_counterpart:true}});
if(response.status()!==202)throw Error('Intake rejected: '+await response.text());
let job=await response.json();
const deadline=Date.now()+60000;
while(job.counterpart?.status!=='READY'&&Date.now()<deadline){
if(['REJECTED','FAILED','RESIDUAL'].includes(job.status))throw Error('Capture failed: '+JSON.stringify(job));
await new Promise(resolve=>setTimeout(resolve,500));
job=await (await context.request.get(`http://127.0.0.1:8120/api/v1/adaptations/${job.id}`)).json();
}
if(job.counterpart?.status!=='READY')throw Error('Capture timeout');
const twin=job.counterpart.twin_id;
const base=['scripts/verify_saved_repository.py','--owner',owner,'--twin',twin,'--contract',path.join(output,'contract.json')];
const original=await run('.venv/Scripts/python.exe',[...base,'--output',path.join(output,'original')],{cwd:root,timeout:90000,windowsHide:true});
await run('node',['scripts/build_calculator_overlay.mjs',path.join(output,'original/package.json'),path.join(output,'overlay.json')],{cwd:root,timeout:10000,windowsHide:true});
const repaired=await run('.venv/Scripts/python.exe',[...base,'--output',path.join(output,'repaired'),'--overlay',path.join(output,'overlay.json')],{cwd:root,timeout:90000,windowsHide:true});
await page.reload();
// API-created jobs do not update the browser's remembered conversation.
// Reopen through the actual saved-conversation control, as a returning user does.
await page.locator('.saved-menu summary').click();
await page.locator('.saved-conversations button').filter({hasText:source.replace(/^https?:\/\//,'')}).click();
const saved=await (await context.request.get('http://127.0.0.1:8120/api/v1/twins')).json();
const artifact=saved.find(item=>item.id===twin);
if(!artifact)throw Error('Saved counterpart missing from owner list');
await page.getByRole('button',{name:artifact.source+' / open source package',exact:true}).click();
const panel=page.getByRole('region',{name:'Recorded behavior checks'});
await expect(panel.getByText('Contract passed',{exact:true})).toBeVisible();
await expect(panel.getByText('Contract failed',{exact:true})).toBeVisible();
await expect(panel.getByText('Repaired copy',{exact:true})).toBeVisible();
await expect(panel.getByText('Original source',{exact:true})).toBeVisible();
const downloadEvent=page.waitForEvent('download');
await panel.getByRole('link',{name:'Download tested counterpart ZIP'}).click();
const download=await downloadEvent;
await download.saveAs(path.join(output,'downloaded-counterpart.zip'));
const downloadHash=createHash('sha256').update(await readFile(path.join(output,'downloaded-counterpart.zip'))).digest('hex');
expect(downloadHash).toBe(JSON.parse(repaired.stdout).counterpart.sha256);
await panel.getByRole('button',{name:'Open repaired counterpart',exact:true}).click();
const app=page.frameLocator('iframe[title="Your isolated repaired counterpart"]');
for(const key of ['2','+','3','='])await app.getByText(key,{exact:true}).click();
await expect(app.locator('#display')).toHaveText('5');
const isolated=await app.locator('body').evaluate(async()=>{
let parentDenied=false,fetchDenied=false;
try{void parent.document.body}catch{parentDenied=true}
try{await fetch('/api/v1/session')}catch{fetchDenied=true}
return {parentDenied,fetchDenied};
});
expect(isolated).toEqual({parentDenied:true,fetchDenied:true});
await page.screenshot({path:path.join(output,'desktop.png'),fullPage:true});
await page.setViewportSize({width:390,height:844});
await page.screenshot({path:path.join(output,'mobile.png'),fullPage:true});
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBe(true);
expect(errors).toEqual([]);
const report={status:'PASS',source,job:job.id,twin,original:JSON.parse(original.stdout),repaired:JSON.parse(repaired.stdout),download_sha256:downloadHash,interactive:{addition:'PASS',...isolated},source_writes:false,public_deployment:false};
await writeFile(path.join(output,'report.json'),JSON.stringify(report,null,2));
console.log(JSON.stringify({status:'PASS',output}));
}catch(error){
await writeFile(path.join(output,'failure.json'),JSON.stringify({status:'FAIL',message:String(error),public_deployment:false},null,2));
if(page)await page.screenshot({path:path.join(output,'failure.png'),fullPage:true}).catch(()=>{});
throw error;
}finally{await browser.close();}