CI/CD / preconditions (pull_request) Successful in 2s
Native JavaScript checks / Native launcher and release tests (pull_request) Successful in 1m4s
macOS checks / Native Rust tests and unsigned build (pull_request) Successful in 1m31s
CI/CD / test_and_check_app (pull_request) Successful in 6m44s
CI/CD / Publish immutable image (pull_request) Skipped
152 lines
5.9 KiB
JavaScript
152 lines
5.9 KiB
JavaScript
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(`<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1"><style>
|
|
body { margin: 0; } #board { overflow-x: auto; width: 300px; height: 100px; }
|
|
#board > div { width: 1500px; height: 100px; } dialog { max-height: 200px; overflow-y: auto; }
|
|
</style></head><body><p id="page">${request.url}</p><input id="draft" value="retained"><div id="board"><div>Board columns</div></div>
|
|
<dialog><button id="close">Close</button><div style="height:1000px">Long dialog</div></dialog><div id="long"></div></body></html>`);
|
|
});
|
|
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));
|
|
}
|