import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import { createServer } from 'node:http'; import process from 'node:process'; const { chromium } = await import( process.env.PLAYWRIGHT_MODULE ?? 'playwright' ); const script = await readFile( new URL('../ui/workspace-navigation.js', import.meta.url), 'utf8', ); const server = createServer((request, response) => { response.setHeader('Content-Type', 'text/html'); response.end(`

${request.url}

Board columns
Long dialog
`); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const browser = await chromium.launch({ channel: 'chrome', headless: true }); const page = await browser.newPage({ viewport: { width: 390, height: 844 } }); await page.addInitScript(script); const root = `http://127.0.0.1:${server.address().port}`; try { await page.goto(root); const length = await page.evaluate(() => history.length); for (const path of ['/board', '/ticket', '/settings']) { await page.evaluate( (path) => history.pushState( { page: { url: path }, rememberedState: { draft: 'retained' }, }, '', path, ), path, ); } await page.evaluate(() => { window.steps = 0; addEventListener('popstate', () => window.steps++); }); await page.evaluate(() => window.__kanbanNavigation.step(-1)); await page.waitForFunction(() => location.pathname === '/ticket'); assert.equal(await page.evaluate(() => window.steps), 1); await page.evaluate(() => window.__kanbanNavigation.step(-1)); await page.waitForFunction(() => location.pathname === '/board'); await page.evaluate(() => window.__kanbanNavigation.step(1)); await page.waitForFunction(() => location.pathname === '/ticket'); assert.equal(await page.evaluate(() => window.steps), 3); assert.equal(await page.evaluate(() => history.length), length + 3); assert.equal(await page.locator('#draft').inputValue(), 'retained'); assert.equal( await page.evaluate(() => history.state.rememberedState.draft), 'retained', ); await page.goto(`${root}/full-page`); await page.evaluate(() => window.__kanbanNavigation.step(-1)); await page.waitForURL(`${root}/ticket`); await page.evaluate(() => window.__kanbanNavigation.step(1)); await page.waitForURL(`${root}/full-page`); await page.locator('dialog').evaluate((dialog) => dialog.showModal()); assert.equal( await page.evaluate(() => window.__kanbanNavigation.canSwipe(1, 1)), false, ); await page.locator('dialog').evaluate((dialog) => dialog.close()); for (const selector of ['#board', '#draft']) { const point = await page.locator(selector).boundingBox(); assert.equal( await page.evaluate( ({ x, y }) => window.__kanbanNavigation.canSwipe(x + 5, y + 5), point, ), false, ); } assert.equal( await page.evaluate(() => window.__kanbanNavigation.canSwipe(1, 1)), true, ); await page.dispatchEvent('#board', 'dragstart'); assert.equal( await page.evaluate(() => window.__kanbanNavigation.canSwipe(1, 1)), false, ); await page.dispatchEvent('#board', 'dragend'); assert.equal( await page.evaluate(() => window.__kanbanNavigation.canSwipe(1, 1)), true, ); assert.equal( await page.evaluate( () => getComputedStyle(document.documentElement).overscrollBehaviorY, ), 'none', ); assert.equal( await page.evaluate( () => getComputedStyle(document.documentElement).overscrollBehaviorX, ), 'auto', ); assert.equal( await page.evaluate(() => document.documentElement.scrollHeight), 844, ); await page.mouse.move(380, 400); await page.mouse.wheel(0, -1000); await page.waitForTimeout(200); assert.equal(await page.evaluate(() => scrollY), 0); await page .locator('#long') .evaluate((element) => (element.style.height = '3000px')); await page.mouse.wheel(0, 1000); await page.waitForFunction(() => scrollY > 0); await page.evaluate(() => scrollTo(0, document.documentElement.scrollHeight), ); await page.mouse.wheel(0, 5000); await page.waitForTimeout(200); assert.equal( await page.evaluate(() => scrollY + innerHeight), await page.evaluate(() => document.documentElement.scrollHeight), ); await page.evaluate(() => scrollTo(0, 0)); await page.locator('#board').hover(); await page.mouse.wheel(500, 0); await page.waitForFunction( () => document.querySelector('#board').scrollLeft > 0, ); assert.equal(new URL(page.url()).pathname, '/full-page'); await page.locator('dialog').evaluate((dialog) => dialog.showModal()); await page.locator('dialog').hover(); await page.mouse.wheel(0, 500); await page.waitForFunction( () => document.querySelector('dialog').scrollTop > 0, ); console.log( 'PASS: SPA/full-page history, forward history, overlays, short/long scroll bounds, horizontal board, drag and input guards', ); } finally { await browser.close(); await new Promise((resolve) => server.close(resolve)); }