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,100 @@
|
||||
// End-to-end check of where "Today" puts the daily note. Run with `bun run test:e2e`.
|
||||
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-daily-'));
|
||||
const vault = path.join(tmp, 'Vault');
|
||||
fs.mkdirSync(path.join(vault, 'Private', 'Daily'), { recursive: true });
|
||||
fs.writeFileSync(path.join(vault, 'Private.md'), 'Private stuff\n');
|
||||
fs.writeFileSync(path.join(vault, 'Private', 'Daily.md'), 'My journal\n');
|
||||
fs.writeFileSync(path.join(vault, 'Private', 'Daily', '2020-01-01.md'), 'An old day\n');
|
||||
app.setPath('userData', tmp);
|
||||
fs.writeFileSync(path.join(tmp, 'settings.json'), JSON.stringify({ vault, lastPage: 'Private', theme: 'dark', bounds: { width: 1100, height: 760 } }));
|
||||
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);
|
||||
};
|
||||
const z = (n) => String(n).padStart(2, '0');
|
||||
const d = new Date();
|
||||
const TODAY = `${d.getFullYear()}-${z(d.getMonth() + 1)}-${z(d.getDate())}`;
|
||||
|
||||
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 m = (type, x, y) => wc.sendInputEvent({ type, x: Math.round(x), y: Math.round(y), button: 'left', clickCount: 1 });
|
||||
const box = (sel) => js(`const e = ${sel}; if (!e) return null; const r = e.getBoundingClientRect(); return { cx: r.x + r.width / 2, cy: r.y + r.height / 2 }`);
|
||||
const click = async (x, y) => { m('mouseMove', x, y); m('mouseDown', x, y); m('mouseUp', x, y); await sleep(300); };
|
||||
const type = async (t) => { for (const ch of t) { wc.sendInputEvent({ type: 'char', keyCode: ch }); await sleep(30); } await sleep(250); };
|
||||
const key = async (keyCode) => { wc.sendInputEvent({ type: 'keyDown', keyCode }); wc.sendInputEvent({ type: 'keyUp', keyCode }); await sleep(250); };
|
||||
const saved = () => JSON.parse(fs.readFileSync(path.join(tmp, 'settings.json'), 'utf8'));
|
||||
const today = async () => {
|
||||
await js(`[...document.querySelectorAll('.sidebar .qa')].find((b) => b.textContent.includes('Today')).click()`);
|
||||
await sleep(1500);
|
||||
};
|
||||
|
||||
try {
|
||||
await sleep(2200);
|
||||
await today();
|
||||
check('"Today" uses the Daily folder where it already is, not a new one at the top', fs.existsSync(path.join(vault, 'Private', 'Daily', `${TODAY}.md`)) && !fs.existsSync(path.join(vault, 'Daily')) && !fs.existsSync(path.join(vault, 'Daily.md')));
|
||||
check('...and opens it', (await js(`return document.querySelector('.tree .row.active')?.dataset.id`)) === `Private/Daily/${TODAY}`);
|
||||
|
||||
// choose another folder in Settings
|
||||
await js(`[...document.querySelectorAll('.sidebar button')].find((b) => b.getAttribute('aria-label') === 'Settings').click()`);
|
||||
await sleep(500);
|
||||
check('Settings shows the folder it found', (await js(`return document.querySelector('input[aria-label="Daily notes folder"]')?.placeholder`)) === 'Private/Daily');
|
||||
const field = await box(`document.querySelector('input[aria-label="Daily notes folder"]')`);
|
||||
await click(field.cx, field.cy);
|
||||
check('clicking the field lists the folders, with "find automatically" first', await js(`const l = [...document.querySelectorAll('.fp-list .mi')].map((b) => b.textContent.trim()); return l[0].startsWith('Find automatically') && l.some((t) => t.startsWith('Daily')) && l.some((t) => t.startsWith('Private'))`));
|
||||
await type('dai');
|
||||
check('typing narrows it, best matches first', await js(`const l = [...document.querySelectorAll('.fp-list .mi')].map((b) => b.querySelector('.mi-label').textContent.trim()); return l.length >= 2 && l[0] === 'Daily' && !l.includes('Private') && l[l.length - 1].startsWith('Use')`));
|
||||
await key('Escape');
|
||||
check('Escape closes the list and keeps the dialog', !(await js(`return !!document.querySelector('.fp-list')`)) && !!(await js(`return !!document.querySelector('.settings')`)));
|
||||
await click(field.cx, field.cy);
|
||||
await js(`const i = document.querySelector('input[aria-label="Daily notes folder"]'); i.value = ''; i.dispatchEvent(new Event('input', { bubbles: true }));`);
|
||||
await type(' Journal / Days ');
|
||||
check('a folder that does not exist yet can be used as typed, tidied', await js(`const l = [...document.querySelectorAll('.fp-list .mi')]; return l[l.length - 1].textContent.includes('Use “Journal/Days”')`));
|
||||
await key('Return');
|
||||
await sleep(600);
|
||||
check('Enter keeps it', (await js(`return document.querySelector('input[aria-label="Daily notes folder"]').value`)) === 'Journal/Days');
|
||||
const vkey = Object.keys(saved().vaults || {})[0];
|
||||
check('...and remembers it with the notes folder', saved().vaults[vkey].dailyFolder === 'Journal/Days', JSON.stringify(saved().vaults));
|
||||
// picking an existing folder from the list
|
||||
await click(field.cx, field.cy);
|
||||
await type('priv');
|
||||
const row = await box(`[...document.querySelectorAll('.fp-list .mi')].find((b) => b.querySelector('.mi-label').textContent.trim() === 'Private')`);
|
||||
await click(row.cx, row.cy);
|
||||
await sleep(500);
|
||||
check('clicking a folder in the list chooses it', saved().vaults[vkey].dailyFolder === 'Private' && !(await js(`return !!document.querySelector('.fp-list')`)));
|
||||
await click(field.cx, field.cy);
|
||||
await type('Journal/Days');
|
||||
await key('Return');
|
||||
await sleep(500);
|
||||
await js(`document.querySelector('.settings [aria-label="Close"]').click()`);
|
||||
await sleep(400);
|
||||
fs.rmSync(path.join(vault, 'Private', 'Daily', `${TODAY}.md`));
|
||||
await today();
|
||||
check('"Today" now makes the note in the chosen folder', fs.existsSync(path.join(vault, 'Journal', 'Days', `${TODAY}.md`)) && !fs.existsSync(path.join(vault, 'Private', 'Daily', `${TODAY}.md`)));
|
||||
check('...and nothing is made at the top', !fs.existsSync(path.join(vault, 'Daily')));
|
||||
|
||||
// back to automatic
|
||||
await js(`[...document.querySelectorAll('.sidebar button')].find((b) => b.getAttribute('aria-label') === 'Settings').click()`);
|
||||
await sleep(500);
|
||||
await js(`document.querySelector('.set-link').click()`);
|
||||
await sleep(500);
|
||||
check('"Find it automatically" goes back', (await js(`return document.querySelector('input[aria-label="Daily notes folder"]').value`)) === '' && saved().vaults[vkey].dailyFolder === null);
|
||||
} catch (err) {
|
||||
check('harness', false, err.stack);
|
||||
}
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
console.log(failures.length ? `\n${failures.length} check(s) failed` : '\ndaily e2e passed!');
|
||||
app.exit(failures.length ? 1 : 0);
|
||||
});
|
||||
Reference in new issue
Block a user