| 1 | /* Drive the table view's formatting with JavaScript SWITCHED OFF. |
| 2 | * |
| 3 | * The only drive script that runs a page with javaScriptEnabled:false, |
| 4 | * and the point of it: PRODUCT.md's second principle says the scriptless |
| 5 | * path is the product, not the fallback, so a test that drove this with |
| 6 | * scripting on would prove the wrong thing entirely. |
| 7 | * |
| 8 | * It picks bold, centre and a fill from three selects, submits, follows |
| 9 | * the 303, and reads the COMPUTED style off the cell — then reopens the |
| 10 | * grid and asserts it agrees. Both halves matter: a screen that can set |
| 11 | * bold and not show it is the worse half of the feature. |
| 12 | * |
| 13 | * SHEETS_SOCK=$D/s.sock SHEETS_LOG=$D/s.log OUT=$D \ |
| 14 | * PLAYWRIGHT=/path/to/playwright/index.js node tools/drive/tableformat.mjs |
| 15 | */ |
| 16 | import net from 'node:net'; |
| 17 | import fs from 'node:fs'; |
| 18 | const pw=await import(process.env.PLAYWRIGHT); const chromium=pw.chromium||(pw.default&&pw.default.chromium); |
| 19 | const proxy=net.createServer(c=>{const u=net.connect(process.env.SHEETS_SOCK);c.pipe(u);u.pipe(c);c.on('error',()=>u.destroy());u.on('error',()=>c.destroy());}); |
| 20 | await new Promise(r=>proxy.listen(8799,'127.0.0.1',r)); |
| 21 | const base='http://127.0.0.1:8799', OUT=process.env.OUT; |
| 22 | const browser=await chromium.launch(); |
| 23 | const page=await (await browser.newContext({viewport:{width:1280,height:900},deviceScaleFactor:2})).newPage(); |
| 24 | const errs=[]; page.on('pageerror',e=>errs.push(String(e))); |
| 25 | await page.goto(`${base}/signin`); await page.fill('input[type=email]','paul@example.com'); |
| 26 | await Promise.all([page.waitForURL(/signin\?sent=1/), page.click('form[action="/signin"] button[type=submit]')]); |
| 27 | const mm=[...fs.readFileSync(process.env.SHEETS_LOG,'utf8').matchAll(/http:\/\/127\.0\.0\.1:8799\/auth\/verify\?[^\s\\"]+/g)].pop(); |
| 28 | await page.goto(mm[0]); await page.waitForURL(/\/sheets/); |
| 29 | // Create from the top bar, then name it by renaming: the list's #title |
| 30 | // field is gone, so a sheet is born "Untitled sheet" and named after. |
| 31 | await Promise.all([page.waitForURL(/\/sheets\/\d+$/),page.click('[estilo-bar-actions] form[action="/sheets"] button[type=submit]')]); |
| 32 | await page.fill('[estilo-bar-title-input]','Table format'); |
| 33 | await Promise.all([page.waitForURL(/\/sheets\/\d+$/),page.press('[estilo-bar-title-input]','Enter')]); |
| 34 | const url=page.url(); |
| 35 | await page.waitForFunction(()=>window.SheetsGrid&&window.SheetsGrid.worksheet()); |
| 36 | for(const [r,c,v] of [[0,0,'Region'],[0,1,'Revenue'],[1,0,'North'],[1,1,'1200'],[2,0,'South'],[2,1,'900']]) |
| 37 | await page.evaluate(([r,c,v])=>window.SheetsGrid.write(r,c,v),[r,c,v]); |
| 38 | await page.waitForTimeout(600); |
| 39 | |
| 40 | // JavaScript OFF, to prove the scriptless claim. |
| 41 | const ctx2=await browser.newContext({viewport:{width:1280,height:900},deviceScaleFactor:2,javaScriptEnabled:false, |
| 42 | storageState: await page.context().storageState()}); |
| 43 | const p2=await ctx2.newPage(); |
| 44 | await p2.goto(`${base}${new URL(url).pathname}/table?page=1&edit=0,0`); |
| 45 | console.log('scriptless: the format form is present:', await p2.locator('[data-cell-format]').count()>0); |
| 46 | await p2.selectOption('select[name=bold]','set'); |
| 47 | await p2.selectOption('select[name=align]','center'); |
| 48 | await p2.selectOption('select[name=fill]','green'); |
| 49 | await Promise.all([p2.waitForNavigation(), p2.click('[data-cell-format] button[type=submit]')]); |
| 50 | console.log('scriptless: landed on', new URL(p2.url()).pathname + new URL(p2.url()).search); |
| 51 | const painted = await p2.evaluate(()=>{ |
| 52 | const td=[...document.querySelectorAll('[data-cell-table] td')].find(t=>t.textContent.includes('Region')); |
| 53 | if(!td) return 'cell not found'; |
| 54 | const cs=getComputedStyle(td); |
| 55 | return {attrs:[...td.attributes].map(a=>a.name).join(' '), weight:cs.fontWeight, align:cs.textAlign, bg:cs.backgroundColor};}); |
| 56 | console.log('scriptless: the cell now paints', JSON.stringify(painted)); |
| 57 | await p2.screenshot({path:`${OUT}/table-format.png`, clip:{x:0,y:0,width:1000,height:620}}); |
| 58 | |
| 59 | // and the grid agrees |
| 60 | await page.reload(); await page.waitForFunction(()=>window.SheetsGrid&&window.SheetsGrid.worksheet()); |
| 61 | await page.waitForTimeout(600); |
| 62 | console.log('grid agrees:', JSON.stringify(await page.evaluate(()=>window.SheetsGrid.styleAt(0,0)))); |
| 63 | console.log('ERRORS:',JSON.stringify(errs)); |
| 64 | await browser.close(); proxy.close(); process.exit(0); |
| 65 | |