// quickadd.mjs — the quick-add box in a real browser, both ways. // // Driven by TestQuickAddInARealBrowser, which starts the app, signs in, // and passes this script the origin and a session cookie. Playwright // rather than chromedp: chromedp does not drive in this environment at // all (it starts Chromium and parks at about:blank until the Go test // times out), and a gate that cannot run is not a gate. // // Five passes, and the scriptless ones matter most: // // 1. WITH script — type, and the pane fills in from the server. // 2. WITHOUT script — the same box still opens, still submits, and // still creates the event. // 3. WITH script — Enter adds it, Shift+Enter does not. // 4. WITHOUT script — the Enter hint is absent, because the shortcut // does not exist there and a page must not promise one. // 5. WITH script — a date range reads as a run of days, and the pane // names the last one. // // The scriptless passes are the only way to prove the floor is real. // Every other test in this repo asks Go what Go produced; these ask a // browser with JavaScript switched off whether the feature still works. import { chromium } from 'playwright'; const [origin, cookieName, cookieValue] = process.argv.slice(2); if (!origin || !cookieName || !cookieValue) { console.error('usage: quickadd.mjs '); process.exit(2); } const failures = []; function check(ok, what) { if (ok) return; failures.push(what); console.error('FAIL: ' + what); } const SENTENCE = 'Board meeting with doc@tito.io tomorrow at 3pm in McCloskeys'; const browser = await chromium.launch(); async function contextWith(javaScriptEnabled) { const ctx = await browser.newContext({ javaScriptEnabled }); await ctx.addCookies([ { name: cookieName, value: cookieValue, url: origin, httpOnly: true }, ]); return ctx; } // ── 1. With script: the pane fills in as you type ──────────────────── { const ctx = await contextWith(true); const page = await ctx.newPage(); const errors = []; page.on('console', (m) => { if (m.type() === 'error') errors.push(m.text()); }); page.on('pageerror', (e) => errors.push(String(e))); await page.goto(origin + '/today', { waitUntil: 'networkidle' }); const box = page.locator('[cal-quick]'); check(await box.count() === 1, 'the quick add box is on the day page'); // Closed, the form must not be reachable — a popover that is merely // transparent is still in the tab order. check(!(await page.locator('[cal-quick-text]').isVisible()), 'the textarea is hidden until the box is opened'); await page.locator('[cal-quick] > summary').click(); check(await page.locator('[cal-quick-text]').isVisible(), 'clicking the box opens it'); // The crossing from computed value to PAINTED output: the popover has // to have real size and sit inside the viewport. A panel positioned // off the side of the page passes every string assertion in the Go // suite and is invisible to a person. const rect = await page.locator('[cal-quick-form]').boundingBox(); check(rect !== null && rect.width > 200 && rect.height > 80, 'the popover has real painted size (got ' + JSON.stringify(rect) + ')'); const vw = page.viewportSize().width; check(rect !== null && rect.x >= 0 && rect.x + rect.width <= vw + 1, 'the popover sits inside the viewport (got ' + JSON.stringify(rect) + ')'); await page.locator('[cal-quick-text]').fill(SENTENCE); // The pane is filled by the SERVER. Waiting for its text is waiting // for the round trip, which is the thing under test. await page.locator('[cal-quick-pane]').getByText('McCloskeys').waitFor({ timeout: 5000 }); const pane = await page.locator('[cal-quick-pane]').innerText(); check(pane.includes('Board meeting'), 'the pane shows the title it parsed'); check(pane.includes('McCloskeys'), 'the pane shows the place'); check(pane.includes('doc@tito.io'), 'the pane shows the attendee'); check(pane.includes('15:00'), 'the pane resolved 3pm to 15:00, not 03:00'); check(/not emailed/i.test(pane), 'the pane says nobody is emailed'); check(errors.length === 0, 'no console errors (' + errors.join('; ') + ')'); await ctx.close(); } // ── 2. Without script: the same box still works ────────────────────── { const ctx = await contextWith(false); const page = await ctx.newPage(); await page.goto(origin + '/today'); //
is native, so it opens with no JavaScript at all. await page.locator('[cal-quick] > summary').click(); check(await page.locator('[cal-quick-text]').isVisible(), 'the box opens with JavaScript disabled'); await page.locator('[cal-quick-text]').fill('Scriptless lunch tomorrow at 1pm in Fumbally'); await Promise.all([ page.waitForNavigation(), page.locator('[cal-quick-form] button[type=submit]').click(), ]); check(/\/events\/\d+$/.test(page.url()), 'submitting with no script lands on the new event (got ' + page.url() + ')'); const body = await page.locator('body').innerText(); check(body.includes('Scriptless lunch'), 'the scriptless event kept its title'); check(body.includes('Fumbally'), 'the scriptless event kept its place'); await ctx.close(); } // ── 3. Enter adds it; Shift+Enter does not ────────────────────────── { const ctx = await contextWith(true); const page = await ctx.newPage(); await page.goto(origin + '/today', { waitUntil: 'networkidle' }); await page.locator('[cal-quick] > summary').click(); const box = page.locator('[cal-quick-text]'); // Shift+Enter is a newline, so it must NOT create anything. Asserted // as "did not land on an event" rather than "still on /today": /today // redirects to /day/, so the page was never on /today to begin // with and the tighter-looking assertion was just wrong. // // Checked BEFORE the submitting case — once Enter navigates there is no // page left to test this on. await box.fill('Draft lunch'); await box.press('Shift+Enter'); await page.waitForTimeout(300); check(!/\/events\/\d+$/.test(page.url()), 'Shift+Enter created nothing (got ' + page.url() + ')'); check((await box.inputValue()).includes('\n'), 'Shift+Enter inserts a newline rather than submitting'); // The hint only appears when this file is running, because without it // Enter does nothing. check(await page.locator('[cal-quick-key]').isVisible(), 'the Enter hint is shown when script is on'); await box.fill('Keyboard standup tomorrow at 8am in the Loft'); await Promise.all([ page.waitForNavigation(), box.press('Enter'), ]); check(/\/events\/\d+$/.test(page.url()), 'Enter creates the event (got ' + page.url() + ')'); const body = await page.locator('body').innerText(); check(body.includes('Keyboard standup'), 'the event Enter created kept its title'); check(body.includes('Loft'), 'the event Enter created kept its place'); await ctx.close(); } // ── 4. The Enter hint is absent with no script ────────────────────── { const ctx = await contextWith(false); const page = await ctx.newPage(); await page.goto(origin + '/today'); await page.locator('[cal-quick] > summary').click(); check(!(await page.locator('[cal-quick-key]').isVisible()), 'the Enter hint stays hidden with JavaScript off — the shortcut does not work there'); await ctx.close(); } // ── 5. A date range is a run of days ──────────────────────────────── { const ctx = await contextWith(true); const page = await ctx.newPage(); await page.goto(origin + '/today', { waitUntil: 'networkidle' }); await page.locator('[cal-quick] > summary').click(); await page.locator('[cal-quick-text]').fill('london tues-thurs'); await page.waitForFunction( () => /Tuesday/.test(document.querySelector('[cal-quick-pane]')?.innerText || ''), null, { timeout: 5000 }, ); const pane = await page.locator('[cal-quick-pane]').innerText(); check(/Tuesday/.test(pane) && /Thursday/.test(pane), 'the pane names both ends of the run:\n' + pane); check(/all day/i.test(pane), 'the pane says a run with no time is all day:\n' + pane); // The stranded word: the whole reason this pass exists. check(!/thurs\b/i.test(pane.split('\n')[0] || ''), 'the end of the range was left in the title:\n' + pane); await ctx.close(); } await browser.close(); if (failures.length) { console.error('\n' + failures.length + ' browser check(s) failed'); process.exit(1); } console.log('quick add: all browser checks passed');