Skip to content

Commit d40223d

Browse files
committed
test(e2e): Keep the bfcache reason tests working on Chromium 151+
Two assumptions in the bfcache app stopped holding on newer Chromium, and both traced back to `unload`. An unload listener still makes a page ineligible, but Chrome no longer hands out the `unload-listener` reason for it, only the privacy-masked one. The test waited for both, so it hung on a reason that never arrives. It now asserts the masked reason, which is what the browser actually reports and which older versions reported too. The child-frame test used an unload listener inside the iframe purely as a blocker, so it lost its reason as well. It blocks with a held-up IndexedDB version upgrade instead, mirroring the top-level botcher, which keeps the child-frame classification covered.
1 parent 7e77c82 commit d40223d

3 files changed

Lines changed: 37 additions & 23 deletions

File tree

dev-packages/e2e-tests/test-applications/browser-bfcache/iframe.html

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,23 @@
44
<meta charset="UTF-8" />
55
<title>BFCache E2E - iframe</title>
66
<script>
7-
// A same-origin child frame. With ?blocker=unload it registers an unload listener, which makes
8-
// the *whole* embedding page bfcache-ineligible - the not-restored reason then comes from this
9-
// child frame (frame: 'child').
10-
if (new URLSearchParams(window.location.search).get('blocker') === 'unload') {
11-
window.addEventListener('unload', () => {});
7+
// A same-origin child frame. With ?blocker=indexeddb it holds up an IndexedDB version upgrade,
8+
// which makes the *whole* embedding page bfcache-ineligible - the not-restored reason then
9+
// comes from this child frame (frame: 'child'). Mirrors the top-level `indexeddb` botcher.
10+
if (new URLSearchParams(window.location.search).get('blocker') === 'indexeddb') {
11+
var dbName = 'bf_child_' + Math.random().toString(36).slice(2);
12+
window.__idbBlocked = false;
13+
var open1 = indexedDB.open(dbName, 1);
14+
open1.addEventListener('upgradeneeded', function (event) {
15+
event.target.result.createObjectStore('s');
16+
});
17+
open1.addEventListener('success', function (event) {
18+
window.__db = event.target.result; // intentionally no `versionchange` handler
19+
var open2 = indexedDB.open(dbName, 2);
20+
open2.addEventListener('blocked', function () {
21+
window.__idbBlocked = true;
22+
});
23+
});
1224
}
1325
</script>
1426
</head>

dev-packages/e2e-tests/test-applications/browser-bfcache/src/main.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ window.addEventListener(
3535
const botch = new URLSearchParams(window.location.search).get('botch');
3636

3737
if (botch === 'unload') {
38-
// An `unload` listener is the canonical, version-stable bfcache blocker.
38+
// An `unload` listener still makes the page ineligible, though the reason Chrome reports for it
39+
// has changed across versions.
3940
window.addEventListener('unload', () => {});
4041
}
4142

@@ -79,11 +80,11 @@ if (botch === 'indexeddb') {
7980
});
8081
}
8182

82-
if (botch === 'iframe-clean' || botch === 'iframe-unload') {
83-
// Embed a same-origin child frame. A clean child keeps the top page eligible (hit); a child with an
84-
// unload listener makes the whole top page ineligible, and the reason comes from the child frame.
83+
if (botch === 'iframe-clean' || botch === 'iframe-blocked') {
84+
// Embed a same-origin child frame. A clean child keeps the top page eligible (hit); a child that
85+
// blocks makes the whole top page ineligible, and the reason comes from the child frame.
8586
const iframe = document.createElement('iframe');
86-
iframe.src = botch === 'iframe-unload' ? '/iframe.html?blocker=unload' : '/iframe.html';
87+
iframe.src = botch === 'iframe-blocked' ? '/iframe.html?blocker=indexeddb' : '/iframe.html';
8788
const w = window as unknown as { __iframeLoaded?: boolean };
8889
w.__iframeLoaded = false;
8990
iframe.addEventListener('load', () => {

dev-packages/e2e-tests/test-applications/browser-bfcache/tests/bfcache.test.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,9 @@ test('reports a hit on a genuine back/forward-cache restore', async ({ page }) =
4545

4646
test('reports a miss with notRestoredReasons when an unload listener blocks bfcache', async ({ page }) => {
4747
const missPromise = waitForMetric(PROXY_SERVER_NAME, metric => isNavigation(metric, 'miss'));
48-
const unloadReasonPromise = waitForMetric(
49-
PROXY_SERVER_NAME,
50-
metric =>
51-
metric.name === 'browser.bfcache.not_restored' && attr(metric, 'browser.bfcache.reason') === 'unload-listener',
52-
);
53-
// Chrome reports a privacy-masked reason alongside the real one. It's a top-frame reason here, so
54-
// the integration must frame it positionally (`top`) and pass the value through untouched.
48+
// An unload listener still makes the page ineligible, but from Chromium 151 on the only reason
49+
// Chrome hands out for it is the privacy-masked one. It's a top-frame reason, so the integration
50+
// must frame it positionally (`top`) and pass the value through untouched.
5551
const maskedReasonPromise = waitForMetric(
5652
PROXY_SERVER_NAME,
5753
metric => metric.name === 'browser.bfcache.not_restored' && attr(metric, 'browser.bfcache.reason') === 'masked',
@@ -82,12 +78,9 @@ test('reports a miss with notRestoredReasons when an unload listener blocks bfca
8278
expect(attr(miss, 'browser.bfcache.not_restored_reason_count')).toBeGreaterThanOrEqual(1);
8379
expect(attr(miss, 'sentry.origin')).toBe(BFCACHE_ORIGIN);
8480

85-
const unloadReason = await unloadReasonPromise;
86-
expect(attr(unloadReason, 'browser.bfcache.frame')).toBe('top');
87-
expect(attr(unloadReason, 'sentry.origin')).toBe(BFCACHE_ORIGIN);
88-
8981
const maskedReason = await maskedReasonPromise;
9082
expect(attr(maskedReason, 'browser.bfcache.frame')).toBe('top');
83+
expect(attr(maskedReason, 'sentry.origin')).toBe(BFCACHE_ORIGIN);
9184

9285
const reloadDuration = await reloadDurationPromise;
9386
expect(reloadDuration.type).toBe('distribution');
@@ -218,15 +211,23 @@ test('reports a child-frame reason when an ineligible iframe blocks the top page
218211
PROXY_SERVER_NAME,
219212
metric =>
220213
metric.name === 'browser.bfcache.not_restored' &&
221-
attr(metric, 'browser.bfcache.reason') === 'unload-listener' &&
214+
attr(metric, 'browser.bfcache.reason') === 'idbversionchangeevent' &&
222215
attr(metric, 'browser.bfcache.frame') === 'child',
223216
);
224217

225-
await page.goto('/?botch=iframe-unload');
218+
await page.goto('/?botch=iframe-blocked');
226219
await page.waitForFunction(() => document.title === 'BFCache E2E - Page 1');
227220
await page.waitForFunction(() => (window as unknown as { __iframeLoaded?: boolean }).__iframeLoaded === true, {
228221
timeout: 5000,
229222
});
223+
// Only proceed once the child frame's version upgrade is actually blocked.
224+
await page.waitForFunction(
225+
() => {
226+
const frame = document.querySelector('iframe') as HTMLIFrameElement | null;
227+
return (frame?.contentWindow as unknown as { __idbBlocked?: boolean } | undefined)?.__idbBlocked === true;
228+
},
229+
{ timeout: 5000 },
230+
);
230231

231232
await page.click('#to-page-2');
232233
await page.waitForFunction(() => document.title === 'BFCache E2E - Page 2');

0 commit comments

Comments
 (0)