|
|
|
@@ -34,7 +34,9 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
|
|
|
|
app.whenReady().then(async () => {
|
|
|
|
|
const win = new BrowserWindow({ show: true, width: 1280, height: 800, useContentSize: true,
|
|
|
|
|
webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false, backgroundThrottling: false } });
|
|
|
|
|
const js = code => win.webContents.executeJavaScript(code).catch(error => { throw new Error(code + ': ' + error.message); });
|
|
|
|
|
let lastCommand = 'startup';
|
|
|
|
|
const watchdog = setTimeout(() => { console.error('Electron stalled at: ' + lastCommand); app.exit(1); }, 75000);
|
|
|
|
|
const js = code => { lastCommand = code; return win.webContents.executeJavaScript(code).catch(error => { throw new Error(code + ': ' + error.message); }); };
|
|
|
|
|
const settle = () => js('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))');
|
|
|
|
|
const screenshot = async name => {
|
|
|
|
|
if (process.env.GOODBUDDY_NOTES_SCREENSHOT_DIR) {
|
|
|
|
@@ -71,20 +73,74 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
|
|
|
|
const open = async () => { await wait('!!document.querySelector("[id^=magic-note-select-]")');
|
|
|
|
|
await click('[id^=magic-note-select-]'); await wait('!!document.querySelector(".magic-note-record")'); await settle(); };
|
|
|
|
|
await win.loadURL(${JSON.stringify(server.resolvedUrls!.local[0] + 'layout.html')});
|
|
|
|
|
await wait('!!document.querySelector(".magic-note-tag-filter")'); await js('document.fonts.ready'); await settle();
|
|
|
|
|
// Overview: the tag filter row fits the page and cards cap chips at three plus a count.
|
|
|
|
|
assert(await js('document.documentElement.scrollWidth <= innerWidth'));
|
|
|
|
|
assert.equal(await js('document.querySelectorAll("#magic-note-select-layout-note .magic-note-tag-chip").length'), 4);
|
|
|
|
|
assert.equal(await js('document.querySelector("#magic-note-select-layout-note .magic-note-tag-chip--more").textContent'), '+1');
|
|
|
|
|
const card = await rect('#magic-note-select-layout-note'), chips = await rect('#magic-note-select-layout-note .magic-note-tag-chips');
|
|
|
|
|
assert(chips.x + chips.width <= card.x + card.width + 0.5, 'Card chips stay inside the card');
|
|
|
|
|
await screenshot('magic-notes-overview-tags');
|
|
|
|
|
await click('#magic-note-tag-manager');
|
|
|
|
|
await wait('!!document.querySelector(".magic-note-tag-manager")');
|
|
|
|
|
await screenshot('magic-notes-tag-manager');
|
|
|
|
|
await key('.magic-note-tag-manager', 'Escape');
|
|
|
|
|
await wait('!document.querySelector(".magic-note-tag-manager")');
|
|
|
|
|
await open(); await js('document.fonts.ready'); win.focus(); win.webContents.focus();
|
|
|
|
|
assert.equal(await js('document.querySelectorAll(".magic-note-tag-editor .magic-note-tag-chip--removable").length'), 4);
|
|
|
|
|
assert.equal(await js('getComputedStyle(document.querySelector("#magic-note-tag-manager")).width'), '28px', 'Split list uses the compact icon');
|
|
|
|
|
// The sticky toolbar spans the full stream width even though content is capped at 880px.
|
|
|
|
|
const toolbarBox = await rect('.magic-note-detail-toolbar'), streamBox = await rect('.magic-notes-stream-pane');
|
|
|
|
|
const streamClient = await js('document.querySelector(".magic-notes-stream-pane").clientWidth');
|
|
|
|
|
assert(Math.abs(toolbarBox.x - streamBox.x) <= 1 && Math.abs(toolbarBox.width - streamClient) <= 1, 'Toolbar spans the stream: ' + JSON.stringify({ toolbarBox, streamBox, streamClient }));
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 280);
|
|
|
|
|
assert(await js('document.querySelector(".magic-notes-overview").checkVisibility()'));
|
|
|
|
|
await js('document.querySelector(".magic-notes-card-grid").scrollTop = 300; document.querySelector(".magic-notes-stream-pane").scrollTop = 420');
|
|
|
|
|
await wait('document.querySelector(".magic-notes-stream-pane").scrollTop === 420');
|
|
|
|
|
await settle();
|
|
|
|
|
await click('#magic-note-select-other-note-3');
|
|
|
|
|
await wait('document.querySelector(".magic-note-detail-title").value === "Other note 3"');
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-card-grid").scrollTop'), 300);
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-stream-pane").scrollTop'), 0);
|
|
|
|
|
await click('#magic-note-select-layout-note');
|
|
|
|
|
await wait('document.querySelector(".magic-notes-stream-pane").scrollTop === 420');
|
|
|
|
|
assert.equal(await js('document.querySelector("#magic-note-select-layout-note").getAttribute("aria-current")'), 'true');
|
|
|
|
|
await drag('.magic-notes-notes-resize-handle', 40);
|
|
|
|
|
const notesWidth = (await rect('.magic-notes-overview')).width;
|
|
|
|
|
assert(notesWidth >= 320 && notesWidth <= 325);
|
|
|
|
|
await key('.magic-notes-notes-resize-handle', 'Left');
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, notesWidth - 16);
|
|
|
|
|
await click('button[aria-controls="magic-library-panel-notes"]');
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 0);
|
|
|
|
|
await click('button[aria-controls="magic-library-panel-notes"]');
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, notesWidth - 16);
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-card-grid").scrollTop'), 300);
|
|
|
|
|
await key('.magic-notes-notes-resize-handle', 'Home');
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
|
|
|
|
await js('document.querySelector(".magic-notes-stream-pane").scrollTop = 0');
|
|
|
|
|
const editor = '.magic-note-composer .magic-note-editor__content .ql-editor';
|
|
|
|
|
const emptyHeight = (await rect(editor)).height;
|
|
|
|
|
const saveButton = '.magic-note-composer > footer > .primary-button';
|
|
|
|
|
// Idle composer is a single prompt line without toolbar or footer.
|
|
|
|
|
const idleHeight = (await rect('.magic-note-composer')).height;
|
|
|
|
|
assert(idleHeight <= 60, 'Idle composer height: ' + idleHeight);
|
|
|
|
|
assert.equal((await rect(saveButton)).width, 0);
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-ai-pane")?.checkVisibility() ?? false'), false, 'AI stays closed for notes without comments');
|
|
|
|
|
assert.equal((await rect('.magic-notes-index-pane')).width, 0, 'Record index starts collapsed');
|
|
|
|
|
const focusComposer = async () => { await js('document.querySelector(' + JSON.stringify(editor) + ').focus()'); await settle(); };
|
|
|
|
|
await focusComposer();
|
|
|
|
|
const emptyHeight = (await rect(editor)).height;
|
|
|
|
|
const desktopSaveWidth = (await rect(saveButton)).width;
|
|
|
|
|
assert(emptyHeight >= 110 && emptyHeight <= 125, 'Empty composer height: ' + emptyHeight);
|
|
|
|
|
assert.equal(await js('document.querySelector("#magic-notes-title").textContent'), 'Layout note');
|
|
|
|
|
assert.equal(await js('document.querySelector(".page-header__description")'), null);
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-note-detail-title").value'), 'Layout note');
|
|
|
|
|
assert.equal(await js('document.querySelector("#magic-notes-title").textContent'), 'Magic Notes');
|
|
|
|
|
await screenshot('magic-notes-light');
|
|
|
|
|
await js('document.documentElement.dataset.theme = "dark"'); await settle();
|
|
|
|
|
assert.equal((await rect(editor)).height, emptyHeight);
|
|
|
|
|
await screenshot('magic-notes-dark');
|
|
|
|
|
await js('document.documentElement.dataset.theme = "light"'); await settle();
|
|
|
|
|
const index = '.magic-notes-index-pane', handle = '.magic-notes-index-resize-handle', stream = '.magic-notes-stream-pane';
|
|
|
|
|
await click('#magic-notes-index-toggle');
|
|
|
|
|
await click('button[aria-controls="magic-notes-ai-pane"]');
|
|
|
|
|
assert.equal((await rect(index)).width, 168);
|
|
|
|
|
const firstRecord = await rect('.magic-note-record');
|
|
|
|
|
assert.equal(firstRecord.x, (await rect(index)).x, 'Thumbnails align with the left pane edge');
|
|
|
|
@@ -104,23 +160,50 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
|
|
|
|
const withoutAi = (await rect(index)).width; assert(withoutAi < 250 && withoutAi > 230);
|
|
|
|
|
await click('button[aria-controls="magic-notes-ai-pane"]'); assert.equal((await rect(index)).width, withoutAi);
|
|
|
|
|
await win.loadURL(${JSON.stringify(server.resolvedUrls!.local[0] + 'layout.html')}); await open(); assert.equal((await rect(index)).width, withoutAi);
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
|
|
|
|
await js('document.querySelector(".magic-notes-card-grid").scrollTop = 300');
|
|
|
|
|
win.setContentSize(1000, 720); await settle(); await settle();
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
|
|
|
|
assert.equal((await rect(index)).width, 0, 'Record drawer follows detail width, not the outer page');
|
|
|
|
|
const assertSideBySide = async () => {
|
|
|
|
|
const s = await rect(stream), ai = await rect('.magic-notes-ai-pane'), list = await rect('.magic-notes-overview');
|
|
|
|
|
assert(s.x >= list.x + list.width, 'Note stream must not overlap the notes list');
|
|
|
|
|
assert(ai.x >= s.x + s.width && ai.y === s.y, 'AI pane sits beside the note stream: ' + JSON.stringify({ s, ai }));
|
|
|
|
|
assert(s.width >= 300, 'Note stream keeps its minimum width: ' + s.width);
|
|
|
|
|
};
|
|
|
|
|
await assertSideBySide();
|
|
|
|
|
win.setContentSize(930, 720); await settle(); await settle();
|
|
|
|
|
await assertSideBySide();
|
|
|
|
|
win.setContentSize(1000, 720); await settle(); await settle();
|
|
|
|
|
assert(await js('document.documentElement.scrollWidth <= innerWidth'));
|
|
|
|
|
win.setContentSize(820, 720); await settle(); await settle();
|
|
|
|
|
assert((await rect(stream)).width >= 299);
|
|
|
|
|
const constrained = { index: (await rect(index)).width, editor: (await rect(stream)).width, ai: (await rect('.magic-notes-ai-pane')).width };
|
|
|
|
|
win.setContentSize(720, 640); await settle(); await settle();
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 0);
|
|
|
|
|
await click('#magic-notes-back');
|
|
|
|
|
assert(await js('document.querySelector(".magic-notes-overview").checkVisibility()'));
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-card-grid").scrollTop'), 300);
|
|
|
|
|
await open();
|
|
|
|
|
assert.equal((await rect(index)).width, 0);
|
|
|
|
|
await click('#magic-notes-index-toggle'); assert.equal((await rect(index)).width, 168);
|
|
|
|
|
assert.equal(await js('document.querySelector(".magic-notes-index-resize-handle")'), null);
|
|
|
|
|
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-ai-resize-handle")).display'), 'none');
|
|
|
|
|
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-ai-resize-handle")).display'), 'block');
|
|
|
|
|
assert((await rect('.magic-notes-ai-pane')).x >= (await rect(stream)).x + (await rect(stream)).width);
|
|
|
|
|
assert(await js('document.documentElement.scrollWidth <= innerWidth'));
|
|
|
|
|
const narrowStream = (await rect(stream)).width;
|
|
|
|
|
await click('#magic-notes-index-toggle'); assert.equal((await rect(stream)).width, narrowStream);
|
|
|
|
|
await focusComposer();
|
|
|
|
|
const narrowSave = await rect(saveButton), narrowFooter = await rect('.magic-note-composer > footer');
|
|
|
|
|
const footerPaddingRight = await js('parseFloat(getComputedStyle(document.querySelector(".magic-note-composer > footer")).paddingRight)');
|
|
|
|
|
assert(Math.abs(narrowSave.width - desktopSaveWidth) <= 1, 'Narrow Save button retains its content width: ' + JSON.stringify({ desktopSaveWidth, narrowSave, narrowFooter }));
|
|
|
|
|
assert(Math.abs(narrowFooter.x + narrowFooter.width - footerPaddingRight - narrowSave.x - narrowSave.width) <= 1, 'Narrow Save button stays right-aligned within footer padding');
|
|
|
|
|
await screenshot('magic-notes-narrow');
|
|
|
|
|
win.setContentSize(560, 640); await settle(); await settle();
|
|
|
|
|
assert((await rect('.magic-notes-ai-pane')).y >= (await rect(stream)).y + (await rect(stream)).height, 'AI pane stacks below the stream on very narrow widths');
|
|
|
|
|
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-ai-resize-handle")).display'), 'none');
|
|
|
|
|
win.setContentSize(1280, 800); await settle(); await settle();
|
|
|
|
|
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
|
|
|
|
assert.equal((await rect(index)).width, withoutAi);
|
|
|
|
|
await key('.magic-note-record', 'Tab');
|
|
|
|
|
await js('document.querySelector(".magic-note-record").focus()');
|
|
|
|
@@ -168,6 +251,7 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
|
|
|
|
await click('#magic-library-switch');
|
|
|
|
|
assert.equal((await rect('#magic-todo-list')).width, todoWidth);
|
|
|
|
|
fs.writeFileSync(${JSON.stringify(join(directory, 'result.json'))}, JSON.stringify({ emptyHeight, grownHeight, dragged, withoutAi, constrained, narrowStream, desktopSaveWidth, narrowSave, narrowFooter, footerPaddingRight, persisted: true }));
|
|
|
|
|
clearTimeout(watchdog);
|
|
|
|
|
app.exit(0);
|
|
|
|
|
}).catch(error => { console.error(error); app.exit(1); });
|
|
|
|
|
`)
|
|
|
|
|