diff --git a/e2e/check-appearance.mjs b/e2e/check-appearance.mjs index 59845ed..71482b8 100644 --- a/e2e/check-appearance.mjs +++ b/e2e/check-appearance.mjs @@ -26,10 +26,10 @@ async function login(user, password = 'password123', setup) { return page; } -// The attributes theme.js sets; absent means the built-in default (auto, standard, purple). +// The attributes theme.js sets; absent means the built-in default (auto, standard, normal, purple). const looks = (page) => page.evaluate(() => { const d = document.documentElement.dataset; - return `${d.mode ?? 'auto'} ${d.contrast ?? 'standard'} ${d.palette ?? 'purple'}`; + return `${d.mode ?? 'auto'} ${d.contrast ?? 'standard'} ${d.size ?? 'normal'} ${d.palette ?? 'purple'}`; }); async function expectLook(name, page, want) { @@ -78,8 +78,8 @@ await Promise.all(['jdoe', 'msmith', 'pweber'].map(resetUser)); // 1. Admin default on the Display settings tab const admin = await login('admin', 'admin'); await admin.goto(`${BASE}/settings?tab=display`); -// Mode, contrast and color are three form rows -const rowsShown = () => admin.$$eval('p:has([data-nm-default])', (rows) => rows.length === 3 && rows.map((r) => !!r.offsetParent)); +// Mode, contrast, size and color are four form rows +const rowsShown = () => admin.$$eval('p:has([data-nm-default])', (rows) => rows.length === 4 && rows.map((r) => !!r.offsetParent)); check('default picker shown while Tonalmine is selected', (await rowsShown())?.every(Boolean)); await admin.selectOption('#settings_ui_theme', ''); check('default picker hidden and not posted for another theme', (await rowsShown())?.every((shown) => !shown) && @@ -94,25 +94,29 @@ check('default picker shows the saved default', await admin.evaluate(() => // 2. A user without a choice gets the default const jdoe = await login('jdoe'); -await expectLook('user without a choice gets the default', jdoe, 'dark standard blue'); +await expectLook('user without a choice gets the default', jdoe, 'dark standard normal blue'); // 3. The user's choice is saved on the account and only overrides that part check('choosing Light is saved (204)', (await pick(jdoe, '[data-nm-mode="light"]')) === 204); +check('choosing Compact is saved (204)', (await pick(jdoe, '[data-nm-size="compact"]')) === 204); const jdoe2 = await login('jdoe'); -await expectLook('choice follows the user to another browser', jdoe2, 'light standard blue'); +await expectLook('choice follows the user to another browser', jdoe2, 'light standard compact blue'); -// 4. Parts the user didn't change follow a new default -await saveDefault(admin, () => admin.click('[data-nm-default] [data-nm-palette="green"]')); +// 4. Parts the user didn't change follow a new default; the user's Compact beats the default Large +await saveDefault(admin, async () => { + await admin.click('[data-nm-default] [data-nm-palette="green"]'); + await admin.click('[data-nm-default] [data-nm-size="large"]'); +}); await jdoe2.reload({ waitUntil: 'networkidle' }); -await expectLook('unchanged parts follow the new default', jdoe2, 'light standard green'); +await expectLook('unchanged parts follow the new default', jdoe2, 'light standard compact green'); // 5. Reset to default check('Reset to default is shown after a change', await jdoe2.isVisible('#account [data-nm-reset]') || await jdoe2.evaluate(() => !document.querySelector('#account [data-nm-reset]').hidden)); check('reset is saved (204)', (await pick(jdoe2, '[data-nm-reset]')) === 204); -await expectLook('reset returns to the default', jdoe2, 'dark standard green'); +await expectLook('reset returns to the default', jdoe2, 'dark standard large green'); await jdoe2.reload({ waitUntil: 'networkidle' }); -await expectLook('reset is kept after reload', jdoe2, 'dark standard green'); +await expectLook('reset is kept after reload', jdoe2, 'dark standard large green'); check('Reset to default is hidden without a choice', await jdoe2.evaluate(() => document.querySelector('#account [data-nm-reset]').hidden)); // 5b. A custom color, with its generated CSS, is saved on the account @@ -120,33 +124,33 @@ const savedColor = jdoe2.waitForResponse((r) => r.url().includes('/tonalmine/app await pickColor(jdoe2, '#account', '#c0392b'); check('custom color is saved (204)', (await savedColor).status() === 204); const jdoe3 = await login('jdoe'); -await expectLook('custom color follows the user', jdoe3, 'dark standard custom'); +await expectLook('custom color follows the user', jdoe3, 'dark standard large custom'); check('custom color CSS comes from the account', (await customCss(jdoe3)) > 1000); await pick(jdoe3, '[data-nm-reset]'); // 6. A localStorage choice from before the plugin moves to the account once const msmith = await login('msmith', 'password123', () => localStorage.setItem('tonalmine', '{"contrast":"high"}')); -await expectLook('localStorage choice is applied after login', msmith, 'dark high green'); +await expectLook('localStorage choice is applied after login', msmith, 'dark high large green'); check('localStorage is cleared for logged-in users', await msmith.evaluate(() => localStorage.getItem('tonalmine') === null)); const msmith2 = await login('msmith'); -await expectLook('uploaded choice is on the account', msmith2, 'dark high green'); +await expectLook('uploaded choice is on the account', msmith2, 'dark high large green'); // 7. Validation; the request without a CSRF token goes last, since Redmine then ends the session const pweber = await login('pweber'); check('valid choice is saved (204)', (await put(pweber, { contrast: 'medium' })) === 204); -for (const body of [{ mode: 'neon' }, { palette: 'custom' }, { palette: 'custom', seed: '#123456', css: '' }, { foo: 1 }, { mode: 5 }]) { +for (const body of [{ mode: 'neon' }, { palette: 'custom' }, { palette: 'custom', seed: '#123456', css: '' }, { size: 'huge' }, { foo: 1 }, { mode: 5 }]) { const status = await put(pweber, body); check(`invalid ${JSON.stringify(body)} is refused (422)`, status === 422, `got ${status}`); } const tokenless = await put(pweber, { contrast: 'high' }, false); check('request without CSRF token is refused (422)', tokenless === 422, `got ${tokenless}`); const pweber2 = await login('pweber'); -await expectLook('refused requests left the choice unchanged', pweber2, 'dark medium green'); +await expectLook('refused requests left the choice unchanged', pweber2, 'dark medium large green'); // 8. Anonymous visitors get the default const anonymous = await (await browser.newContext()).newPage(); await anonymous.goto(`${BASE}/login`); -await expectLook('anonymous visitor gets the default', anonymous, 'dark standard green'); +await expectLook('anonymous visitor gets the default', anonymous, 'dark standard large green'); // 8b. A custom default color reaches everyone await saveDefault(admin, async () => { @@ -154,13 +158,13 @@ await saveDefault(admin, async () => { await admin.waitForFunction(() => document.querySelector('[data-nm-default-input="css"]').value.length > 1000); }); await anonymous.reload(); -await expectLook('custom default reaches anonymous visitors', anonymous, 'dark standard custom'); +await expectLook('custom default reaches anonymous visitors', anonymous, 'dark standard large custom'); check('custom default CSS is rendered', (await customCss(anonymous)) > 1000); // 9. Clean up: empty default, so other checks and screenshots see the built-in look await saveDefault(admin, () => admin.$$eval('[data-nm-default-input]', (inputs) => inputs.forEach((i) => { i.value = ''; }))); await anonymous.reload(); -await expectLook('empty default restores the built-in look', anonymous, 'auto standard purple'); +await expectLook('empty default restores the built-in look', anonymous, 'auto standard normal purple'); await Promise.all(['jdoe', 'msmith', 'pweber'].map(resetUser)); await browser.close();