60 lines
5.7 KiB
JavaScript
60 lines
5.7 KiB
JavaScript
const {chromium} = require('playwright');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const {pathToFileURL} = require('node:url');
|
|
(async () => {
|
|
const out = process.env.EVIDENCE_DIR || '/tmp/context-curator-evidence'; fs.mkdirSync(out,{recursive:true});
|
|
const url = process.env.PAGE_URL || pathToFileURL(path.resolve(__dirname,'../site/index.html')).href;
|
|
const browser = await chromium.launch({executablePath:process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', args:['--no-sandbox']});
|
|
const errors = []; const external = [];
|
|
try {
|
|
for (const [name,width,height] of [['desktop',1280,900],['phone',390,844]]) {
|
|
const context = await browser.newContext({viewport:{width,height},acceptDownloads:true});
|
|
const page = await context.newPage();
|
|
page.on('pageerror',e=>errors.push(e.message));
|
|
page.on('request',r=>{if (/^https?:/.test(r.url()) && !r.url().startsWith(new URL(url).origin)) external.push(r.url());});
|
|
await page.goto(url); await page.locator('#diff').waitFor(); await page.evaluate(()=>document.fonts.ready);
|
|
assert.equal(await page.evaluate(()=>document.fonts.check('16px "Curator Serif"')),true);
|
|
assert.match(await page.locator('#measure').innerText(),/55.0% smaller/);
|
|
assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true);
|
|
await page.screenshot({path:path.join(out,`${name}.png`),fullPage:true});
|
|
await page.locator('#undo').click(); assert.equal(await page.locator('#packet').isDisabled(),true);
|
|
await page.locator('#apply').click();
|
|
const [download] = await Promise.all([page.waitForEvent('download'),page.locator('#packet').click()]);
|
|
await download.saveAs(path.join(out,`${name}-continuation.md`));
|
|
const packet = fs.readFileSync(path.join(out,`${name}-continuation.md`),'utf8');
|
|
assert.ok(packet.includes('Manual conclusion')); assert.ok(packet.includes('Now implement invalidation'));
|
|
await page.locator('#conclusion').fill('Revised finding.'); assert.equal(await page.locator('#packet').isDisabled(),true);
|
|
await page.locator('#first').selectOption('7'); await page.locator('#last').selectOption('2'); await page.locator('#apply').click();
|
|
assert.match(await page.locator('#status').innerText(),/valid contiguous span/);
|
|
await page.locator('#sample').click();
|
|
const before = await page.locator('#before').textContent();
|
|
await page.locator('summary').first().click(); await page.locator('#source').fill('{broken'); await page.locator('#load').click();
|
|
assert.match(await page.locator('#status').innerText(),/Invalid JSON/); assert.equal(await page.locator('#before').textContent(),before);
|
|
const hostile = JSON.stringify({version:1,messages:[{id:'a',role:'user',content:'<img src=x onerror="window.bad=1">```'},{id:'b',role:'tool',content:'Keep this exactly',metadata:{nested:[1,2]}}]});
|
|
await page.locator('#file').setInputFiles({name:'transcript.json',mimeType:'application/json',buffer:Buffer.from(hostile)});
|
|
await page.waitForFunction(()=>document.querySelector('#before').textContent.includes('Keep this exactly'));
|
|
assert.equal(await page.evaluate(()=>window.bad),undefined); assert.equal(await page.locator('#diff img').count(),0);
|
|
await page.locator('#first').selectOption('0'); await page.locator('#last').selectOption('0'); await page.locator('#conclusion').fill('Found it.'); await page.locator('#apply').click();
|
|
const [originalDownload] = await Promise.all([page.waitForEvent('download'),page.locator('#original').click()]);
|
|
await originalDownload.saveAs(path.join(out,`${name}-original.json`)); assert.equal(fs.readFileSync(path.join(out,`${name}-original.json`),'utf8'),hostile);
|
|
const form = await page.locator('#join-form').evaluate(f=>({method:f.method,action:f.getAttribute('action'),enctype:f.enctype,fields:Object.fromEntries(new FormData(f))}));
|
|
assert.equal(form.method,'post'); assert.equal(form.action,'/api/signup'); assert.equal(form.enctype,'application/x-www-form-urlencoded');
|
|
assert.equal(form.fields.venture,'context-curator'); assert.equal(form.fields.website,''); assert.equal(form.fields.plan,'free');
|
|
if (/^https?:/.test(url)) {
|
|
const [offline] = await Promise.all([page.waitForEvent('download'),page.locator('a[download="context-curator.html"]').click()]);
|
|
const offlinePath = path.join(out,`${name}-editor.html`); await offline.saveAs(offlinePath);
|
|
const offlinePage = await context.newPage(); await offlinePage.goto(pathToFileURL(offlinePath).href);
|
|
assert.match(await offlinePage.locator('#measure').innerText(),/55.0% smaller/);
|
|
await offlinePage.close();
|
|
}
|
|
await page.goto(url+'?joined=1#join'); assert.equal(await page.locator('#thanks').isVisible(),true); assert.equal(await page.locator('#join-form').isVisible(),false);
|
|
await context.close();
|
|
}
|
|
assert.deepEqual(errors,[]); assert.deepEqual(external,[]);
|
|
fs.writeFileSync(path.join(out,'receipt.json'),JSON.stringify({browser:await browser.version(),platform:process.platform,url,htmlSha256:require('node:crypto').createHash('sha256').update(fs.readFileSync(path.resolve(__dirname,'../site/index.html'))).digest('hex'),viewports:['1280x900','390x844'],checks:['no horizontal overflow','sample measurement','undo','Markdown download','exact original download','invalid range','invalid import recovery','file import','literal hostile content','form fields','thank-you state','no external requests','no JavaScript errors']},null,2)+'\n');
|
|
console.log('PASS: desktop + phone; screenshots and receipt in '+out);
|
|
} finally {await browser.close();}
|
|
})().catch(e=>{console.error(e);process.exitCode=1;});
|