Skip to content

Commit 6253eec

Browse files
committed
Guard the title bar drag region with an e2e test
The title bar renders inside its own WebContentsView and window dragging depends on the app-region CSS staying put: a drag surface on the title and a no-drag opt-out on every control. A refactor that drops either rule breaks window moving or the buttons with no signal from the type checker or the unit suite. Pin the computed declarations so that regression fails loudly. The real OS window move cannot be driven from Playwright since synthetic mouse events do not trigger the app-region hit-test, so this stays a CSS guard and the actual drag plus the layered-view no-drag behavior remain manual per OS. The focus fixes in this branch (welcome view focus in Welcome mode, background window not stealing focus) resist a deterministic e2e assertion too: the app never becomes OS-frontmost under the test harness, so webContents.isFocused() always reads false and any assertion would be tautological. Those stay manual and are written up in the PR. I worked these tests and the harness probing out with Claude Code and confirmed the guard fails when the drag rule is removed.
1 parent d5d92cc commit 6253eec

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { expect, test } from '@playwright/test';
2+
import { cleanup, launchApp } from './helpers';
3+
4+
// The title bar is drawn inside its own WebContentsView. Window dragging relies
5+
// on `-webkit-app-region: drag` on the `.app-title` element, and every control
6+
// button opts back out with `-webkit-app-region: no-drag`. Dropping either rule
7+
// in a refactor silently breaks window moving or makes the buttons undraggable.
8+
//
9+
// This is only a guard on the computed CSS declaration. It does NOT exercise a
10+
// real window move: synthetic Playwright/CDP mouse events do not drive the OS
11+
// drag on the app-region hit-test, so an actual drag cannot be asserted here.
12+
// The real window move, and the layered-WebContentsView no-drag hit-testing
13+
// (electron/electron#43320), still have to be verified by hand per OS.
14+
test('the title bar keeps its drag region and no-drag controls', async () => {
15+
const { app, userDataDir, jupyterDir } = await launchApp();
16+
try {
17+
// Arrange: find the titlebar page. It has no window title, so it is located
18+
// by the presence of its `#app-title` element, the same element-probe
19+
// pattern the dialog-titlebar test uses.
20+
const deadline = Date.now() + 20_000;
21+
let titlebar;
22+
while (!titlebar && Date.now() < deadline) {
23+
for (const page of app.windows()) {
24+
const hasTitle = await page
25+
.evaluate(() => !!document.getElementById('app-title'))
26+
.catch(() => false);
27+
if (hasTitle) {
28+
titlebar = page;
29+
break;
30+
}
31+
}
32+
if (!titlebar) {
33+
await new Promise(resolve => setTimeout(resolve, 200));
34+
}
35+
}
36+
if (!titlebar) {
37+
throw new Error('title bar page with #app-title never appeared');
38+
}
39+
await titlebar.waitForLoadState('load');
40+
41+
// Act: read the computed app-region of the drag surface and of two control
42+
// buttons. The JS-side property is `webkitAppRegion`; its value is the
43+
// keyword ('drag' / 'no-drag'), not a length.
44+
const regions = await titlebar.evaluate(() => {
45+
const regionOf = (id: string) => {
46+
const el = document.getElementById(id);
47+
if (!el) {
48+
return null;
49+
}
50+
return (getComputedStyle(el) as CSSStyleDeclaration & {
51+
webkitAppRegion?: string;
52+
}).webkitAppRegion;
53+
};
54+
return {
55+
appTitle: regionOf('app-title'),
56+
closeButton: regionOf('close-button'),
57+
serverButton: regionOf('server-button')
58+
};
59+
});
60+
61+
// Assert: the title is draggable and the controls explicitly opt out. If a
62+
// refactor drops the `-webkit-app-region` rules these flip to the inherited
63+
// default ('none') and the test fails.
64+
expect(regions.appTitle).toBe('drag');
65+
expect(regions.closeButton).toBe('no-drag');
66+
expect(regions.serverButton).toBe('no-drag');
67+
} finally {
68+
await app.close();
69+
cleanup(userDataDir, jupyterDir);
70+
}
71+
});

0 commit comments

Comments
 (0)