Skip to content

Commit fef8a8a

Browse files
authored
fix(visual): stop silently dropping mobile screenshots of long pages (#9790)
MAX_SCREENSHOT_HEIGHT was 10000, derived for the 1440-wide DESKTOP viewport -- MAX_SCREENSHOT_PIXELS is literally 1440 x 10000. Reusing that height bound for the 390-wide MOBILE viewport rejected captures costing a third as much: an ordinary long docs page renders ~10850px tall at 390 wide, which is 4.2M pixels against a 14.4M budget. The shot was discarded and the function returned null. On the ORB that was 64 mobile screenshots dropped in a single hour, weakening the visual gate on exactly the viewport most likely to show a responsive regression. The renderer was never the constraint. Verified against this deployment's own browserless (v2 / Chrome 149): a 390 x 20000 full-page capture returns 200 in ~157KB. Raise the height bound to 20000 and let MAX_SCREENSHOT_PIXELS remain the real cost ceiling, which judges a narrow-tall page by what it actually costs. Desktop is unchanged: 1440 x 10847 is 15.6M pixels and still fails the pixel cap.
1 parent c368d7e commit fef8a8a

2 files changed

Lines changed: 47 additions & 3 deletions

File tree

src/review/visual/shot.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,23 @@ type ScreenshotPage = {
6868
export const DESKTOP_VIEWPORT: Viewport = { width: 1440, height: 900 };
6969
export const MOBILE_VIEWPORT: Viewport = { width: 390, height: 844 }; // iPhone-class portrait
7070
const VIEWPORT = DESKTOP_VIEWPORT;
71-
export const MAX_SCREENSHOT_HEIGHT = 10000;
72-
export const MAX_SCREENSHOT_PIXELS = 14_400_000; // 1440 × 10000, matching the full-page cap.
71+
// A sanity bound against a pathological infinite-scroll page, NOT a cost proxy -- cost is bounded by
72+
// MAX_SCREENSHOT_PIXELS below, and separately by MAX_SCREENSHOT_BYTES.
73+
//
74+
// It used to be 10000, which was derived for the 1440-wide DESKTOP viewport (see the pixel cap's own
75+
// comment: 1440 × 10000). Applied unchanged to the 390-wide MOBILE viewport it rejected captures costing a
76+
// third as much: an ordinary long docs page renders ~10850px tall at 390 wide, which is 4.2M pixels against
77+
// a 14.4M budget -- comfortably affordable, silently dropped. On the ORB that was 64 mobile screenshots
78+
// discarded in a single hour, weakening the visual gate precisely on the viewport most likely to reveal a
79+
// responsive regression.
80+
//
81+
// 20000 is empirically verified against this deployment's own renderer (browserless v2 / Chrome 149):
82+
// a 390 × 20000 full-page capture returns 200 in ~157KB. The renderer was never the binding constraint.
83+
// Desktop is unaffected -- 1440 × 20000 is 28.8M pixels and still fails the pixel cap.
84+
export const MAX_SCREENSHOT_HEIGHT = 20000;
85+
// The real cost ceiling: width × height, so a narrow-tall page is judged by what it actually costs to
86+
// render rather than by height alone.
87+
export const MAX_SCREENSHOT_PIXELS = 14_400_000; // 1440 × 10000 — one full desktop-width page.
7388
export const MAX_SCREENSHOT_BYTES = 5 * 1024 * 1024;
7489
const SCREENSHOT_TIMEOUT_MS = 10000;
7590
const SCREENSHOT_HEIGHT_PROBE_TIMEOUT_MS = 2_000;

test/unit/visual-shot.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
2-
import { captureInteractionFrames, captureScrollFrames, captureShot, handleShot } from "../../src/review/visual/shot";
2+
import { captureInteractionFrames, captureScrollFrames, captureShot, handleShot, MAX_SCREENSHOT_HEIGHT, MAX_SCREENSHOT_PIXELS, MOBILE_VIEWPORT, DESKTOP_VIEWPORT } from "../../src/review/visual/shot";
33
import { counterValue, resetMetrics } from "../../src/selfhost/metrics";
44

55
const mocks = vi.hoisted(() => ({
@@ -1113,3 +1113,32 @@ describe("visual capture result metric (#9487)", () => {
11131113
});
11141114

11151115
});
1116+
1117+
describe("a narrow-tall mobile page is judged by cost, not height alone", () => {
1118+
// Observed on the ORB: 64 mobile captures discarded in one hour, every one at width 390. An ordinary long
1119+
// docs page is ~10850px tall at that width -- 4.2M pixels against a 14.4M budget. The old 10000 height cap
1120+
// was derived for the 1440-wide DESKTOP viewport (MAX_SCREENSHOT_PIXELS is literally 1440 × 10000), so
1121+
// reusing it for mobile rejected captures costing a third as much, weakening the visual gate on exactly
1122+
// the viewport most likely to reveal a responsive regression.
1123+
it("REGRESSION: the real observed mobile page (390 × 10847) is now within both caps", () => {
1124+
const width = MOBILE_VIEWPORT.width;
1125+
const height = 10847;
1126+
expect(width).toBe(390);
1127+
expect(height).toBeLessThanOrEqual(MAX_SCREENSHOT_HEIGHT);
1128+
expect(width * height).toBeLessThanOrEqual(MAX_SCREENSHOT_PIXELS);
1129+
});
1130+
1131+
it("INVARIANT: desktop is unaffected — the pixel cap still rejects a wide page of the same height", () => {
1132+
// The cost ceiling must not have been widened by raising the height bound.
1133+
const area = DESKTOP_VIEWPORT.width * 10847;
1134+
expect(area).toBeGreaterThan(MAX_SCREENSHOT_PIXELS);
1135+
});
1136+
1137+
it("INVARIANT: the height cap is a sanity bound the renderer can actually satisfy", () => {
1138+
// 20000 is verified against this deployment's renderer (browserless v2 / Chrome 149): a 390 × 20000
1139+
// full-page capture returns 200 in ~157KB. If this is ever raised past what the renderer can produce,
1140+
// captures fail at request time instead of being cheaply rejected here.
1141+
expect(MAX_SCREENSHOT_HEIGHT).toBe(20000);
1142+
expect(MOBILE_VIEWPORT.width * MAX_SCREENSHOT_HEIGHT).toBeLessThanOrEqual(MAX_SCREENSHOT_PIXELS);
1143+
});
1144+
});

0 commit comments

Comments
 (0)