oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1import assert from 'node:assert/strict';
2import fs from 'node:fs';
3import { checkCellQueue } from './batch-checks.mjs';
4const pw = await import(process.env.PLAYWRIGHT || 'playwright');
5const base = process.env.SHEETS_CANARY_ORIGIN;
6const state = process.env.SHEETS_CANARY_AUTH_STATE;
7if (base !== 'https://overnight-sheets.bds.oncarlos.com' || !state) throw new Error('Missing HTTPS canary origin or authentication state');
8if ((fs.statSync(state).mode & 0o077) !== 0) throw new Error('Authentication state must be private');
9const expected = process.env.SHEETS_CANARY_VERSION;
10if (!/^[a-f0-9]{7,40}$/.test(expected || '')) throw new Error('Expected candidate version is required');
11const browser = await pw.chromium.launch({headless:true,executablePath:process.env.CHROMIUM_EXECUTABLE_PATH});
12try {
13 const context = await browser.newContext({storageState:state, permissions:['clipboard-read','clipboard-write']});
14 let signinPosted = false;
15 await context.route('**/signin', route => {
16 if (route.request().method() === 'POST') { signinPosted = true; return route.abort(); }
17 return route.continue();
18 });
19 const version = await context.request.get(base+'/api/version');
20 assert.equal((await version.text()).trim(), expected);
21 const page = await context.newPage();
22 const errors=[];page.on('pageerror',e=>errors.push(e.message));
23 await page.goto(base+'/sheets');
24 assert.equal(new URL(page.url()).pathname,'/sheets');
25 const location = await page.evaluate(async()=>{
26 const r=await fetch('/sheets',{method:'POST',body:new URLSearchParams({title:'Atomic batch canary '+Date.now()})});
27 if(!r.ok)throw new Error('Fixture creation failed');return r.url;
28 });
29 await page.goto(location);
30 await page.waitForFunction(()=>window.SheetsGrid?.worksheet());
31 const ids=await page.locator('[data-sheet-surface]').evaluate(el=>({sheet:el.dataset.sheetId,tab:el.dataset.tabId}));
32 const api=`/api/sheets/${ids.sheet}/tabs/${ids.tab}/cells`;
33 const second=await context.newPage();second.on('pageerror',e=>errors.push(e.message));
34 await second.goto(location);await second.waitForFunction(()=>window.SheetsGrid?.worksheet());
35 const requests=[];page.on('request',r=>{if(r.method()==='POST'&&r.url().includes('/cells'))requests.push(r.url());});
36 const text=Array.from({length:25},(_,r)=>Array.from({length:20},(_,c)=>String(r*20+c+1)).join('\t')).join('\n');
37 await page.bringToFront();
38 await page.evaluate(async text=>{await navigator.clipboard.writeText(text);},text);
39 await page.locator('[data-sheet-grid] tbody tr:first-child td:nth-child(2)').click();
40 const start=Date.now();
41 await page.keyboard.press('Control+v');
42 await page.waitForFunction(()=>window.SheetsGrid.rawAt(24,19)==='500');
43 assert.equal(requests.length,1);assert.ok(requests[0].endsWith('/cells/batch'));
44 const pasteMs=Date.now()-start;
45 await second.bringToFront();
46 await second.waitForFunction(()=>window.SheetsGrid.rawAt(24,19)==='500');
47 await page.bringToFront();
48 await page.evaluate(()=>{window.SheetsGrid.worksheet().updateSelectionFromCoords(0,0,19,24);window.SheetsGrid.focus();});
49 await page.keyboard.press('Control+x');
50 assert.equal(await page.evaluate(()=>SheetsGrid.rawAt(0,0)), '1');
51 await page.keyboard.press('Escape');
52 const snap=await page.evaluate(async api=>{const r=await fetch(api);return r.json();},api);
53 assert.equal(snap.cells.length,500);
54 const before=snap.cells.map(c=>[c.r,c.c,c.raw]).sort();
55 const refused=await page.evaluate(async api=>{
56 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;
57 },api);
58 assert.equal(refused,400);
59 const after=await page.evaluate(async api=>{const r=await fetch(api);return r.json();},api);
60 assert.deepEqual(after.cells.map(c=>[c.r,c.c,c.raw]).sort(),before);
61 await page.reload();await page.waitForFunction(()=>window.SheetsGrid?.worksheet()&&window.SheetsGrid.rawAt(24,19)==='500');
62 const nojs=await browser.newContext({storageState:state,javaScriptEnabled:false});
63 const table=await nojs.newPage();await table.goto(location+'/table');
64 assert.equal(await table.locator('a[aria-label="Edit T25"]').innerText(),'500');
65 assert.equal(await table.locator('a[aria-label="Edit A1"]').innerText(),'1');
66 const queue = await checkCellQueue(page);
67 assert.equal(signinPosted, false);
68 assert.deepEqual(errors,[]);
69 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};
70 if(process.env.SHEETS_CANARY_RESULT)fs.writeFileSync(process.env.SHEETS_CANARY_RESULT,JSON.stringify(result,null,2));
71 console.log(JSON.stringify(result));
72} finally {await browser.close();}
73