/** @fileoverview Real Chromium coverage for COD-358 rail resize and label geometry. */ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { chromium, type Browser, type Page } from 'playwright'; const publicDir = resolve(import.meta.dirname, '../src/web/public'); const styles = readFileSync(resolve(publicDir, 'styles.css'), 'utf8'); const constants = readFileSync(resolve(publicDir, 'constants.js'), 'utf8'); const settingsUi = readFileSync(resolve(publicDir, 'settings-ui.js'), 'utf8'); const controller = readFileSync(resolve(publicDir, 'tab-rail-resize.js'), 'utf8'); describe('COD-358 vertical rail in Chromium', () => { let browser: Browser; let page: Page; beforeAll(async () => { browser = await chromium.launch({ headless: true }); page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); await page.setContent(`
`); await page.addScriptTag({ content: constants }); await page.addScriptTag({ content: 'class CodemanApp {}; window.CodemanApp = CodemanApp;' }); await page.addScriptTag({ content: settingsUi }); await page.addScriptTag({ content: controller }); await page.evaluate(() => { const app = new (window as any).CodemanApp(); const settings = { tabOrientation: 'vertical', tabRailWidth: 256 }; (window as any).__settings = settings; (window as any).__deviceType = 'desktop'; (window as any).MobileDetection = { getDeviceType: () => (window as any).__deviceType }; app.activeSessionId = 'session-1'; app.loadAppSettingsFromStorage = () => settings; app.saveAppSettingsToStorage = (next: Record) => Object.assign(settings, next); app.getDefaultSettings = () => ({}); app.isSessionSidebarActive = () => false; app.updateTabOverflowMode = () => undefined; app._fullRenderSessionTabs = () => undefined; app._updateConnectionLinesImmediate = () => undefined; app.fitAddon = { proposeDimensions: () => ({ cols: 80, rows: 24 }), fit: () => undefined }; app.sendResize = async () => ((window as any).__resizeCount = ((window as any).__resizeCount || 0) + 1); app.initTabRailResize(); (window as any).app = app; }); }); afterAll(async () => browser.close()); it('shows the complete label while clamping it to two lines', async () => { const metrics = await page.locator('.tab-name').evaluate((element) => { const style = getComputedStyle(element); return { text: element.textContent, clamp: style.getPropertyValue('-webkit-line-clamp'), whiteSpace: style.whiteSpace, overflowWrap: style.overflowWrap, height: element.getBoundingClientRect().height, lineHeight: Number.parseFloat(style.lineHeight), }; }); expect(metrics.text).toBe('w2-codeman: super-long-unbroken-session-name-for-wrapping'); expect(metrics.clamp).toBe('2'); expect(metrics.whiteSpace).toBe('normal'); expect(metrics.overflowWrap).toBe('anywhere'); expect(metrics.height).toBeLessThanOrEqual(metrics.lineHeight * 2 + 1); }); it('resizes from the keyboard and settles once', async () => { const handle = page.locator('#tabRailResizeHandle'); await handle.focus(); await handle.press('ArrowRight'); expect(await handle.getAttribute('aria-valuenow')).toBe('264'); await page.waitForTimeout(180); expect(await page.evaluate(() => (window as any).__resizeCount)).toBe(1); }); it('preserves 360px while a narrow viewport applies only an effective clamp', async () => { await page.setViewportSize({ width: 700, height: 800 }); const narrow = await page.evaluate(() => { (window as any).__settings.tabRailWidth = 360; return { effective: (window as any).app.applyTabRailWidth(), preferred: (window as any).__settings.tabRailWidth }; }); expect(narrow).toEqual({ effective: 280, preferred: 360 }); await page.setViewportSize({ width: 1280, height: 800 }); expect(await page.evaluate(() => (window as any).app.applyTabRailWidth())).toBe(360); }); it('reapplies phone fallback in both directions without changing preferences', async () => { await page.evaluate(() => { (window as any).__settings.tabRailWidth = 360; (window as any).__deviceType = 'mobile'; }); await page.setViewportSize({ width: 390, height: 800 }); await page.waitForTimeout(130); expect(await page.locator('#sessionTabs').evaluate((node) => node.parentElement?.id)).toBe('sessionTabsHost'); expect(await page.evaluate(() => (window as any).__settings)).toEqual({ tabOrientation: 'vertical', tabRailWidth: 360, }); await page.evaluate(() => ((window as any).__deviceType = 'desktop')); await page.setViewportSize({ width: 1280, height: 800 }); await page.waitForTimeout(130); expect(await page.locator('#sessionTabs').evaluate((node) => node.parentElement?.id)).toBe('tabRail'); expect(await page.evaluate(() => (window as any).app._getCurrentTabRailWidth())).toBe(360); }); });