mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Merge candidate 20260908T070012Z-web-product
Change-source: pulse-maintainer
This commit is contained in:
@@ -137,3 +137,30 @@ not merely a larger timeout or successful CDP acknowledgements. No Pulse fixture
|
||||
installed convergence, destination receipt or release qualification was exercised.
|
||||
Complete output is retained in maintainer run `20260908T065015Z-web-product`,
|
||||
`headed-window-control.log`. Earlier failed controls remain relevant.
|
||||
|
||||
## Native tab-switch control — 8 September 2026
|
||||
|
||||
Run the same owned Xvfb command with `--headed-tab` instead of
|
||||
`--headed-window`. This does not request minimisation or require a window
|
||||
manager: it creates a second owned blank tab and activates it. Before freezing,
|
||||
it requires observed hidden → visible/focused → hidden states, each bounded to
|
||||
ten seconds with 250ms samples. It then uses the existing 2100ms host-side frozen
|
||||
wait, resumes while backgrounded, and activates the original tab. Ordered
|
||||
freeze/resume with unchanged ticks, a subsequent visible event, final focus and
|
||||
timer progress remain required. No synthetic visibility events or forced focus
|
||||
are used; no Playwright page session is attached.
|
||||
|
||||
One run exited 0 on the same Chrome revision, Playwright and Node versions above,
|
||||
owned display :99. Preflight recorded hidden at tick 5, visible/focused at tick
|
||||
11, then hidden at tick 12. Freeze and resume both recorded tick 12.
|
||||
Post-resume remained hidden/unfocused at tick 12; tab activation produced a
|
||||
visible event and visible/focused state at tick 18. Complete runtime identities,
|
||||
intervention commands/responses and samples are retained in maintainer run
|
||||
`20260908T070012Z-web-product/headed-tab-control.log`.
|
||||
|
||||
This establishes the positive control for the **tab-switch** mechanism, not
|
||||
window minimisation. Earlier adverse results and the default mode's unreliable
|
||||
250ms hidden-timer sample remain unchanged. No Pulse fixture or installed-pair
|
||||
acceptance ran. The next source-level convergence fixture must retain raw
|
||||
single-session ownership, the genuine background preflight, and the suspension
|
||||
probe rather than attach a Playwright page and assume equivalent behaviour.
|
||||
|
||||
@@ -6,7 +6,8 @@ import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { chromium } from '@playwright/test';
|
||||
const headedWindow = process.argv.includes('--headed-window');
|
||||
const headedTab = process.argv.includes('--headed-tab');
|
||||
const headedWindow = headedTab || process.argv.includes('--headed-window');
|
||||
const checkForeground = headedWindow || process.argv.includes('--foreground');
|
||||
if (headedWindow && !process.env.DISPLAY) throw new Error('--headed-window requires an owned X display');
|
||||
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
@@ -50,7 +51,7 @@ try {
|
||||
console.log(JSON.stringify({ browser: await send('Browser.getVersion'),
|
||||
playwright: require('@playwright/test/package.json').version, node: process.version,
|
||||
platform: process.platform, arch: process.arch, executable: chromium.executablePath(),
|
||||
headedWindow, display: process.env.DISPLAY, observationBoundMs: headedWindow ? 10000 : 250 }));
|
||||
headedWindow, headedTab, display: process.env.DISPLAY, observationBoundMs: headedWindow ? 10000 : 250 }));
|
||||
const results = [];
|
||||
// Preselected negative (focus forced) and positive (no forced focus), fresh targets.
|
||||
for (const focusEnabled of (headedWindow ? [false] : [true, false])) {
|
||||
@@ -85,7 +86,28 @@ try {
|
||||
const snapshot = `({...window.probe, visibility: document.visibilityState, focus: document.hasFocus()})`;
|
||||
await wait(250);
|
||||
const before = await evaluate(snapshot);
|
||||
if (headedWindow) {
|
||||
let otherTarget;
|
||||
const observe = async (stage, visibility) => {
|
||||
const deadline = Date.now() + 10000;
|
||||
let sample;
|
||||
do {
|
||||
await wait(250);
|
||||
sample = await evaluate(snapshot);
|
||||
console.log(JSON.stringify({ stage, snapshot: sample }));
|
||||
if (sample.visibility === visibility && (visibility !== 'visible' || sample.focus)) return sample;
|
||||
} while (Date.now() < deadline);
|
||||
throw new Error(stage + ': native visibility transition not observed');
|
||||
};
|
||||
if (headedTab) {
|
||||
assert.ok(before.visibility === 'visible' && before.focus, 'Tab baseline must be active');
|
||||
otherTarget = (await windowCommand('Target.createTarget', { url: 'about:blank' })).targetId;
|
||||
await windowCommand('Target.activateTarget', { targetId: otherTarget });
|
||||
await observe('tab-background-preflight', 'hidden');
|
||||
await windowCommand('Target.activateTarget', { targetId });
|
||||
await observe('tab-foreground-preflight', 'visible');
|
||||
await windowCommand('Target.activateTarget', { targetId: otherTarget });
|
||||
await observe('tab-background-before-freeze', 'hidden');
|
||||
} else if (headedWindow) {
|
||||
assert.ok(before.visibility === 'visible' && before.focus, 'Headed baseline must be visible and focused');
|
||||
await windowCommand('Browser.setWindowBounds', { windowId: windowInfo.windowId, bounds: { windowState: 'minimized' } });
|
||||
await wait(250);
|
||||
@@ -103,7 +125,9 @@ try {
|
||||
// Resume is not foreground activation. Observe the two transitions separately.
|
||||
let foreground;
|
||||
if (checkForeground) {
|
||||
if (headedWindow) {
|
||||
if (headedTab) {
|
||||
await windowCommand('Target.activateTarget', { targetId });
|
||||
} else if (headedWindow) {
|
||||
await windowCommand('Browser.setWindowBounds', { windowId: windowInfo.windowId, bounds: { windowState: 'normal' } });
|
||||
}
|
||||
await command('Page.bringToFront', {});
|
||||
@@ -117,6 +141,7 @@ try {
|
||||
}
|
||||
const result = { focusEnabled, before, after, foreground, commands, suspended };
|
||||
results.push(result); console.log(JSON.stringify(result));
|
||||
if (otherTarget) await send('Target.closeTarget', { targetId: otherTarget });
|
||||
} finally { await send('Target.closeTarget', { targetId }); }
|
||||
}
|
||||
if (!headedWindow) assert.ok(!results[0].suspended && results[0].after.ticks - results[0].before.ticks >= 20,
|
||||
|
||||
Reference in New Issue
Block a user