Files
iptvnator/apps/electron-backend-e2e/src/sidebar-width.e2e.ts
T
4grayandClaude Opus 5.5 7bd7ff5ac7 fix(ui): start a sidebar drag from its CSS width, not its border box (#1856)
ResizableDirective started a drag from offsetWidth, which includes
padding and border, but writes the result to style.width. The hosts
are content-box, so the first move widened a padded host by its
padding plus border: 23px on the workspace categories panel, 1px on
the Favorites sidebar. Each drag also saved a width that much larger.

A drag now starts from the computed CSS width (the box style.width
sizes), falling back to the directive's own width when that is not a
length. Border-box hosts (live-layout and groups rails) are unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:09:41 +02:00

90 lines
3.2 KiB
TypeScript

import { Locator, Page } from '@playwright/test';
import {
addXtreamPortal,
closeElectronApp,
expect,
launchElectronApp,
openSettings,
openWorkspaceSection,
resetMockServers,
test,
waitForXtreamWorkspaceReady,
} from './electron-test-fixtures';
/**
* The workspace context panels share one stored width but clamp it to their
* own limits. Opening a narrower panel used to save its clamped width, so a
* categories panel widened past Settings' 400px maximum came back at 400.
* The drag also lands on 520 only if the padded panel does not jump wider
* when the drag starts.
*/
const SHARED_WIDTH_KEY = 'sidebar-width';
/** The CSS width the directive sets; the panels are content-box. */
function renderedWidth(panel: Locator): Promise<number> {
return panel.evaluate((el) => parseFloat(getComputedStyle(el).width));
}
function storedWidth(page: Page): Promise<string | null> {
return page.evaluate((key) => localStorage.getItem(key), SHARED_WIDTH_KEY);
}
async function dragPanelTo(
page: Page,
panel: Locator,
width: number
): Promise<void> {
const handle = panel.locator(':scope > .resize-handle');
const box = await handle.boundingBox();
if (!box) throw new Error('The panel has no resize handle');
const startX = Math.round(box.x + box.width / 2);
const y = Math.round(box.y + box.height / 2);
// A drag starts from the CSS width, so the panel's padding and border
// must not count toward the distance.
const delta = width - (await renderedWidth(panel));
await page.mouse.move(startX, y);
await page.mouse.down();
await page.mouse.move(startX + delta, y, { steps: 10 });
await page.mouse.up();
}
test.describe('Electron shared sidebar width', () => {
test('@persistence @electron keeps a widened categories panel after Settings clamps it', async ({
dataDir,
request,
}) => {
await resetMockServers(request, ['xtream']);
const app = await launchElectronApp(dataDir);
try {
const page = app.mainWindow;
await page.setViewportSize({ width: 1440, height: 900 });
await addXtreamPortal(page);
await openWorkspaceSection(page, 'Movies');
await waitForXtreamWorkspaceReady(page);
const categories = page.locator('aside.context-panel--route');
await expect(categories).toBeVisible();
const categoriesUrl = page.url();
await dragPanelTo(page, categories, 520);
await expect.poll(() => renderedWidth(categories)).toBe(520);
await expect.poll(() => storedWidth(page)).toBe('520');
await openSettings(page);
const settings = page.locator('aside.context-panel--settings');
await expect(settings).toBeVisible();
await expect.poll(() => renderedWidth(settings)).toBe(400);
expect(await storedWidth(page)).toBe('520');
await page.goBack();
await page.waitForURL(categoriesUrl);
await expect(categories).toBeVisible();
await expect.poll(() => renderedWidth(categories)).toBe(520);
expect(await storedWidth(page)).toBe('520');
} finally {
await closeElectronApp(app);
}
});
});