Files
iptvnator/apps/electron-backend-e2e/src/window-zoom-level.e2e.ts
T

147 lines
5.4 KiB
TypeScript

import {
closeElectronApp,
expect,
launchElectronApp,
LaunchedElectronApp,
openSources,
restartElectronApp,
test,
} from './electron-test-fixtures';
import {
expectRendererReloadedOnRoute,
reloadFromMainProcess,
} from './renderer-reload.support';
/**
* Chromium's zoom factor as the renderer actually renders it: the window's
* content width in device-independent pixels over the CSS pixels the page
* sees. Independent of `getZoomLevel()`, which under file:// reports the
* per-URL entry and can disagree with what is on screen.
*/
async function renderedZoomFactor(app: LaunchedElectronApp): Promise<number> {
const contentWidth = await app.electronApp.evaluate(({ BrowserWindow }) => {
const [win] = BrowserWindow.getAllWindows();
return win.getContentSize()[0];
});
const innerWidth = await app.mainWindow.evaluate(() => window.innerWidth);
return contentWidth / innerWidth;
}
/**
* The app zoom shortcuts as the renderer receives them: Cmd on macOS, Ctrl
* elsewhere. Dispatched through CDP they reach the renderer's keydown
* binding directly (`WorkspaceKeyboardShortcutsService`), which steps the
* frame-bound level through the preload bridge; the macOS application menu
* is not in this path.
*/
const ZOOM_MODIFIER = process.platform === 'darwin' ? 'Meta' : 'Control';
async function pressZoomShortcut(
app: LaunchedElectronApp,
key: 'Equal' | 'Minus' | 'Digit0' | 'NumpadAdd' | 'NumpadSubtract',
times = 1
): Promise<void> {
for (let index = 0; index < times; index += 1) {
await app.mainWindow.keyboard.press(`${ZOOM_MODIFIER}+${key}`);
}
}
/**
* A cross-document navigation of the renderer (what a reload is for zoom:
* Chromium drops the temporary level and the new document's preload must
* restore it). A real reload of the routed `file://` URL: the main process
* recovers the missing file by re-loading the index on the same route
* (`renderer-reload.e2e.ts`).
*/
async function reloadRenderer(app: LaunchedElectronApp): Promise<void> {
await reloadFromMainProcess(app);
await expectRendererReloadedOnRoute(
app.mainWindow,
/\/workspace\/sources$/
);
}
async function resizeWindowBy(
app: LaunchedElectronApp,
delta: number
): Promise<void> {
await app.electronApp.evaluate(({ BrowserWindow }, step) => {
const [win] = BrowserWindow.getAllWindows();
const [width, height] = win.getSize();
win.setSize(width + step, height + step);
}, delta);
}
// Zoom level 1 is a 1.2 factor; each shortcut press steps the level by 0.5
// (Electron's zoomIn/zoomOut role step). The packaged renderer runs under
// file:// with path routing, where Chromium keys zoom by full URL: without
// frame-bound (temporary) zoom the level "holds" only until the next resize
// after a section change, and a restart brings back the default (issue #1109).
const ZOOMED_FACTOR = 1.2;
const HALF_STEP_FACTOR = Math.sqrt(ZOOMED_FACTOR);
test('@electron @window keeps the zoom level across sections, resizes and a restart', async ({
dataDir,
}) => {
let app = await launchElectronApp(dataDir);
try {
expect(await renderedZoomFactor(app)).toBeCloseTo(1, 1);
// In three times, out once (main keys and numpad): level 1.
await pressZoomShortcut(app, 'Equal', 2);
await pressZoomShortcut(app, 'NumpadAdd');
await expect
.poll(() => renderedZoomFactor(app))
.toBeCloseTo(ZOOMED_FACTOR * HALF_STEP_FACTOR, 1);
await pressZoomShortcut(app, 'Minus');
await expect
.poll(() => renderedZoomFactor(app))
.toBeCloseTo(ZOOMED_FACTOR, 1);
// Reset returns to level 0, then zoom back in (three in, one out on
// the numpad) for the persistence checks below.
await pressZoomShortcut(app, 'Digit0');
await expect.poll(() => renderedZoomFactor(app)).toBeCloseTo(1, 1);
await pressZoomShortcut(app, 'Equal', 3);
await pressZoomShortcut(app, 'NumpadSubtract');
await expect
.poll(() => renderedZoomFactor(app))
.toBeCloseTo(ZOOMED_FACTOR, 1);
// A route change (new file:// URL) followed by the visual-properties
// sync a resize forces: per-URL zoom snaps back to 1 here.
await openSources(app.mainWindow);
await resizeWindowBy(app, 24);
await app.mainWindow.waitForTimeout(500);
expect(await renderedZoomFactor(app)).toBeCloseTo(ZOOMED_FACTOR, 1);
// A reload rebuilds the document; the level must be saved before it
// and re-applied by the new document's preload.
await reloadRenderer(app);
await expect
.poll(() => renderedZoomFactor(app))
.toBeCloseTo(ZOOMED_FACTOR, 1);
} finally {
app = await restartElectronApp(app, dataDir);
}
try {
await expect
.poll(() => renderedZoomFactor(app))
.toBeCloseTo(ZOOMED_FACTOR, 1);
expect(
await app.electronApp.evaluate(({ BrowserWindow }) =>
BrowserWindow.getAllWindows()[0].webContents.getZoomLevel()
)
).toBe(1);
await openSources(app.mainWindow);
await resizeWindowBy(app, -24);
await app.mainWindow.waitForTimeout(500);
expect(await renderedZoomFactor(app)).toBeCloseTo(ZOOMED_FACTOR, 1);
} finally {
await closeElectronApp(app);
}
});