fix(desktop): browser_logs reports the HTTP status of each request (#8956)

* fix(desktop): browser_logs reports the HTTP status of each request

* test(desktop): browser_logs reports the status the browser recorded

* test(desktop): e2e checks browser_logs reports a 404 and a 200

* docs(browser): browser_logs also reports the HTTP status
This commit is contained in:
Abdulhakeem Adetunji Mustapha
2026-11-07 01:00:56 +00:00
committed by GitHub
parent ec3ae09926
commit 78a089a14e
5 changed files with 153 additions and 2 deletions
@@ -1,4 +1,5 @@
import { resolve as resolvePath } from "node:path";
import { createContext, runInContext } from "node:vm";
import { describe, expect, test, vi } from "vitest";
import type {
@@ -434,6 +435,26 @@ function containsScript(tab: FakeTab, ...parts: string[]): boolean {
return tab.scripts.some((script) => parts.every((part) => script.includes(part)));
}
/**
* A browser whose tab runs the network script the host injects against `performance`, the way a
* page runs it, so the test sees what the script reads from Chromium's timing entries.
*/
function browserWithTimingEntries(entries: { resource: unknown[]; navigation: unknown[] }) {
const browser = new BrowserAutomationHarness();
const fakeAnswer = browser.tab.executeJavaScript.bind(browser.tab);
const page = createContext({
performance: {
getEntriesByType: (type: string) =>
type === "navigation" ? entries.navigation : entries.resource,
},
});
browser.tab.executeJavaScript = async (script: string) =>
script.includes("performance.getEntriesByType")
? runInContext(script, page)
: fakeAnswer(script);
return browser;
}
function requireSnapshotRefs(result: Awaited<ReturnType<BrowserAutomationHarness["snapshot"]>>) {
expect(result).toEqual({
requestId: "req-snapshot",
@@ -1406,6 +1427,72 @@ describe("executeAutomationCommand", () => {
});
});
test("logs reports the HTTP status the browser recorded, and none it did not", async () => {
const timing = (name: string, initiatorType: string, responseStatus?: number) => ({
name,
initiatorType,
startTime: 1,
duration: 2,
transferSize: 3,
...(responseStatus === undefined ? {} : { responseStatus }),
});
const browser = browserWithTimingEntries({
resource: [
timing("https://a.test/api/orders", "fetch", 404),
// Chromium reports 0 for a cross-origin response that sends no Timing-Allow-Origin.
timing("https://cdn.test/lib.js", "script", 0),
// A browser without the field.
timing("https://a.test/old.css", "css"),
],
navigation: [timing("https://a.test/form", "navigation", 200)],
});
const result = await browser.execute({ command: "logs", args: { browserId: BROWSER_A } });
expect(result).toEqual({
requestId: "req-logs",
ok: true,
result: {
command: "logs",
browserId: BROWSER_A,
console: [],
network: [
{
url: "https://a.test/api/orders",
type: "fetch",
status: 404,
startTime: 1,
duration: 2,
transferSize: 3,
},
{
url: "https://cdn.test/lib.js",
type: "script",
startTime: 1,
duration: 2,
transferSize: 3,
},
{
url: "https://a.test/old.css",
type: "css",
startTime: 1,
duration: 2,
transferSize: 3,
},
{
url: "https://a.test/form",
method: "GET",
type: "navigation",
status: 200,
startTime: 1,
duration: 2,
transferSize: 3,
},
],
},
});
});
test("evaluate returns primitive JSON from the page context", async () => {
const browser = new BrowserAutomationHarness();
browser.tab.evaluateScriptResult = { ok: true, resultJson: "42" };
@@ -1628,6 +1628,8 @@ const NETWORK_PERFORMANCE_SCRIPT = String.raw`(() => {
url: entry.name,
method: entry.initiatorType === 'navigation' ? 'GET' : undefined,
type: entry.initiatorType,
// 0 when the browser does not say (a cross-origin response without Timing-Allow-Origin).
status: typeof entry.responseStatus === 'number' && entry.responseStatus > 0 ? entry.responseStatus : undefined,
startTime: entry.startTime,
duration: entry.duration,
transferSize: typeof entry.transferSize === 'number' ? entry.transferSize : undefined,