diff --git a/config/scripts/mobile-web-app-preview-image-evidence.mjs b/config/scripts/mobile-web-app-preview-image-evidence.mjs index 223101e0b31..3a623856f09 100644 --- a/config/scripts/mobile-web-app-preview-image-evidence.mjs +++ b/config/scripts/mobile-web-app-preview-image-evidence.mjs @@ -61,6 +61,21 @@ async function describeImageEvidence(page, frame, { originPrefix, requestLog, pa // Every subresource this document actually fetched, from the document's own side. An entry // here for a URL the rig never saw would mean the request left the frame and died before it. resources: performance.getEntriesByType('resource').map((one) => one.name), + // The same entry in full for the element under test. A zero `responseStatus` with a zero + // `transferSize` is a fetch that reached the network stack and came back with no response, + // which is what a request the rig never intercepted looks like once the host cannot resolve; + // `startTime` is what an attachment time is early or late against. + remoteTiming: performance + .getEntriesByType('resource') + .filter((one) => one.name === remote?.src) + .map((one) => ({ + responseStatus: one.responseStatus ?? null, + transferSize: one.transferSize, + encodedBodySize: one.encodedBodySize, + nextHopProtocol: one.nextHopProtocol, + startTime: Math.round(one.startTime), + duration: Math.round(one.duration) + })), navigations: performance.getEntriesByType('navigation').map((one) => one.type), remote: remote ? { diff --git a/config/scripts/mobile-web-app-preview-request-log.mjs b/config/scripts/mobile-web-app-preview-request-log.mjs index 5ee208d7774..77464c6fa26 100644 --- a/config/scripts/mobile-web-app-preview-request-log.mjs +++ b/config/scripts/mobile-web-app-preview-request-log.mjs @@ -27,9 +27,15 @@ export async function recordRequestsTo(page, originPrefix) { page.on('request', (request) => { if (request.url().startsWith(originPrefix)) { - asked.push(request.url()) + asked.push({ url: request.url(), at: Math.round(performance.now()) }) } }) + // When this page first had any frame at all, so an attachment time has something to be early or + // late against. + const frameAttached = [] + page.on('frameattached', (frame) => { + frameAttached.push({ url: frame.url(), at: Math.round(performance.now()) }) + }) page.on('requestfailed', (request) => { if (request.url().startsWith(originPrefix)) { failed.push({ url: request.url(), errorText: request.failure()?.errorText ?? null }) @@ -47,7 +53,13 @@ export async function recordRequestsTo(page, originPrefix) { // Playwright did record. Flattened auto-attach puts each child target on this same connection, // and `Network.enable` on the child is what makes its requests visible here. cdp.on('Target.attachedToTarget', (event) => { - attached.push({ type: event.targetInfo?.type ?? null, url: event.targetInfo?.url ?? null }) + // The moment, not just the fact: a request whose resource-timing entry starts before this was + // issued by a frame nothing was listening to yet, which is a different bug from a refusal. + attached.push({ + type: event.targetInfo?.type ?? null, + url: event.targetInfo?.url ?? null, + at: Math.round(performance.now()) + }) cdp.send('Network.enable', {}, event.sessionId).catch(() => {}) }) await cdp @@ -83,8 +95,8 @@ export async function recordRequestsTo(page, originPrefix) { } return { - asked: () => [...asked], + asked: () => asked.map((one) => one.url), describe: () => - `asked ${JSON.stringify(asked)}; failed ${JSON.stringify(failed)}; cdp ${cdp ? 'on' : 'off'} attached ${JSON.stringify(attached)} sent ${JSON.stringify(sent)}; cdp loadingFailed ${JSON.stringify(loadingFailed)}` + `asked ${JSON.stringify(asked)}; failed ${JSON.stringify(failed)}; frameAttached ${JSON.stringify(frameAttached)}; cdp ${cdp ? 'on' : 'off'} attached ${JSON.stringify(attached)} sent ${JSON.stringify(sent)}; cdp loadingFailed ${JSON.stringify(loadingFailed)}` } }