mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
test(web): retain failing browser suspension control diagnostic
The previous fresh-session focus intervention did not suspend timers. Preserve a bounded true-to-false comparison and its adverse result so acknowledged CDP commands cannot be mistaken for lifecycle or Pulse recovery proof. This diagnostic is deliberately outside CI and release qualification. Change-source: pulse-maintainer
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# Browser suspension diagnostic
|
||||
|
||||
Run `npm ci --ignore-scripts --no-audit --no-fund` at repository root, then
|
||||
`pulse-heavy-run -- node scripts/check-browser-lifecycle-control.mjs` using an
|
||||
installed Playwright Chromium. This uses owned blank pages only, no Pulse server
|
||||
or credentials. It is deliberately not wired into CI or release qualification.
|
||||
|
||||
The comparison is fresh-session focus false versus true then false, each in a
|
||||
new context. Host-side waits avoid evaluating the frozen page. Success requires
|
||||
ordered freeze/resume events with identical timer counts and subsequent timer
|
||||
progress, not merely acknowledged protocol commands. Exit 1 means the
|
||||
intervention did not establish the control; it does not mean Pulse is broken.
|
||||
|
||||
On 8 September 2026, Chromium 141.0.7390.37 / Playwright 1.56.1 / Node 24.20.0
|
||||
returned successful commands for both cases, but each advanced from 5 to 52
|
||||
ticks and remained visible/focused without lifecycle events. The intervention
|
||||
failed. No Pulse application was loaded and no incident-convergence assertion
|
||||
was exercised.
|
||||
|
||||
The hypothesis came from the version-matched Chromium
|
||||
[emulation agent source](https://raw.githubusercontent.com/chromium/chromium/141.0.7390.37/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc):
|
||||
the equality guard in `setFocusEmulationEnabled` can skip updating the page on
|
||||
a fresh false request. Avoiding that guard did not establish suspension in
|
||||
this experiment. Do not continue treating that guard as a sufficient diagnosis.
|
||||
The next investigation must establish actual page visibility/lifecycle control
|
||||
under the automation harness before attempting Pulse recovery acceptance.
|
||||
@@ -0,0 +1,61 @@
|
||||
// Diagnostic only: proves a blank-page suspension control, not Pulse recovery.
|
||||
// Run through pulse-heavy-run -- node scripts/check-browser-lifecycle-control.mjs
|
||||
import assert from 'node:assert/strict';
|
||||
import { createRequire } from 'node:module';
|
||||
import { chromium } from '@playwright/test';
|
||||
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
console.log(JSON.stringify({ browser: browser.version(),
|
||||
playwright: require('@playwright/test/package.json').version,
|
||||
node: process.version, platform: process.platform, arch: process.arch }));
|
||||
const results = [];
|
||||
// Preselected comparison; independent contexts prevent carry-over.
|
||||
for (const transition of [false, true]) {
|
||||
const context = await browser.newContext();
|
||||
try {
|
||||
const page = await context.newPage();
|
||||
await page.evaluate(() => {
|
||||
window.probe = { ticks: 0, events: [] };
|
||||
setInterval(() => window.probe.ticks++, 50);
|
||||
for (const type of ['freeze', 'resume', 'visibilitychange']) {
|
||||
document.addEventListener(type, () => window.probe.events.push({
|
||||
type, ticks: window.probe.ticks, visibility: document.visibilityState,
|
||||
}));
|
||||
}
|
||||
});
|
||||
const session = await context.newCDPSession(page);
|
||||
const commands = [];
|
||||
const send = async (method, params) => {
|
||||
commands.push({ method, params, response: await session.send(method, params) });
|
||||
};
|
||||
if (transition) await send('Emulation.setFocusEmulationEnabled', { enabled: true });
|
||||
await send('Emulation.setFocusEmulationEnabled', { enabled: false });
|
||||
await wait(250);
|
||||
const before = await page.evaluate(() => ({ ...window.probe,
|
||||
visibility: document.visibilityState, focus: document.hasFocus() }));
|
||||
await send('Page.setWebLifecycleState', { state: 'frozen' });
|
||||
// Do not evaluate the page while frozen. Wait on the host instead.
|
||||
await wait(2100);
|
||||
await send('Page.setWebLifecycleState', { state: 'active' });
|
||||
await wait(250);
|
||||
const after = await page.evaluate(() => ({ ...window.probe,
|
||||
visibility: document.visibilityState, focus: document.hasFocus() }));
|
||||
const freeze = after.events.find((event) => event.type === 'freeze');
|
||||
const resume = after.events.find((event) => event.type === 'resume');
|
||||
const valid = Boolean(freeze && resume && freeze.ticks === resume.ticks &&
|
||||
after.events.indexOf(freeze) < after.events.indexOf(resume) &&
|
||||
after.ticks > resume.ticks);
|
||||
const result = { transition, before, after, commands, valid };
|
||||
results.push(result);
|
||||
console.log(JSON.stringify(result));
|
||||
} finally {
|
||||
await context.close();
|
||||
}
|
||||
}
|
||||
assert.ok(results[1].valid, 'true-to-false intervention did not establish suspension');
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
Reference in New Issue
Block a user