test: unit and end-to-end suites
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27fafa0bcd
commit
9b00e21c44
49 files changed
+6694
-1
No files matched your search
@@ -0,0 +1,105 @@
|
||||
// End-to-end check of the page context menu: the "Add inside" submenu, the delete confirmation, and the sidebar "+" menu.
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ink-e2e-ctx-'));
|
||||
const vault = path.join(tmp, 'Vault');
|
||||
fs.mkdirSync(vault);
|
||||
fs.writeFileSync(path.join(vault, 'Alpha.md'), 'Content of Alpha\n');
|
||||
fs.writeFileSync(path.join(vault, 'Beta.md'), 'Content of Beta\n');
|
||||
|
||||
app.setPath('userData', tmp);
|
||||
fs.writeFileSync(path.join(tmp, 'settings.json'), JSON.stringify({ vault, lastPage: 'Alpha', bounds: { width: 1280, height: 860 } }));
|
||||
require('../electron/main.cjs');
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const failures = [];
|
||||
const check = (name, ok, extra = '') => {
|
||||
console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${extra ? ' — ' + extra : ''}`);
|
||||
if (!ok) failures.push(name);
|
||||
};
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
let win;
|
||||
for (let i = 0; i < 80 && !(win = BrowserWindow.getAllWindows()[0]); i++) await sleep(100);
|
||||
const wc = win.webContents;
|
||||
if (wc.isLoading()) await new Promise((r) => wc.once('did-finish-load', r));
|
||||
const js = (code) => wc.executeJavaScript(`(async () => { ${code} })()`);
|
||||
const menuOpen = () => js(`return !!document.querySelector('.ctx')`);
|
||||
const subItems = () => js(`return [...document.querySelectorAll('.ctx .sub .mi')].map((b) => b.textContent.trim())`);
|
||||
const rightClick = (id) => js(`document.querySelector('.row[data-id="${id}"]').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 200, clientY: 200 }))`);
|
||||
|
||||
try {
|
||||
await sleep(2000);
|
||||
|
||||
await rightClick('Beta');
|
||||
await sleep(300);
|
||||
check('the page menu opens', await menuOpen());
|
||||
check('the four "Add … inside" items are one "Add inside" item', await js(`const t = [...document.querySelectorAll('.ctx > .mi, .ctx > .mi-sub > .mi')].map((b) => b.textContent.trim()); return t.includes('Add inside') && !t.some((x) => /^Add (page|board|database|canvas) inside$/.test(x))`));
|
||||
check('the side menu is closed at first', (await subItems()).length === 0);
|
||||
|
||||
await js(`document.querySelector('.ctx .mi-sub').dispatchEvent(new PointerEvent('pointerenter'))`);
|
||||
await sleep(200);
|
||||
check('hovering opens the side menu with the four kinds', JSON.stringify(await subItems()) === JSON.stringify(['Page', 'Board', 'Database', 'Canvas']), JSON.stringify(await subItems()));
|
||||
|
||||
await js(`document.querySelector('.ctx .mi-sub').dispatchEvent(new PointerEvent('pointerleave'))`);
|
||||
await sleep(40);
|
||||
check('...and it stays open for a moment after the pointer leaves', (await subItems()).length === 4);
|
||||
await js(`document.querySelector('.ctx .mi-sub')?.dispatchEvent(new PointerEvent('pointerenter'))`);
|
||||
await sleep(300);
|
||||
check('...and stays if the pointer comes back', (await subItems()).length === 4);
|
||||
|
||||
await js(`document.querySelector('.ctx .mi-sub').dispatchEvent(new PointerEvent('pointerleave'))`);
|
||||
await sleep(400);
|
||||
check('...and closes once it has really left', (await subItems()).length === 0);
|
||||
|
||||
// Delete asks twice, and forgets the first press.
|
||||
await js(`document.querySelector('.ctx .mi.danger').click()`);
|
||||
await sleep(100);
|
||||
check('Delete asks for a second click', await js(`return document.querySelector('.ctx .mi.danger').classList.contains('armed')`));
|
||||
await sleep(3400);
|
||||
check('...and forgets it after a few seconds', await js(`return !document.querySelector('.ctx .mi.danger').classList.contains('armed')`));
|
||||
|
||||
await js(`window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))`);
|
||||
await sleep(200);
|
||||
check('Escape closes the menu', !(await menuOpen()));
|
||||
|
||||
// Without hover (touch): a click opens and closes the side menu.
|
||||
await rightClick('Beta');
|
||||
await sleep(300);
|
||||
await js(`document.querySelector('.ctx .mi-sub > .mi').click()`);
|
||||
await sleep(150);
|
||||
check('a click opens the side menu', (await subItems()).length === 4);
|
||||
await js(`document.querySelector('.ctx .mi-sub > .mi').click()`);
|
||||
await sleep(150);
|
||||
check('...and a second click closes it', (await subItems()).length === 0);
|
||||
await js(`window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))`);
|
||||
await sleep(150);
|
||||
|
||||
// The page's icon and its breadcrumb open the page menu too
|
||||
await js(`document.querySelector('.p-icon').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 300, clientY: 200 }))`);
|
||||
await sleep(200);
|
||||
check('right-clicking the page icon opens the page menu', await menuOpen());
|
||||
await js(`window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))`);
|
||||
await sleep(150);
|
||||
await js(`document.querySelector('.crumb').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 300, clientY: 40 }))`);
|
||||
await sleep(200);
|
||||
check('right-clicking a breadcrumb opens the page menu', await menuOpen());
|
||||
await js(`window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))`);
|
||||
await sleep(150);
|
||||
|
||||
// The "+" on a sidebar row
|
||||
await js(`document.querySelector('.row[data-id="Beta"] .row-add').click()`);
|
||||
await sleep(300);
|
||||
check('the "+" opens the kind menu', JSON.stringify(await js(`return [...document.querySelectorAll('.ctx .mi')].map((b) => b.textContent.trim())`)) === JSON.stringify(['Page', 'Board', 'Database', 'Canvas']));
|
||||
await js(`window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))`);
|
||||
} catch (err) {
|
||||
check('harness', false, err.stack);
|
||||
}
|
||||
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
console.log(failures.length ? `\n${failures.length} check(s) failed` : '\ncontext menu e2e passed!');
|
||||
app.exit(failures.length ? 1 : 0);
|
||||
});
|
||||
Reference in new issue
Block a user