import assert from 'node:assert/strict'; import fs from 'node:fs'; import { checkCellQueue } from './batch-checks.mjs'; const pw = await import(process.env.PLAYWRIGHT || 'playwright'); const base = process.env.SHEETS_CANARY_ORIGIN; const state = process.env.SHEETS_CANARY_AUTH_STATE; if (base !== 'https://overnight-sheets.bds.oncarlos.com' || !state) throw new Error('Missing HTTPS canary origin or authentication state'); if ((fs.statSync(state).mode & 0o077) !== 0) throw new Error('Authentication state must be private'); const expected = process.env.SHEETS_CANARY_VERSION; if (!/^[a-f0-9]{7,40}$/.test(expected || '')) throw new Error('Expected candidate version is required'); const browser = await pw.chromium.launch({headless:true,executablePath:process.env.CHROMIUM_EXECUTABLE_PATH}); try { const context = await browser.newContext({storageState:state, permissions:['clipboard-read','clipboard-write']}); let signinPosted = false; await context.route('**/signin', route => { if (route.request().method() === 'POST') { signinPosted = true; return route.abort(); } return route.continue(); }); const version = await context.request.get(base+'/api/version'); assert.equal((await version.text()).trim(), expected); const page = await context.newPage(); const errors=[];page.on('pageerror',e=>errors.push(e.message)); await page.goto(base+'/sheets'); assert.equal(new URL(page.url()).pathname,'/sheets'); const location = await page.evaluate(async()=>{ const r=await fetch('/sheets',{method:'POST',body:new URLSearchParams({title:'Atomic batch canary '+Date.now()})}); if(!r.ok)throw new Error('Fixture creation failed');return r.url; }); await page.goto(location); await page.waitForFunction(()=>window.SheetsGrid?.worksheet()); const ids=await page.locator('[data-sheet-surface]').evaluate(el=>({sheet:el.dataset.sheetId,tab:el.dataset.tabId})); const api=`/api/sheets/${ids.sheet}/tabs/${ids.tab}/cells`; const second=await context.newPage();second.on('pageerror',e=>errors.push(e.message)); await second.goto(location);await second.waitForFunction(()=>window.SheetsGrid?.worksheet()); const requests=[];page.on('request',r=>{if(r.method()==='POST'&&r.url().includes('/cells'))requests.push(r.url());}); const text=Array.from({length:25},(_,r)=>Array.from({length:20},(_,c)=>String(r*20+c+1)).join('\t')).join('\n'); await page.bringToFront(); await page.evaluate(async text=>{await navigator.clipboard.writeText(text);},text); await page.locator('[data-sheet-grid] tbody tr:first-child td:nth-child(2)').click(); const start=Date.now(); await page.keyboard.press('Control+v'); await page.waitForFunction(()=>window.SheetsGrid.rawAt(24,19)==='500'); assert.equal(requests.length,1);assert.ok(requests[0].endsWith('/cells/batch')); const pasteMs=Date.now()-start; await second.bringToFront(); await second.waitForFunction(()=>window.SheetsGrid.rawAt(24,19)==='500'); await page.bringToFront(); await page.evaluate(()=>{window.SheetsGrid.worksheet().updateSelectionFromCoords(0,0,19,24);window.SheetsGrid.focus();}); await page.keyboard.press('Control+x'); assert.equal(await page.evaluate(()=>SheetsGrid.rawAt(0,0)), '1'); await page.keyboard.press('Escape'); const snap=await page.evaluate(async api=>{const r=await fetch(api);return r.json();},api); assert.equal(snap.cells.length,500); const before=snap.cells.map(c=>[c.r,c.c,c.raw]).sort(); const refused=await page.evaluate(async api=>{ const r=await fetch(api+'/batch',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({cells:[{r:0,c:0,raw:'99999'},{r:24,c:19,raw:'=SUM(('}]})});return r.status; },api); assert.equal(refused,400); const after=await page.evaluate(async api=>{const r=await fetch(api);return r.json();},api); assert.deepEqual(after.cells.map(c=>[c.r,c.c,c.raw]).sort(),before); await page.reload();await page.waitForFunction(()=>window.SheetsGrid?.worksheet()&&window.SheetsGrid.rawAt(24,19)==='500'); const nojs=await browser.newContext({storageState:state,javaScriptEnabled:false}); const table=await nojs.newPage();await table.goto(location+'/table'); assert.equal(await table.locator('a[aria-label="Edit T25"]').innerText(),'500'); assert.equal(await table.locator('a[aria-label="Edit A1"]').innerText(),'1'); const queue = await checkCellQueue(page); assert.equal(signinPosted, false); assert.deepEqual(errors,[]); const result={origin:base,version:expected,queue,sheet:ids.sheet,tab:ids.tab,pasteCells:500,pasteRequests:1,pasteMs,secondPageObserved:true,clearThenPasteVisible:true,invalidBatchRolledBack:true,reloadPreserved:true,scriptlessTableLoaded:true}; if(process.env.SHEETS_CANARY_RESULT)fs.writeFileSync(process.env.SHEETS_CANARY_RESULT,JSON.stringify(result,null,2)); console.log(JSON.stringify(result)); } finally {await browser.close();}