Skip to content

Conversation

@eldadfux
Copy link
Member

@eldadfux eldadfux commented Oct 21, 2025

What does this PR do?

Makes the API more flexible, allow passing more configs, keeps reasonable defaults,

Test Plan

Tested manually.

Related PRs and Issues

N/A

Have you read the Contributing Guidelines on issues?

Yes

Summary by CodeRabbit

  • New Features

    • Expanded screenshot controls: format, quality, full-page, clipping, headers, and viewport/device emulation (scale, touch, mobile).
    • Enhanced browser context: user agent, locale, timezone, geolocation, and granular permissions.
    • Lighthouse reports: theme selection and customizable thresholds.
    • New interactive test endpoint to verify browser configuration in-page.
  • Documentation

    • Reworked Features and Quick Start with concrete examples and commands.
    • Expanded API docs, timezone validation, extensive permissions examples, and updated contributor/dev guidance.

…nshots and reports, including viewport, geolocation, permissions, and custom thresholds. Update usage examples and improve validation for timezone identifiers.
- Introduced a new GET endpoint `/v1/test` to display current browser configuration values in an HTML format.
- Enhanced the README with detailed usage instructions, response format, and various test command examples for different scenarios, including mobile and high-quality desktop tests.
@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

This pull request expands README documentation (features, quick start, API usage, timezone and permissions examples, contributing) and extends the server payload schemas and handlers. The screenshot and lighthouse schemas now include headers, viewport, format/quality, clip, device flags, browser context options (userAgent, locale, timezoneId, geolocation, permissions), and navigation options (waitUntil, timeout). v1/screenshots and v1/reports are updated to merge and apply these options (context creation, header overrides, navigation, screenshot capture, dynamic thresholds). A new v1/test route renders an HTML page showing detected browser configuration values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The PR title "Feat add more configs" is related to the actual changes being made, which do indeed add more configuration options to the API (viewport, headers, userAgent, locale, timezone, permissions, etc.). However, the title uses generic and non-descriptive language that lacks meaningful specificity. The term "more configs" is quite broad and vague, similar to the "misc updates" example provided in the criteria, and doesn't clearly convey what the primary change or focus is. While the title technically refers to a real aspect of the changeset, it fails to communicate sufficient detail about which configurations are being added or why they matter. Revise the title to be more specific and descriptive about the configuration options being added. For example, consider titles like "Feat: Add viewport, timezone, and permissions configuration options" or "Feat: Expand API with flexible screenshot and Lighthouse parameters" that clearly communicate the main changes rather than using the generic term "more configs".
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-more-configs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/index.js (5)

141-159: Set correct Content-Type for screenshot responses.

You return a raw Buffer but never set the image Content-Type, so clients may treat it as JSON/octet-stream.

Apply headers before returning, mapping type → MIME:

-    const screen = await page.screenshot(screenshotOptions);
+    const screen = await page.screenshot(screenshotOptions);
+    const mime = {
+      png: 'image/png',
+      jpeg: 'image/jpeg',
+      webp: 'image/webp',
+    }[screenshotOptions.type] || 'application/octet-stream';
+    event.node.res.setHeader('Content-Type', mime);
+    event.node.res.setHeader('Content-Length', String(screen.length));
     // await context.tracing.stop({ path: '/tmp/trace' + Date.now() + '.zip' });
 
     await context.close();
     return screen;

Also applies to: 176-182


142-151: Headers are only applied to http://appwrite/*; docs imply they should apply to target origin.

Users expect headers to affect requests to the page’s origin, not just an internal host.

Scope overrides to body.url origin:

- await page.route("**/*", async (route, request) => {
-   const url = request.url();
-   if (url.startsWith("http://appwrite/")) {
+ const targetOrigin = new URL(body.url).origin;
+ await page.route("**/*", async (route, request) => {
+   const reqOrigin = new URL(request.url()).origin;
+   if (reqOrigin === targetOrigin) {
      return await route.continue({
        headers: {
          ...request.headers(),
-         ...(body.headers || {}),
+         ...(body.headers ? Object.fromEntries(Object.entries(body.headers).map(([k,v]) => [k, String(v)])) : {}),
        },
      });
    }
    return await route.continue({ headers: request.headers() });
 });

Apply the same logic in /v1/reports.


295-316: Avoid hardcoding Lighthouse CDP port and improve robustness.

Port 9222 is fixed; conflicts or multiple instances will break, and the DevTools port may be reachable from the network.

  • Bind DevTools to loopback and allow configurable port.
  • Reuse the same variable in launch and playAudit.
-const browser = await chromium.launch({
-  args: ["--remote-debugging-port=9222"],
-});
+const cdpPort = Number(process.env.CDP_PORT ?? 9222);
+const browser = await chromium.launch({
+  args: [`--remote-debugging-address=127.0.0.1`,`--remote-debugging-port=${cdpPort}`],
+});
...
-  port: 9222,
+  port: cdpPort,

Consider CDP_PORT=0 with a discovery step if playwright-lighthouse supports it; otherwise document the env.


20-25: Add lifecycle handling and error guard for the global browser.

A failed launch or missing graceful shutdown will crash/hang containers on signals/restarts.

Add minimal guards:

-console.log("Chromium starting...");
-const browser = await chromium.launch({
+console.log("Chromium starting...");
+let browser;
+try {
+  browser = await chromium.launch({
     args: ["--remote-debugging-port=9222"],
-});
+  });
+} catch (err) {
+  console.error("Chromium failed to start:", err);
+  process.exit(1);
+}
+for (const sig of ["SIGINT","SIGTERM"]) {
+  process.on(sig, async () => {
+    try { if (browser?.isConnected()) await browser.close(); } finally { process.exit(0); }
+  });
+}
console.log("Chromium started!");

Adjust with the cdpPort/address change above.


1-16: Run pnpm biome format ./src to fix formatting throughout the file.

Biome check confirms pending formatting changes across src/index.js. The main issues are systematic method-chain formatting in zod schema definitions (breaking chains onto separate lines, restructuring method calls). While the imports at lines 1-16 appear compliant, merging readValidatedBody into the first h3 import would improve consistency:

import {
	createApp,
	createRouter,
	defineEventHandler,
	toNodeListener,
	readValidatedBody,
} from "h3";
🧹 Nitpick comments (5)
src/index.js (3)

36-108: Add schema-level refinement: prevent clip+fullPage and validate viewport clip bounds.

Avoid runtime 400s where possible.

Example Zod refinements:

-const screenshotSchema = z.object({
+const screenshotSchema = z.object({
   ...
-}).optional(),
+}).refine((data) => !(data.clip && data.fullPage), {
+  message: 'clip cannot be used with fullPage',
+  path: ['clip'],
+}).refine((data) => {
+  if (!data.clip || !data.viewport) return true;
+  const { x,y,width,height } = data.clip;
+  const { width: vw, height: vh } = data.viewport;
+  return x + width <= vw && y + height <= vh;
+}, { message: 'clip must fit within viewport', path: ['clip'] });

254-270: Granting unsupported permissions may throw; sanitize against Playwright’s allowed set.

The enum includes many names Playwright doesn’t support in grantPermissions. Unknown names can error.

Add a whitelist and filter:

- if (body.permissions && body.permissions.length > 0) {
-   await context.grantPermissions(body.permissions, { origin: body.url });
- }
+ const allowed = new Set(['geolocation','midi','clipboard-read','clipboard-write','persistent-storage','push','background-sync','camera','microphone','notifications','sensors']);
+ if (body.permissions?.length) {
+   const filtered = body.permissions.filter(p => allowed.has(p));
+   if (filtered.length) await context.grantPermissions(filtered, { origin: body.url });
+}

Do the same in screenshots. Also reflect supported names in README.


331-691: /v1/test does not reflect caller-provided context; consider query-driven overrides.

README positions it for “verify browser state”, but the endpoint always uses defaultContext.

Option: accept query params (viewport, theme, locale, timezoneId, hasTouch, isMobile) and apply to newContext, mirroring screenshots schema minimally.

README.md (2)

520-528: Headers scope: clarify they apply to target origin (not all requests).

Align with code change to apply headers to the page origin only; mention that cross-origin requests remain unaffected.


699-709: Dev instructions mix pnpm and npm; prefer one path.

Start command uses npm while deps use pnpm; unify or explain both.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9a2c48 and 3030854.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • src/index.js (7 hunks)
🧰 Additional context used
🪛 GitHub Actions: Lint
src/index.js

[error] 1-1: Biome lint/format check reported formatting changes and a failure. Command 'pnpm run lint' (biome check ./src) exited with code 1.

🪛 LanguageTool
README.md

[uncategorized] ~159-~159: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... fullPage | boolean | false | Capture full page scroll | | clip | object | - | Crop a...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

🔇 Additional comments (1)
src/index.js (1)

185-244: Mirror schema fixes: timezone, headers typing, and clip/fullPage constraint.

  • Apply the same timezone validation refinement here (Lines 196-199).
  • Constrain headers to string values (Line 193).

If you keep regex, update README to remove plain "UTC"/"GMT" or accept them here too.

Comment on lines +151 to +177
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | string | **required** | The URL to screenshot |
| `viewport` | object | `{width: 1280, height: 720}` | Viewport dimensions |
| `viewport.width` | number | 1280 | Viewport width (1-3840) |
| `viewport.height` | number | 720 | Viewport height (1-2160) |
| `format` | string | "png" | Image format: "png", "jpeg", "webp" |
| `quality` | number | 90 | Image quality 0-100 (for jpeg/webp) |
| `fullPage` | boolean | false | Capture full page scroll |
| `clip` | object | - | Crop area: `{x, y, width, height}` |
| `theme` | string | "light" | Browser theme: "light", "dark" |
| `userAgent` | string | - | Custom user agent string |
| `locale` | string | - | Browser locale (e.g., "en-US") |
| `timezoneId` | string | - | IANA timezone identifier (see [Timezone](#timezone) section) |
| `geolocation` | object | - | GPS coordinates |
| `geolocation.latitude` | number | - | Latitude (-90 to 90) |
| `geolocation.longitude` | number | - | Longitude (-180 to 180) |
| `geolocation.accuracy` | number | - | Accuracy in meters |
| `permissions` | array | - | Browser permissions (see [Permissions](#permissions) section) |
| `headers` | object | - | Custom HTTP headers |
| `waitUntil` | string | "domcontentloaded" | Wait condition: "load", "domcontentloaded", "networkidle", "commit" |
| `timeout` | number | 30000 | Navigation timeout (0-120000ms) |
| `sleep` | number | 3000 | Wait time after load (0-60000ms) |
| `deviceScaleFactor` | number | 1 | Device pixel ratio (0.1-3) |
| `hasTouch` | boolean | false | Touch support |
| `isMobile` | boolean | false | Mobile device emulation |

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Document PNG quality constraint and clip/fullPage exclusivity.

Quality applies only to jpeg/webp; clip cannot be used with fullPage.

Proposed edits:

  • Note “quality (jpeg/webp only)”.
  • In the table: “clip — cannot be combined with fullPage: true”.
  • Update examples using format: "png" to omit “quality”.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~159-~159: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ... fullPage | boolean | false | Capture full page scroll | | clip | object | - | Crop a...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

Comment on lines +246 to +304
## Timezone

The `timezoneId` parameter allows you to set the browser's timezone for accurate time-based testing and content capture. This is particularly useful for testing applications that display time-sensitive content or have timezone-dependent features.

### Format

The `timezoneId` must be a valid **IANA timezone identifier** in the format `Region/City`. This is the same format used by JavaScript's `Intl.DateTimeFormat` and is the standard for timezone identification.

### Valid Timezone Examples

#### Americas
- `America/New_York` - Eastern Time (US)
- `America/Chicago` - Central Time (US)
- `America/Denver` - Mountain Time (US)
- `America/Los_Angeles` - Pacific Time (US)
- `America/Toronto` - Eastern Time (Canada)
- `America/Vancouver` - Pacific Time (Canada)
- `America/Sao_Paulo` - Brasília Time (Brazil)
- `America/Mexico_City` - Central Time (Mexico)
- `America/Argentina/Buenos_Aires` - Argentina Time

#### Europe
- `Europe/London` - Greenwich Mean Time / British Summer Time
- `Europe/Paris` - Central European Time
- `Europe/Berlin` - Central European Time
- `Europe/Rome` - Central European Time
- `Europe/Madrid` - Central European Time
- `Europe/Amsterdam` - Central European Time
- `Europe/Moscow` - Moscow Time
- `Europe/Istanbul` - Turkey Time

#### Asia
- `Asia/Tokyo` - Japan Standard Time
- `Asia/Shanghai` - China Standard Time
- `Asia/Hong_Kong` - Hong Kong Time
- `Asia/Singapore` - Singapore Time
- `Asia/Seoul` - Korea Standard Time
- `Asia/Dubai` - Gulf Standard Time
- `Asia/Kolkata` - India Standard Time
- `Asia/Bangkok` - Indochina Time

#### Australia & Pacific
- `Australia/Sydney` - Australian Eastern Time
- `Australia/Melbourne` - Australian Eastern Time
- `Australia/Perth` - Australian Western Time
- `Pacific/Auckland` - New Zealand Time
- `Pacific/Honolulu` - Hawaii-Aleutian Time
- `Pacific/Fiji` - Fiji Time

#### Africa
- `Africa/Cairo` - Eastern European Time
- `Africa/Johannesburg` - South Africa Standard Time
- `Africa/Lagos` - West Africa Time
- `Africa/Nairobi` - East Africa Time

#### Special Cases
- `UTC` - Coordinated Universal Time
- `GMT` - Greenwich Mean Time

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Timezone section contradicts schema (UTC/GMT).

You list UTC and GMT as valid, but current regex rejects them; adjust docs or schema to accept plain “UTC” (and consider “GMT” if desired).

If adopting Intl-based validation (recommended), keep docs and provide example with "UTC".


I'll help verify this timezone validation issue. Let me search the codebase for the timezone validation schema.


🏁 Script executed:

#!/bin/bash
# Find timezone validation code/schema
rg -n -i "timezone" -C 5 --type=ts --type=js --type=json --type=yaml | head -100

Length of output: 4206


🏁 Script executed:

#!/bin/bash
# Search for regex patterns related to timezone or IANA
rg -n "IANA|timezone.*regex|pattern.*timezone" -C 3

Length of output: 2966


🏁 Script executed:

#!/bin/bash
# Look for schema files (common patterns)
fd -e json -e ts -e js -e yaml | xargs rg -l "timezone|timezoneId" 2>/dev/null | head -20

Length of output: 12


Fix timezone documentation to match validation schema.

The timezone validation regex in src/index.js (lines 59-62 and 196-199) requires the format Region/City with a mandatory forward slash and characters after it. However, the README lists UTC and GMT as valid standalone entries under "Special Cases."

The current regex /^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific|UTC)\/[A-Za-z_]+$/ rejects:

  • Plain "UTC" (requires something like "UTC/Something")
  • "GMT" entirely (not in the prefix list)

Either:

  1. Update README to remove UTC/GMT from "Special Cases" or clarify they must follow Region/City format, or
  2. Update schema to accept plain "UTC" (and optionally "GMT") by modifying the regex to make the slash and suffix optional or adding alternatives

Comment on lines +430 to +476

```bash
curl -X POST http://localhost:3000/v1/screenshots \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"viewport": {
"width": 2560,
"height": 1440
},
"format": "jpeg",
"quality": 100,
"fullPage": true,
"theme": "dark",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"locale": "en-US",
"timezoneId": "Europe/London",
"geolocation": {
"latitude": 51.5074,
"longitude": -0.1278,
"accuracy": 20
},
"permissions": [
"geolocation",
"notifications",
"camera",
"microphone",
"clipboard-read",
"clipboard-write",
"payment-handler",
"usb",
"bluetooth"
],
"headers": {
"Authorization": "Bearer premium-token",
"X-Premium-User": "true",
"X-Request-Source": "screenshot-api"
},
"waitUntil": "networkidle",
"timeout": 90000,
"sleep": 8000,
"deviceScaleFactor": 2,
"hasTouch": false,
"isMobile": false
}' \
--output "high-quality-desktop-screenshot.jpg"
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Example uses quality with PNG; will fail after enforcing constraints.

Remove quality from PNG examples or switch to jpeg/webp.

🤖 Prompt for AI Agents
In README.md around lines 430 to 476, the example POST includes a "quality"
field while using formats that may be PNG in other examples and will fail once
quality constraints are enforced; update the example(s) by either removing the
"quality" property when the format is PNG or change the format to a lossy type
that supports quality (e.g., "jpeg" or "webp"); ensure the JSON payload and
example output filename are consistent with the chosen format.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
src/index.js (7)

39-39: Restrict header values to strings (Playwright expects string headers).

This was flagged in a previous review. headers currently allows any value; non-strings will break route.continue.

Constrain the schema:

-headers: z.record(z.string(), z.any()).optional(),
+headers: z.record(z.string(), z.string()).optional(),

63-69: Timezone validation rejects valid identifiers advertised in README.

This was flagged in a previous review. The regex requires Region/City and allows UTC/City, but README claims plain UTC and GMT are valid.

Prefer runtime validation via Intl:

-timezoneId: z.string().regex(
-  /^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific|UTC)\/[A-Za-z_]+$/,
-  "Must be a valid IANA timezone identifier (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo')"
-).optional(),
+timezoneId: z.string().refine((tz) => {
+  try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); return true; } catch { return false; }
+}, "Must be a valid IANA timezone (e.g., 'America/New_York', 'Europe/London', 'UTC')").optional(),

Mirror the same change in lighthouseSchema.


180-190: Avoid passing quality for PNG and disallow clip with fullPage.

This was flagged in a previous review:

  • Playwright rejects quality for PNG.
  • clip and fullPage are mutually exclusive; passing both throws.

Update options construction:

-  const screenshotOptions = {
-    type: body.format,
-    quality: body.quality,
-    fullPage: body.fullPage,
-  };
+  const screenshotOptions = {
+    type: body.format,
+    fullPage: body.fullPage,
+  };
+  if ((body.format === 'jpeg' || body.format === 'webp') && typeof body.quality === 'number') {
+    screenshotOptions.quality = body.quality;
+  }
 
-  if (body.clip) {
+  if (body.clip) {
+    if (body.fullPage) {
+      throw createError({ statusCode: 400, statusMessage: 'clip cannot be used with fullPage' });
+    }
     screenshotOptions.clip = body.clip;
   }

208-208: Restrict header values to strings (Playwright expects string headers).

Same issue as line 39. headers currently allows any value; non-strings will break route.continue.

Constrain the schema:

-headers: z.record(z.string(), z.any()).optional(),
+headers: z.record(z.string(), z.string()).optional(),

211-217: Timezone validation rejects valid identifiers.

Same issue as lines 63-69. The regex requires Region/City format and rejects plain UTC and GMT.

Prefer runtime validation via Intl:

-timezoneId: z.string().regex(
-  /^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific|UTC)\/[A-Za-z_]+$/,
-  "Must be a valid IANA timezone identifier (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo')"
-).optional(),
+timezoneId: z.string().refine((tz) => {
+  try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); return true; } catch { return false; }
+}, "Must be a valid IANA timezone (e.g., 'America/New_York', 'Europe/London', 'UTC')").optional(),

52-59: Schema allows clip and fullPage together, but they're mutually exclusive.

Playwright throws if both clip and fullPage: true are provided. Consider adding schema-level validation to reject this combination early.

Add a .refine() check to the schema:

 const screenshotSchema = z.object({
   // ... existing fields
+}).refine(
+  (data) => !(data.clip && data.fullPage),
+  { message: "clip and fullPage cannot be used together" }
 });

50-51: Quality default applies to PNG, but Playwright ignores or rejects it.

The quality field defaults to 90 for all formats, but Playwright's screenshot method only accepts quality for JPEG and WebP. For PNG, this parameter is either ignored or causes an error.

Consider making quality conditional or removing the default:

-format: z.enum(["png", "jpeg", "webp"]).default("png"),
-quality: z.number().min(0).max(100).default(90),
+format: z.enum(["png", "jpeg", "webp"]).default("png"),
+quality: z.number().min(0).max(100).optional(),

Then apply quality only when appropriate in the handler (see separate comment).

🧹 Nitpick comments (3)
src/index.js (3)

280-290: Consider adding viewport and device options to lighthouse context.

The screenshot handler (lines 129-137) applies viewport, deviceScaleFactor, hasTouch, and isMobile from the request body, but the lighthouse handler only sets colorScheme. This inconsistency means lighthouse reports can't test mobile viewports or device-specific behavior.

Consider extending lighthouseSchema and applying these options:

 const contextOptions = {
   ...defaultContext,
   colorScheme: body.theme,
+  viewport: body.viewport || defaultContext.viewport,
+  deviceScaleFactor: body.deviceScaleFactor,
+  hasTouch: body.hasTouch,
+  isMobile: body.isMobile,
 };

(You would also need to add these fields to lighthouseSchema if not already present.)


368-709: Consider extracting HTML template to a separate file.

This 340+ line HTML template with embedded CSS and JavaScript reduces maintainability and readability of the main handler file.

Consider moving the template to a separate file:

// In a new file: templates/test.html
<!DOCTYPE html>
<html lang="en">
...
</html>

// In src/index.js:
import { readFile } from 'node:fs/promises';
const testTemplate = await readFile('./templates/test.html', 'utf-8');

// In the handler:
await context.close();
event.node.res.setHeader("Content-Type", "text/html");
return testTemplate;

Alternatively, use a template function that accepts dynamic values if needed.


357-366: Test route uses only default context; cannot verify custom configurations.

The test route creates a context with defaultContext settings and navigates to about:blank. This means it cannot test the impact of custom userAgent, locale, timezoneId, viewport, deviceScaleFactor, geolocation, or permissions that the API now supports.

Consider accepting query parameters or a POST body to allow testing custom configurations:

router.post("/v1/test", defineEventHandler(async (event) => {
  const body = await readValidatedBody(event, screenshotSchema.pick({
    theme: true,
    viewport: true,
    userAgent: true,
    locale: true,
    timezoneId: true,
    geolocation: true,
    permissions: true,
    deviceScaleFactor: true,
    hasTouch: true,
    isMobile: true,
  }).parse);
  
  const contextOptions = {
    ...defaultContext,
    colorScheme: body.theme,
    viewport: body.viewport || defaultContext.viewport,
    // ... apply other options
  };
  
  const context = await browser.newContext(contextOptions);
  // ... rest of handler
}));

This would make the test route actually useful for verifying configuration effects.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3030854 and a529f76.

📒 Files selected for processing (1)
  • src/index.js (7 hunks)
🔇 Additional comments (3)
src/index.js (3)

129-150: LGTM! Context options construction is well-structured.

The context options are built with proper fallbacks and conditionally apply optional fields. Permission granting is correctly scoped to the target origin.


256-264: Well-designed threshold defaults.

The thresholds object provides sensible defaults (all zeros) when individual properties aren't specified, while keeping the entire object optional. This allows flexible override without requiring all categories.


321-328: LGTM! Threshold fallback logic is correct.

The handler correctly uses custom thresholds if provided, otherwise defaults to zeros for all categories. This matches the schema design and allows flexible overrides.

Comment on lines +603 to +618
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
document.getElementById('geolocation-info').innerHTML =
'Latitude: ' + position.coords.latitude + '<br>' +
'Longitude: ' + position.coords.longitude + '<br>' +
'Accuracy: ' + position.coords.accuracy + 'm';
},
(error) => {
document.getElementById('geolocation-info').innerHTML = 'Error: ' + error.message;
},
{ timeout: 1000 }
);
} else {
document.getElementById('geolocation-info').textContent = 'Not available';
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Geolocation API may fail on about:blank.

The test page tries to call navigator.geolocation.getCurrentPosition(), but this typically requires a secure context with proper permissions. On about:blank, this will likely timeout or error.

Consider either:

  1. Navigating to a real URL (e.g., a local test page) instead of about:blank
  2. Pre-granting geolocation permission to the context
  3. Adjusting the UI to handle the expected failure gracefully (already partially done with error handling)

The current 1-second timeout will cause delays; consider reducing it or making the permission grant explicit.

🤖 Prompt for AI Agents
In src/index.js around lines 603-618, the Geolocation call on about:blank can
fail or timeout because geolocation requires a secure context and permission;
update the code to avoid flaky behavior by either navigating tests to a real
secure URL (or local file:///served page) instead of about:blank, pre-granting
geolocation permission for the test context, and/or reducing the timeout (e.g.,
much less than 1000ms) and improving the UI fallback: ensure getCurrentPosition
errors are treated as expected (show a friendly message and skip waits), and
avoid long blocking timeouts in test flows. Make one concrete change: choose one
approach (prefer navigating to a real test page or programmatically granting
permission) and implement the corresponding adjustment plus a robust
short-timeout error path.

@Meldiron Meldiron merged commit eca1be0 into main Oct 22, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants