import type { Locator, Page, } from '@playwright/test'; /** * Typography settings menu — desktop layout exposes inline ComboControls with * increase/decrease buttons. The current value is encoded in the trigger * button's aria-label as `${controlLabel}: ${value}` (e.g. "Size: 24"). */ export type TypographyControl = 'size' | 'weight' | 'leading' | 'tracking'; const LABELS: Record = { size: { increase: 'Increase Font Size', decrease: 'Decrease Font Size', trigger: 'Size', }, weight: { increase: 'Increase Font Weight', decrease: 'Decrease Font Weight', trigger: 'Weight', }, leading: { increase: 'Increase Line Height', decrease: 'Decrease Line Height', trigger: 'Leading', }, tracking: { increase: 'Increase Letter Spacing', decrease: 'Decrease Letter Spacing', trigger: 'Tracking', }, }; export class TypographyMenu { constructor(private readonly page: Page) {} increase(control: TypographyControl): Locator { return this.page.getByRole('button', { name: LABELS[control].increase }); } decrease(control: TypographyControl): Locator { return this.page.getByRole('button', { name: LABELS[control].decrease }); } /** * Trigger button whose aria-label encodes the current value, e.g. "Size: 24". */ trigger(control: TypographyControl): Locator { return this.page.getByRole('button', { name: new RegExp(`^${LABELS[control].trigger}:\\s`) }); } /** * Parse the numeric value out of the trigger button's aria-label. * Returns null if the label can't be read yet. */ async readValue(control: TypographyControl): Promise { const label = await this.trigger(control).getAttribute('aria-label'); if (!label) { return null; } const match = label.match(/:\s*(-?\d+(?:\.\d+)?)/); return match ? Number(match[1]) : null; } async bump(control: TypographyControl, direction: 'up' | 'down', times = 1) { const button = direction === 'up' ? this.increase(control) : this.decrease(control); for (let i = 0; i < times; i++) { await button.click(); } } }