diff --git a/packages/cli/src/commands/validate.test.ts b/packages/cli/src/commands/validate.test.ts
index 797c8e105e..0c60e1036d 100644
--- a/packages/cli/src/commands/validate.test.ts
+++ b/packages/cli/src/commands/validate.test.ts
@@ -189,6 +189,13 @@ describe("shouldIgnoreRequestFailure", () => {
}
});
+ it("keeps an aborted image request reportable whatever its file suffix", () => {
+ for (const ext of ["mkv", "mp4", "png"]) {
+ const url = `http://127.0.0.1:3000/assets/plate.${ext}`;
+ expect(shouldIgnoreRequestFailure(url, "net::ERR_ABORTED", "image")).toBe(false);
+ }
+ });
+
it("keeps non-media and non-aborted failures reportable", () => {
expect(
shouldIgnoreRequestFailure("http://127.0.0.1:3000/assets/map.png", "net::ERR_ABORTED"),
diff --git a/packages/cli/src/commands/validate.ts b/packages/cli/src/commands/validate.ts
index 0c63ac58bb..21afa80b3b 100644
--- a/packages/cli/src/commands/validate.ts
+++ b/packages/cli/src/commands/validate.ts
@@ -51,7 +51,7 @@ interface ContrastEntry {
const CONTRAST_SAMPLES = 5;
const SEEK_SETTLE_MS = 150;
const PREFERRED_SEEK_TARGET_WAIT_MS = 500;
-// Containers a proxy swap can abandon mid-fetch; an aborted fetch of one is not a load failure.
+// Media files a proxy swap can abandon mid-fetch; an aborted non-image request for one is not a load failure.
const MEDIA_EXTENSIONS =
/\.(3gp|aac|aif|aiff|avi|flac|flv|m2ts|m4a|m4v|mkv|mov|mp3|mp4|mpeg|mpg|mts|mxf|oga|ogg|ogv|opus|wav|weba|webm|wma|wmv)$/i;
// Turn Puppeteer's opaque "Navigation timeout of Nms exceeded" into an
@@ -76,6 +76,7 @@ export function shouldIgnoreRequestFailure(
if (errorText === "net::ERR_ABORTED" && isOptionalCaptionOverridesRequest(url)) return true;
if (errorText !== "net::ERR_ABORTED") return false;
if (resourceType === "media") return true;
+ if (resourceType === "image") return false;
try {
return MEDIA_EXTENSIONS.test(new URL(url).pathname);
} catch {
diff --git a/packages/cli/src/utils/checkBrowser.test.ts b/packages/cli/src/utils/checkBrowser.test.ts
index 9dcca61359..3dbd1146f8 100644
--- a/packages/cli/src/utils/checkBrowser.test.ts
+++ b/packages/cli/src/utils/checkBrowser.test.ts
@@ -1174,6 +1174,37 @@ describe("keepBrokenImageAborts", () => {
]);
});
+ it("reports a still-broken aborted image even when its URL ends in a video suffix", async () => {
+ const base = "http://127.0.0.1:3000/assets";
+ mountCanvasFixture(`
`);
+ (document.getElementById("stuck") as HTMLImageElement).decode = () =>
+ Promise.reject(new Error("broken"));
+ const aborted = (url: string, resourceType: string) => ({
+ url: () => url,
+ failure: () => ({ errorText: "net::ERR_ABORTED" }),
+ resourceType: () => resourceType,
+ });
+ const page = fakePage();
+ page.on = vi.fn((event: string, handler: (request: ReturnType) => void) => {
+ if (event !== "requestfailed") return;
+ handler(aborted(`${base}/plate.mkv`, "image"));
+ handler(aborted(`${base}/clip.mkv`, "fetch"));
+ });
+ installSessionMock(page);
+
+ const result = await runBrowserCheck(
+ PROJECT,
+ { ...DEFAULT_CHECK_OPTIONS, samples: 1, contrast: false },
+ { kind: "none" },
+ runAuditGrid,
+ );
+
+ const failed = result.runtimeFindings.filter((finding) => finding.code === "request_failed");
+ expect(failed.map((finding) => finding.message)).toEqual([
+ "Failed to load assets/plate.mkv: net::ERR_ABORTED",
+ ]);
+ });
+
it("does not report an aborted image whose reload is still pending at the cap", async () => {
const url = "http://127.0.0.1:3000/assets/slow.png";
mountCanvasFixture(`
`);