From 5738abe3ef3f6f0b51a81a8443e098fce9497627 Mon Sep 17 00:00:00 2001 From: surtic86 Date: Thu, 15 Oct 2026 09:24:04 +0200 Subject: [PATCH] test: check-sizes measures text and control heights per size mode (T8) Co-Authored-By: Claude Opus 5.5 --- e2e/check-sizes.mjs | 51 +++++++++++++++++++++++++++++++++++++++++++++ e2e/size.mjs | 10 +++++++++ 2 files changed, 61 insertions(+) create mode 100644 e2e/check-sizes.mjs create mode 100644 e2e/size.mjs diff --git a/e2e/check-sizes.mjs b/e2e/check-sizes.mjs new file mode 100644 index 0000000..93b1b39 --- /dev/null +++ b/e2e/check-sizes.mjs @@ -0,0 +1,51 @@ +// Checks the size modes (Compact / Normal / Large): body text and the heights of a table row, a +// text field, the submit button and (on phones) a mobile menu link. Sets the size on the admin +// account, so it needs the redmine_tonalmine plugin (dev/docker-compose.yml); resets it when done. +// Usage: node check-sizes.mjs (exits 1 on any failure) +import { chromium } from 'playwright'; +import { setSize } from './size.mjs'; + +const BASE = process.env.REDMINE_URL ?? 'http://127.0.0.1:3070'; +// size → [body text, fields / buttons / phone table rows, mobile menu links] +const SIZES = { compact: ['13px', 32, 40], normal: ['14px', 40, 48], large: ['16px', 48, 56] }; +const browser = await chromium.launch(); +let failures = 0; + +function check(name, got, want) { + const ok = got === want; + if (!ok) failures++; + console.log(`${ok ? 'ok ' : 'FAIL'} ${name}${ok ? '' : `\n expected ${want}, got ${got}`}`); +} + +const height = (page, selector) => page.$eval(selector, (el) => Math.round(el.getBoundingClientRect().height)); + +for (const [width, mobile] of [[1440, false], [390, true]]) { + const page = await browser.newPage({ viewport: { width, height: 900 } }); + await page.goto(`${BASE}/login`); + await page.fill('#username', 'admin'); + await page.fill('#password', 'admin'); + await page.click('#login-submit'); + await page.waitForURL((url) => !url.pathname.startsWith('/login')); + + for (const [size, [text, control, menuLink]] of Object.entries(SIZES)) { + await setSize(page, size); + const at = `${size} ${width}`; + await page.goto(`${BASE}/projects/apollo/issues`, { waitUntil: 'networkidle' }); + check(`${at} body text`, await page.$eval('body', (el) => getComputedStyle(el).fontSize), text); + // On desktop the cells' 4px padding comes on top of their block-size + check(`${at} issue row`, await height(page, 'table.list.issues tbody tr:first-child'), control + (mobile ? 0 : 8)); + if (mobile) { + await page.click('.mobile-toggle-button'); + await page.waitForSelector('.flyout-menu .js-project-menu a.overview', { state: 'visible' }); + check(`${at} mobile menu link`, await height(page, '.flyout-menu .js-project-menu a.overview'), menuLink); + } + await page.goto(`${BASE}/projects/apollo/issues/new`, { waitUntil: 'networkidle' }); + check(`${at} text field`, await height(page, '#issue_subject'), control); + check(`${at} submit button`, await height(page, 'input[name="commit"]'), control); + } + await setSize(page); + await page.close(); +} + +await browser.close(); +process.exit(failures ? 1 : 0); diff --git a/e2e/size.mjs b/e2e/size.mjs new file mode 100644 index 0000000..f7d5880 --- /dev/null +++ b/e2e/size.mjs @@ -0,0 +1,10 @@ +// Saves a size mode on the logged-in user's account (needs the redmine_tonalmine plugin). +// No size resets the account to the default appearance. +export async function setSize(page, size) { + const status = await page.evaluate(async (body) => { + const headers = { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }; + return (await fetch('/tonalmine/appearance', { method: 'PUT', headers, body: JSON.stringify(body) })).status; + }, size ? { size } : {}); + if (status !== 204) throw new Error(`could not save size ${size ?? 'default'} (HTTP ${status})`); +}