mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
116 lines
6.1 KiB
TypeScript
116 lines
6.1 KiB
TypeScript
/** @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(`<!doctype html><html data-tab-orientation="vertical"><head><style>${styles}</style></head>
|
|
<body><header class="header"><div id="sessionTabsHost"></div></header><main class="main" style="width:100%;height:700px">
|
|
<aside class="tab-rail" id="tabRail"><div class="session-tabs" id="sessionTabs"><div class="session-tab active">
|
|
<span class="tab-info"><span class="tab-name-row"><span class="tab-name"><span class="tab-name-prefix">w2-codeman: </span>super-long-unbroken-session-name-for-wrapping</span></span></span>
|
|
</div></div><div id="tabRailResizeHandle" class="tab-rail-resize-handle" role="separator" tabindex="0" aria-valuemin="208" aria-valuemax="360" aria-valuenow="256"></div></aside>
|
|
<div class="terminal-wrap"><iframe title="terminal"></iframe></div></main>
|
|
<div id="tabRailResizeShield" class="tab-rail-resize-shield" hidden></div>
|
|
<select id="appSettingsTabRailWidth"><option value="custom">Custom</option><option value="208">Compact</option><option value="256">Default</option><option value="320">Wide</option><option value="360">Maximum</option></select>
|
|
</body></html>`);
|
|
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<string, unknown>) => 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);
|
|
});
|
|
});
|