Skip to content

Commit e7b4d2b

Browse files
authored
Merge pull request #14 from CodeThicket/feat/pong-includes-visibility-state
feat(worker): pong carries visibilityState (unblocks lifecycle e2e #6)
2 parents ccf5df0 + 021f5e4 commit e7b4d2b

4 files changed

Lines changed: 64 additions & 9 deletions

File tree

e2e/multi-tab.spec.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,49 @@ test.describe('TabMesh — multi-tab harness', () => {
247247
await b.close();
248248
});
249249

250-
test.fixme('worker-side observation of lifecycle messages (#6)', async () => {
251-
// SharedWorker console output is not visible to Playwright's page-
252-
// attached console listener. Either:
253-
// (a) route worker logs over a `port.ping`-style introspection that
254-
// returns the recorded visibility state, or
255-
// (b) use chrome://inspect/#workers via CDP to attach to the SW.
250+
test('worker records lifecycle messages and replies via pong (#6)', async ({ context }) => {
251+
// Open the playground, drive a visibilitychange so the tab posts a
252+
// `lifecycle` message to the worker, then probe the worker by opening
253+
// a fresh SharedWorker port (same name) and sending a `ping` with the
254+
// playground tab's id. The pong now carries `visibilityState`, which
255+
// proves the lifecycle message reached the worker registry.
256+
const a = await newPlaygroundTab(context);
257+
await waitForTransportConnected(a);
258+
259+
const playgroundTabId = await a.evaluate(() =>
260+
sessionStorage.getItem('tabmesh:tabId:playground-todos')
261+
);
262+
expect(playgroundTabId).toBeTruthy();
263+
264+
await a.evaluate(() => {
265+
Object.defineProperty(document, 'visibilityState', {
266+
value: 'hidden',
267+
configurable: true,
268+
});
269+
document.dispatchEvent(new Event('visibilitychange'));
270+
});
271+
272+
// Give the lifecycle message a tick to land in the worker.
273+
await a.waitForTimeout(150);
274+
275+
const visibility = await a.evaluate((targetTabId) => {
276+
return new Promise<string | undefined>((resolve) => {
277+
const w = new SharedWorker('/tabmesh-worker.js', {
278+
name: 'tabmesh:playground-todos',
279+
});
280+
w.port.onmessage = (e) => {
281+
const msg = e.data as { kind?: string; visibilityState?: string };
282+
if (msg?.kind === 'pong') resolve(msg.visibilityState);
283+
};
284+
w.port.start();
285+
w.port.postMessage({ kind: 'ping', tabId: targetTabId });
286+
setTimeout(() => resolve(undefined), 1500);
287+
});
288+
}, playgroundTabId);
289+
290+
expect(visibility).toBe('hidden');
291+
292+
await a.close();
256293
});
257294

258295
test.fixme('Service Worker Background Sync drains pending events (#26 / #27)', async () => {

packages/core/src/types.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,17 @@ export type HubMessage =
174174
| { kind: 'clear-outbox-ack' }
175175
| { kind: 'broadcast-event'; event: TabMeshEvent }
176176
| { kind: 'ping'; tabId: string }
177-
| { kind: 'pong'; tabId: string }
177+
| {
178+
kind: 'pong';
179+
tabId: string;
180+
/**
181+
* Worker's recorded visibility state for `tabId`, or undefined if
182+
* the tab is not in the registry. Populated by the SharedWorker
183+
* hub; useful for tests verifying that lifecycle messages reached
184+
* the worker.
185+
*/
186+
visibilityState?: TabVisibilityState;
187+
}
178188
| { kind: 'lifecycle'; tabId: string; state: TabVisibilityState }
179189
| { kind: 'leader-elected'; tabId: string; term: number }
180190
| { kind: 'system-event'; event: TabMeshEvent }

packages/core/src/worker/tabmesh-worker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,11 @@ function handlePing(port: MessagePort, msg: Extract<HubMessage, { kind: 'ping' }
405405
if (entry) {
406406
entry.lastSeenAt = Date.now();
407407
}
408-
const pong: HubMessage = { kind: 'pong', tabId: msg.tabId };
408+
const pong: HubMessage = {
409+
kind: 'pong',
410+
tabId: msg.tabId,
411+
visibilityState: entry?.visibilityState,
412+
};
409413
port.postMessage(pong);
410414
}
411415

packages/playground/public/tabmesh-worker.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,11 @@
444444
if (entry) {
445445
entry.lastSeenAt = Date.now();
446446
}
447-
const pong = { kind: "pong", tabId: msg.tabId };
447+
const pong = {
448+
kind: "pong",
449+
tabId: msg.tabId,
450+
visibilityState: entry?.visibilityState
451+
};
448452
port.postMessage(pong);
449453
}
450454
function handleLifecycle(msg) {

0 commit comments

Comments
 (0)