test: size in the appearance end-to-end check (T7)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 09:24:04 +02:00
co-authored by Claude Opus 5.5
parent 94a9bd066e
commit 77ff80e60b
+23 -19
View File
@@ -26,10 +26,10 @@ async function login(user, password = 'password123', setup) {
return page; return page;
} }
// The <html> attributes theme.js sets; absent means the built-in default (auto, standard, purple). // The <html> attributes theme.js sets; absent means the built-in default (auto, standard, normal, purple).
const looks = (page) => page.evaluate(() => { const looks = (page) => page.evaluate(() => {
const d = document.documentElement.dataset; 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) { 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 // 1. Admin default on the Display settings tab
const admin = await login('admin', 'admin'); const admin = await login('admin', 'admin');
await admin.goto(`${BASE}/settings?tab=display`); await admin.goto(`${BASE}/settings?tab=display`);
// Mode, contrast and color are three form rows // Mode, contrast, size and color are four form rows
const rowsShown = () => admin.$$eval('p:has([data-nm-default])', (rows) => rows.length === 3 && rows.map((r) => !!r.offsetParent)); 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)); check('default picker shown while Tonalmine is selected', (await rowsShown())?.every(Boolean));
await admin.selectOption('#settings_ui_theme', ''); await admin.selectOption('#settings_ui_theme', '');
check('default picker hidden and not posted for another theme', (await rowsShown())?.every((shown) => !shown) && 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 // 2. A user without a choice gets the default
const jdoe = await login('jdoe'); 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 // 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 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'); 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 // 4. Parts the user didn't change follow a new default; the user's Compact beats the default Large
await saveDefault(admin, () => admin.click('[data-nm-default] [data-nm-palette="green"]')); 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 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 // 5. Reset to default
check('Reset to default is shown after a change', await jdoe2.isVisible('#account [data-nm-reset]') || 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)); await jdoe2.evaluate(() => !document.querySelector('#account [data-nm-reset]').hidden));
check('reset is saved (204)', (await pick(jdoe2, '[data-nm-reset]')) === 204); 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 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)); 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 // 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'); await pickColor(jdoe2, '#account', '#c0392b');
check('custom color is saved (204)', (await savedColor).status() === 204); check('custom color is saved (204)', (await savedColor).status() === 204);
const jdoe3 = await login('jdoe'); 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); check('custom color CSS comes from the account', (await customCss(jdoe3)) > 1000);
await pick(jdoe3, '[data-nm-reset]'); await pick(jdoe3, '[data-nm-reset]');
// 6. A localStorage choice from before the plugin moves to the account once // 6. A localStorage choice from before the plugin moves to the account once
const msmith = await login('msmith', 'password123', () => localStorage.setItem('tonalmine', '{"contrast":"high"}')); 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)); check('localStorage is cleared for logged-in users', await msmith.evaluate(() => localStorage.getItem('tonalmine') === null));
const msmith2 = await login('msmith'); 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 // 7. Validation; the request without a CSRF token goes last, since Redmine then ends the session
const pweber = await login('pweber'); const pweber = await login('pweber');
check('valid choice is saved (204)', (await put(pweber, { contrast: 'medium' })) === 204); 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: '</style>' }, { foo: 1 }, { mode: 5 }]) { for (const body of [{ mode: 'neon' }, { palette: 'custom' }, { palette: 'custom', seed: '#123456', css: '</style>' }, { size: 'huge' }, { foo: 1 }, { mode: 5 }]) {
const status = await put(pweber, body); const status = await put(pweber, body);
check(`invalid ${JSON.stringify(body)} is refused (422)`, status === 422, `got ${status}`); check(`invalid ${JSON.stringify(body)} is refused (422)`, status === 422, `got ${status}`);
} }
const tokenless = await put(pweber, { contrast: 'high' }, false); const tokenless = await put(pweber, { contrast: 'high' }, false);
check('request without CSRF token is refused (422)', tokenless === 422, `got ${tokenless}`); check('request without CSRF token is refused (422)', tokenless === 422, `got ${tokenless}`);
const pweber2 = await login('pweber'); 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 // 8. Anonymous visitors get the default
const anonymous = await (await browser.newContext()).newPage(); const anonymous = await (await browser.newContext()).newPage();
await anonymous.goto(`${BASE}/login`); 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 // 8b. A custom default color reaches everyone
await saveDefault(admin, async () => { 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 admin.waitForFunction(() => document.querySelector('[data-nm-default-input="css"]').value.length > 1000);
}); });
await anonymous.reload(); 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); 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 // 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 saveDefault(admin, () => admin.$$eval('[data-nm-default-input]', (inputs) => inputs.forEach((i) => { i.value = ''; })));
await anonymous.reload(); 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 Promise.all(['jdoe', 'msmith', 'pweber'].map(resetUser));
await browser.close(); await browser.close();