96 lines
5.3 KiB
JavaScript
96 lines
5.3 KiB
JavaScript
// End-to-end: keyboard use of the sidebar, focus inside dialogs, rebinding a shortcut, long folders shown in steps.
|
|
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-a11y-'));
|
|
const vault = path.join(tmp, 'Vault');
|
|
fs.mkdirSync(path.join(vault, 'Big'), { recursive: true });
|
|
fs.writeFileSync(path.join(vault, 'Big.md'), 'big\n');
|
|
for (let i = 0; i < 250; i++) fs.writeFileSync(path.join(vault, 'Big', `Item ${String(i).padStart(3, '0')}.md`), `item ${i}\n`);
|
|
fs.writeFileSync(path.join(vault, 'Alpha.md'), 'a\n');
|
|
fs.writeFileSync(path.join(vault, 'Beta.md'), 'b\n');
|
|
app.setPath('userData', tmp);
|
|
fs.writeFileSync(path.join(tmp, 'settings.json'), JSON.stringify({ vault, lastPage: 'Alpha', theme: 'dark', bounds: { width: 1200, height: 800 } }));
|
|
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 key = async (keyCode, modifiers = []) => { wc.sendInputEvent({ type: 'keyDown', keyCode, modifiers }); wc.sendInputEvent({ type: 'keyUp', keyCode, modifiers }); await sleep(250); };
|
|
const active = () => js(`const a = document.activeElement; return a && (a.dataset.id || a.getAttribute('aria-label') || a.className)`);
|
|
try {
|
|
await sleep(2200);
|
|
|
|
// sidebar: arrow keys
|
|
await js(`document.querySelector('.tree .row[data-id="Alpha"]').focus()`);
|
|
await key('Down');
|
|
check('Down moves to the next row', (await active()) === 'Beta', await active());
|
|
await key('Up');
|
|
await key('Up');
|
|
check('Up moves back, and stops at the first row', (await active()) === 'Alpha', await active());
|
|
await key('End');
|
|
check('End goes to the last row', (await active()) === 'Big', await active());
|
|
check('only the open page is a tab stop in the list', (await js(`return document.querySelectorAll('.tree .row[tabindex="0"]').length`)) === 1);
|
|
|
|
// long folders come in steps
|
|
await js(`document.querySelector('.tree .row[data-id="Big"]').focus()`);
|
|
await key('Right');
|
|
await sleep(400);
|
|
check('a long folder shows its first 100', (await js(`return document.querySelectorAll('.tree .row[data-id^="Big/"]').length`)) === 100);
|
|
await js(`document.querySelector('.tree .row.more').click()`);
|
|
await sleep(300);
|
|
check('...and "show more" adds the next 100', (await js(`return document.querySelectorAll('.tree .row[data-id^="Big/"]').length`)) === 200);
|
|
|
|
// dialogs keep focus
|
|
await js(`document.querySelector('.sidebar button[aria-label="Tasks"]').focus()`);
|
|
await js(`document.querySelector('.sidebar button[aria-label="Tasks"]').click()`);
|
|
await sleep(500);
|
|
check('a dialog is modal and holds focus', (await js(`const d = document.querySelector('[role=dialog]'); return d.getAttribute('aria-modal') === 'true' && d.contains(document.activeElement)`)) === true);
|
|
for (let i = 0; i < 12; i++) await key('Tab');
|
|
check('Tab stays inside it', (await js(`return document.querySelector('[role=dialog]').contains(document.activeElement)`)) === true);
|
|
await key('Escape');
|
|
await sleep(600);
|
|
check('Escape closes it and focus goes back', (await js(`return !document.querySelector('[role=dialog]') && document.activeElement?.getAttribute('aria-label') === 'Tasks'`)) === true);
|
|
|
|
// rebinding
|
|
await key('Y', ['control', 'shift']);
|
|
check('Ctrl+Shift+Y does nothing yet', (await js(`return !document.querySelector('.tasks-panel')`)) === true);
|
|
await js(`document.querySelector('.sidebar button[aria-label="Settings"]').click()`);
|
|
await sleep(400);
|
|
await js(`[...document.querySelectorAll('.set-nav button')].find((b) => b.textContent.includes('Shortcuts')).click()`);
|
|
await sleep(200);
|
|
await js(`document.querySelector('button[aria-label="Change shortcut for Tasks"]').click()`);
|
|
await sleep(150);
|
|
await key('Y', ['control', 'shift']);
|
|
await sleep(400);
|
|
const saved = JSON.parse(fs.readFileSync(path.join(tmp, 'settings.json'), 'utf8'));
|
|
check('the new shortcut is saved', saved.prefs && saved.prefs.shortcuts && saved.prefs.shortcuts.tasks === 'Mod+Shift+Y', JSON.stringify(saved.prefs && saved.prefs.shortcuts));
|
|
await key('Escape');
|
|
await sleep(500);
|
|
await key('Y', ['control', 'shift']);
|
|
await sleep(400);
|
|
check('...and opens Tasks', (await js(`return !!document.querySelector('.tasks-panel')`)) === true);
|
|
await key('Escape');
|
|
await sleep(300);
|
|
await key('T', ['control', 'shift']);
|
|
check('the old one no longer does', (await js(`return !document.querySelector('.tasks-panel')`)) === 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` : '\na11y e2e passed!');
|
|
app.exit(failures.length ? 1 : 0);
|
|
});
|