playwright-interactive

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Playwright Interactive Skill

Playwright交互式技能

Use this skill when a task needs interactive browser or Electron work in a persistent
js_repl
session. Keep the Playwright handles alive across code edits, reloads, and repeated checks so iteration stays fast.
当任务需要在持久化
js_repl
会话中进行交互式浏览器或Electron操作时,可使用本技能。在代码编辑、重新加载和重复检查过程中保持Playwright句柄存活,从而确保迭代效率。

Preconditions

前置条件

  • js_repl
    must be enabled for this skill.
  • If
    js_repl
    is missing, enable it in
    ~/.codex/config.toml
    :
toml
[features]
js_repl = true
  • You can also start a new session with
    --enable js_repl
    (equivalent to
    -c features.js_repl=true
    ).
  • After enabling
    js_repl
    , start a new Codex session so the tool list refreshes.
  • For now, run this workflow with sandboxing disabled: start Codex with
    --sandbox danger-full-access
    (or the equivalent config for
    sandbox_mode=danger-full-access
    ). This is a temporary requirement while
    js_repl
    + Playwright support inside the sandbox is still being completed.
  • Run setup from the same project directory you need to debug.
  • Treat
    js_repl_reset
    as a recovery tool, not routine cleanup. Resetting the kernel destroys your Playwright handles.
  • 本技能需启用
    js_repl
  • 若缺少
    js_repl
    ,请在
    ~/.codex/config.toml
    中启用:
toml
[features]
js_repl = true
  • 你也可以通过
    --enable js_repl
    启动新会话(等效于
    -c features.js_repl=true
    )。
  • 启用
    js_repl
    后,请启动新的Codex会话以刷新工具列表。
  • 目前,运行此工作流需禁用沙箱:使用
    --sandbox danger-full-access
    启动Codex(或配置
    sandbox_mode=danger-full-access
    )。这是临时要求,因为沙箱内的
    js_repl
    +Playwright支持仍在开发中。
  • 从需要调试的项目目录运行设置步骤。
  • js_repl_reset
    视为恢复工具,而非常规清理手段。重置内核会销毁你的Playwright句柄。

One-time setup

一次性设置

bash
test -f package.json || npm init -y
npm install playwright
bash
test -f package.json || npm init -y
npm install playwright

Web-only, for headed Chromium or mobile emulation:

仅用于网页端,如需有头Chromium或移动设备模拟:

npx playwright install chromium

npx playwright install chromium

Electron-only, and only if the target workspace is the app itself:

仅用于Electron端,且目标工作区为应用本身时:

npm install --save-dev electron

npm install --save-dev electron

node -e "import('playwright').then(() => console.log('playwright import ok')).catch((error) => { console.error(error); process.exit(1); })"

If you switch to a different workspace later, repeat setup there.
node -e "import('playwright').then(() => console.log('playwright import ok')).catch((error) => { console.error(error); process.exit(1); })"

若后续切换到其他工作区,请重复上述设置步骤。

Core Workflow

核心工作流

  1. Write a brief QA inventory before testing:
    • Build the inventory from three sources: the user's requested requirements, the user-visible features or behaviors you actually implemented, and the claims you expect to make in the final response.
    • Anything that appears in any of those three sources must map to at least one QA check before signoff.
    • List the user-visible claims you intend to sign off on.
    • List every meaningful user-facing control, mode switch, or implemented interactive behavior.
    • List the state changes or view changes each control or implemented behavior can cause.
    • Use this as the shared coverage list for both functional QA and visual QA.
    • For each claim or control-state pair, note the intended functional check, the specific state where the visual check must happen, and the evidence you expect to capture.
    • If a requirement is visually central but subjective, convert it into an observable QA check instead of leaving it implicit.
    • Add at least 2 exploratory or off-happy-path scenarios that could expose fragile behavior.
  2. Run the bootstrap cell once.
  3. Start or confirm any required dev server in a persistent TTY session.
  4. Launch the correct runtime and keep reusing the same Playwright handles.
  5. After each code change, reload for renderer-only changes or relaunch for main-process/startup changes.
  6. Run functional QA with normal user input.
  7. Run a separate visual QA pass.
  8. Verify viewport fit and capture the screenshots needed to support your claims.
  9. Clean up the Playwright session only when the task is actually finished.
  1. 测试前编写简要的QA清单:
    • 从三个来源构建清单:用户需求、实际实现的用户可见功能或行为、最终响应中你期望做出的声明。
    • 上述三个来源中出现的任何内容,在签署确认前必须对应至少一项QA检查。
    • 列出你打算签署确认的用户可见声明。
    • 列出所有有意义的用户交互控件、模式切换或已实现的交互行为。
    • 列出每个控件或已实现行为可能导致的状态变化或视图变化。
    • 将此清单用作功能QA和视觉QA的共享覆盖列表。
    • 对于每项声明或控件-状态对,注明预期的功能检查、必须进行视觉检查的特定状态,以及你期望捕获的证据。
    • 若某项需求以视觉为核心但具有主观性,请将其转化为可观察的QA检查,而非留作隐含要求。
    • 至少添加2个探索性或非常规路径场景,以暴露潜在的脆弱行为。
  2. 运行一次引导代码块。
  3. 在持久化TTY会话中启动或确认所需的开发服务器。
  4. 启动正确的运行时,并重复使用相同的Playwright句柄。
  5. 每次代码变更后,若仅为渲染器变更则重新加载;若为主进程/启动变更则重新启动。
  6. 使用正常用户输入执行功能QA。
  7. 单独执行视觉QA环节。
  8. 验证视口适配情况,并捕获支持你声明所需的截图。
  9. 仅当任务实际完成时,才清理Playwright会话。

Bootstrap (Run Once)

引导代码(运行一次)

javascript
var chromium;
var electronLauncher;
var browser;
var context;
var page;
var mobileContext;
var mobilePage;
var electronApp;
var appWindow;

try {
  ({ chromium, _electron: electronLauncher } = await import("playwright"));
  console.log("Playwright loaded");
} catch (error) {
  throw new Error(
    `Could not load playwright from the current js_repl cwd. Run the setup commands from this workspace first. Original error: ${error}`
  );
}
javascript
var chromium;
var electronLauncher;
var browser;
var context;
var page;
var mobileContext;
var mobilePage;
var electronApp;
var appWindow;

try {
  ({ chromium, _electron: electronLauncher } = await import("playwright"));
  console.log("Playwright loaded");
} catch (error) {
  throw new Error(
    `Could not load playwright from the current js_repl cwd. Run the setup commands from this workspace first. Original error: ${error}`
  );
}

Start or Reuse Web Session

启动或复用网页会话

Set
TARGET_URL
to the app you are debugging. For local servers, prefer
127.0.0.1
over
localhost
.
javascript
const TARGET_URL = "http://127.0.0.1:3000";

if (!browser) {
  browser = await chromium.launch({ headless: false });
}

if (!context) {
  context = await browser.newContext({
    viewport: { width: 1600, height: 900 },
  });
}

if (!page) {
  page = await context.newPage();
}

await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded:", await page.title());
TARGET_URL
设置为你要调试的应用。对于本地服务器,优先使用
127.0.0.1
而非
localhost
javascript
const TARGET_URL = "http://127.0.0.1:3000";

if (!browser) {
  browser = await chromium.launch({ headless: false });
}

if (!context) {
  context = await browser.newContext({
    viewport: { width: 1600, height: 900 },
  });
}

if (!page) {
  page = await context.newPage();
}

await page.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
console.log("Loaded:", await page.title());

Start or Reuse Electron Session

启动或复用Electron会话

Set
ELECTRON_ENTRY
to
.
when the current workspace is the Electron app and
package.json
points
main
to the right entry file. If you need to target a specific main-process file directly, use a path such as
./main.js
instead.
javascript
const ELECTRON_ENTRY = ".";

if (electronApp) {
  await electronApp.close().catch(() => {});
}

electronApp = await electronLauncher.launch({
  args: [ELECTRON_ENTRY],
  cwd: process.cwd(),
});

appWindow = await electronApp.firstWindow();

console.log("Loaded Electron window:", await appWindow.title());
当当前工作区为Electron应用且
package.json
main
指向正确入口文件时,将
ELECTRON_ENTRY
设置为
.
。若需直接指定特定主进程文件,请使用
./main.js
之类的路径。
javascript
const ELECTRON_ENTRY = ".";

if (electronApp) {
  await electronApp.close().catch(() => {});
}

electronApp = await electronLauncher.launch({
  args: [ELECTRON_ENTRY],
  cwd: process.cwd(),
});

appWindow = await electronApp.firstWindow();

console.log("Loaded Electron window:", await appWindow.title());

Reuse Sessions During Iteration

迭代过程中复用会话

Keep the same session alive whenever you can.
Web renderer reload:
javascript
for (const p of context.pages()) {
  await p.reload({ waitUntil: "domcontentloaded" });
}
console.log("Reloaded existing tabs");
Electron renderer-only reload:
javascript
await appWindow.reload({ waitUntil: "domcontentloaded" });
console.log("Reloaded Electron window");
Electron restart after main-process, preload, or startup changes:
javascript
await electronApp.close().catch(() => {});

electronApp = await electronLauncher.launch({
  args: ["."],
  cwd: process.cwd(),
});

appWindow = await electronApp.firstWindow();
console.log("Relaunched Electron window:", await appWindow.title());
Default posture:
  • Keep each
    js_repl
    cell short and focused on one interaction burst.
  • Reuse the existing top-level bindings (
    browser
    ,
    context
    ,
    page
    ,
    electronApp
    ,
    appWindow
    ) instead of redeclaring them.
  • If you need isolation, create a new page or a new context inside the same browser.
  • For Electron, use
    electronApp.evaluate(...)
    only for main-process inspection or purpose-built diagnostics.
  • Fix helper mistakes in place; do not reset the REPL unless the kernel is actually broken.
尽可能保持同一会话存活。
网页渲染器重新加载:
javascript
for (const p of context.pages()) {
  await p.reload({ waitUntil: "domcontentloaded" });
}
console.log("Reloaded existing tabs");
Electron仅渲染器重新加载:
javascript
await appWindow.reload({ waitUntil: "domcontentloaded" });
console.log("Reloaded Electron window");
主进程、预加载或启动变更后重启Electron:
javascript
await electronApp.close().catch(() => {});

electronApp = await electronLauncher.launch({
  args: ["."],
  cwd: process.cwd(),
});

appWindow = await electronApp.firstWindow();
console.log("Relaunched Electron window:", await appWindow.title());
默认准则:
  • 每个
    js_repl
    代码块应简短且聚焦于单次交互操作。
  • 复用现有顶级绑定(
    browser
    context
    page
    electronApp
    appWindow
    ),而非重新声明。
  • 若需要隔离环境,请在同一浏览器内创建新页面或新上下文。
  • 对于Electron,仅在主进程检查或专门诊断时使用
    electronApp.evaluate(...)
  • 就地修复辅助代码错误;除非内核确实故障,否则不要重置REPL。

Checklists

检查清单

Session Loop

会话循环

  • Bootstrap
    js_repl
    once, then keep the same Playwright handles alive across iterations.
  • Launch the target runtime from the current workspace.
  • Make the code change.
  • Reload or relaunch using the correct path for that change.
  • Update the shared QA inventory if exploration reveals an additional control, state, or visible claim.
  • Re-run functional QA.
  • Re-run visual QA.
  • Capture final artifacts only after the current state is the one you are evaluating.
  • Execute cleanup before ending the task or leaving the session.
  • 启动一次
    js_repl
    引导代码,然后在迭代过程中保持相同的Playwright句柄存活。
  • 从当前工作区启动目标运行时。
  • 进行代码变更。
  • 根据变更类型选择正确的重新加载或重启路径。
  • 若探索过程发现额外控件、状态或可见声明,请更新共享QA清单。
  • 重新执行功能QA。
  • 重新执行视觉QA。
  • 仅当当前状态为待评估状态时,才捕获最终产物。
  • 结束任务或退出会话前执行清理。

Reload Decision

重新加载决策

  • Renderer-only change: reload the existing page or Electron window.
  • Main-process, preload, or startup change: relaunch Electron.
  • New uncertainty about process ownership or startup code: relaunch instead of guessing.
  • 仅渲染器变更:重新加载现有页面或Electron窗口。
  • 主进程、预加载或启动变更:重启Electron。
  • 对进程归属或启动代码存在新疑问:选择重启而非猜测。

Functional QA

功能QA

  • Use real user controls for signoff: keyboard, mouse, click, touch, or equivalent Playwright input APIs.
  • Verify at least one end-to-end critical flow.
  • Confirm the visible result of that flow, not just internal state.
  • For realtime or animation-heavy apps, verify behavior under actual interaction timing.
  • Work through the shared QA inventory rather than ad hoc spot checks.
  • Cover every obvious visible control at least once before signoff, not only the main happy path.
  • For reversible controls or stateful toggles in the inventory, test the full cycle: initial state, changed state, and return to the initial state.
  • After the scripted checks pass, do a short exploratory pass using normal input for 30-90 seconds instead of following only the intended path.
  • If the exploratory pass reveals a new state, control, or claim, add it to the shared QA inventory and cover it before signoff.
  • page.evaluate(...)
    and
    electronApp.evaluate(...)
    may inspect or stage state, but they do not count as signoff input.
  • 使用真实用户控件进行签署确认:键盘、鼠标、点击、触摸或等效的Playwright输入API。
  • 验证至少一条端到端关键流程。
  • 确认该流程的可见结果,而非仅检查内部状态。
  • 对于实时或动画密集型应用,在实际交互时序下验证行为。
  • 依据共享QA清单进行检查,而非临时抽查。
  • 签署确认前,至少覆盖所有明显可见的控件,而非仅主常规路径。
  • 对于清单中可逆的控件或有状态的切换,测试完整周期:初始状态、变更后状态、恢复初始状态。
  • 脚本化检查通过后,进行30-90秒的简短探索性测试,使用正常输入而非仅遵循预期路径。
  • 若探索性测试发现新状态、控件或声明,请将其添加到共享QA清单并在签署确认前覆盖。
  • page.evaluate(...)
    electronApp.evaluate(...)
    可用于检查或设置状态,但不能作为签署确认的输入依据。

Visual QA

视觉QA

  • Treat visual QA as separate from functional QA.
  • Use the same shared QA inventory defined before testing and updated during QA; do not start visual coverage from a different implicit list.
  • Restate the user-visible claims and verify each one explicitly; do not assume a functional pass proves a visual claim.
  • A user-visible claim is not signed off until it has been inspected in the specific state where it is meant to be perceived.
  • Inspect the initial viewport before scrolling.
  • Confirm that the initial view visibly supports the interface's primary claims; if a core promised element is not clearly perceptible there, treat that as a bug.
  • Inspect all required visible regions, not just the main interaction surface.
  • Inspect the states and modes already enumerated in the shared QA inventory, including at least one meaningful post-interaction state when the task is interactive.
  • If motion or transitions are part of the experience, inspect at least one in-transition state in addition to the settled endpoints.
  • If labels, overlays, annotations, guides, or highlights are meant to track changing content, verify that relationship after the relevant state change.
  • For dynamic or interaction-dependent visuals, inspect long enough to judge stability, layering, and readability; do not rely on a single screenshot for signoff.
  • For interfaces that can become denser after loading or interaction, inspect the densest realistic state you can reach during QA, not only the empty, loading, or collapsed state.
  • If the product has a defined minimum supported viewport or window size, run a separate visual QA pass there; otherwise, choose a smaller but still realistic size and inspect it explicitly.
  • Distinguish presence from implementation: if an intended affordance is technically there but not clearly perceptible because of weak contrast, occlusion, clipping, or instability, treat that as a visual failure.
  • If any required visible region is clipped, cut off, obscured, or pushed outside the viewport in the state you are evaluating, treat that as a bug even if page-level scroll metrics appear acceptable.
  • Look for clipping, overflow, distortion, layout imbalance, inconsistent spacing, alignment problems, illegible text, weak contrast, broken layering, and awkward motion states.
  • Judge aesthetic quality as well as correctness. The UI should feel intentional, coherent, and visually pleasing for the task.
  • Prefer viewport screenshots for signoff. Use full-page captures only as secondary debugging artifacts.
  • If the full-window screenshot is not enough to judge a region confidently, capture a focused screenshot for that region.
  • If motion makes a screenshot ambiguous, wait briefly for the UI to settle, then capture the image you are actually evaluating.
  • Before signoff, explicitly ask: what visible part of this interface have I not yet inspected closely?
  • Before signoff, explicitly ask: what visible defect would most likely embarrass this result if the user looked closely?
  • 将视觉QA与功能QA分开处理。
  • 使用测试前定义并在QA过程中更新的共享QA清单,而非从其他隐含列表开始视觉覆盖。
  • 重新陈述用户可见声明并逐一明确验证;不要假设功能通过即证明视觉声明成立。
  • 仅当在声明相关的特定状态下进行检查后,用户可见声明才可签署确认。
  • 滚动前检查初始视口。
  • 确认初始视图能清晰支持界面的核心声明;若核心承诺元素在初始视图中无法清晰感知,应视为缺陷。
  • 检查所有必要的可见区域,而非仅主交互表面。
  • 检查共享QA清单中已枚举的所有状态和模式,包括交互式任务中至少一个有意义的交互后状态。
  • 若体验包含动画或过渡效果,除稳定端点外,至少检查一个过渡中的状态。
  • 若标签、覆盖层、注释、指南或高亮需跟踪变化内容,请在相关状态变更后验证其关联关系。
  • 对于动态或依赖交互的视觉元素,检查足够长时间以判断稳定性、层级和可读性;不要仅依赖单张截图进行签署确认。
  • 对于加载或交互后可能变得更密集的界面,检查QA过程中可达到的最密集真实状态,而非仅空状态、加载状态或折叠状态。
  • 若产品定义了最低支持视口或窗口大小,请单独运行一次视觉QA;否则,选择一个较小但仍真实的尺寸并明确检查。
  • 区分存在性与实现效果:若预期交互元素技术上存在,但因对比度低、遮挡、裁剪或不稳定而无法清晰感知,应视为视觉故障。
  • 若在评估状态下,任何必要可见区域被裁剪、截断、遮挡或推出视口,即使页面级滚动指标显示正常,也应视为缺陷。
  • 检查是否存在裁剪、溢出、变形、布局失衡、间距不一致、对齐问题、文本难以辨认、对比度低、层级错乱和动画状态异常。
  • 同时判断美学质量与正确性。UI应针对任务表现出目的性、连贯性和视觉愉悦性。
  • 签署确认优先使用视口截图。仅将全页捕获作为次要调试产物。
  • 若全窗口截图不足以自信判断某区域,请捕获该区域的聚焦截图。
  • 若动画导致截图模糊,请稍等UI稳定后,再捕获你实际评估的图像。
  • 签署确认前,明确自问:此界面的哪个可见部分我尚未仔细检查?
  • 签署确认前,明确自问:若用户仔细查看,最可能让此结果尴尬的可见缺陷是什么?

Signoff

签署确认

  • The functional path passed with normal user input.
  • Coverage is explicit against the shared QA inventory: note which requirements, implemented features, controls, states, and claims were exercised, and call out any intentional exclusions.
  • The visual QA pass covered the whole relevant interface.
  • Each user-visible claim has a matching visual check and artifact from the state where that claim matters.
  • The viewport-fit checks passed for the intended initial view and any required minimum supported viewport or window size.
  • If the product launches in a window, the as-launched size, placement, and initial layout were checked before any manual resize or repositioning.
  • The screenshots directly support the claims you are making.
  • The required screenshots were reviewed for the relevant states and viewport or window sizes established during QA.
  • The UI is not just functional; it is visually coherent and not aesthetically weak for the task.
  • Functional correctness, viewport fit, and visual quality must each pass on their own; one does not imply the others.
  • A short exploratory pass was completed for interactive products, and the response mentions what that pass covered.
  • If screenshot review and numeric checks disagreed at any point, the discrepancy was investigated before signoff; visible clipping in screenshots is a failure to resolve, not something metrics can overrule.
  • Include a brief negative confirmation of the main defect classes you checked for and did not find.
  • Cleanup was executed, or you intentionally kept the session alive for further work.
  • 功能路径通过正常用户输入测试。
  • 覆盖范围与共享QA清单明确对应:注明已验证的需求、实现的功能、控件、状态和声明,并指出任何有意排除的内容。
  • 视觉QA覆盖了整个相关界面。
  • 每个用户可见声明均有对应的视觉检查和来自相关状态的产物。
  • 针对预期初始视图和任何要求的最低支持视口或窗口大小的视口适配检查通过。
  • 若产品以窗口形式启动,在手动调整大小或位置前,检查启动时的窗口大小、位置和初始布局。
  • 截图直接支持你做出的声明。
  • 已针对QA过程中确定的相关状态和视口/窗口大小审查所需截图。
  • UI不仅功能正常,且视觉连贯,针对任务而言具有良好的美学表现。
  • 功能正确性、视口适配和视觉质量必须分别通过;其中一项通过不代表其他项也通过。
  • 交互式产品已完成简短探索性测试,且响应中提及测试覆盖内容。
  • 若截图审查与数值检查存在分歧,签署确认前需调查差异;截图中可见的裁剪是必须解决的故障,而非指标可推翻的问题。
  • 简要说明你检查过且未发现的主要缺陷类别。
  • 已执行清理,或有意保持会话存活以便后续工作。

Screenshot Examples

截图示例

Prefer JPEG at
quality: 85
for
view_image
artifacts unless lossless inspection is specifically required.
Desktop example:
javascript
const { unlink } = await import("node:fs/promises");
const desktopPath = `${codex.tmpDir}/desktop.jpg`;

await page.screenshot({ path: desktopPath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: desktopPath });
await unlink(desktopPath).catch(() => {});
Electron example:
javascript
const { unlink } = await import("node:fs/promises");
const electronPath = `${codex.tmpDir}/electron-window.jpg`;

await appWindow.screenshot({ path: electronPath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: electronPath });
await unlink(electronPath).catch(() => {});
Mobile example:
javascript
const { unlink } = await import("node:fs/promises");

if (!mobileContext) {
  mobileContext = await browser.newContext({
    viewport: { width: 390, height: 844 },
    isMobile: true,
    hasTouch: true,
  });
  mobilePage = await mobileContext.newPage();
}

await mobilePage.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
const mobilePath = `${codex.tmpDir}/mobile.jpg`;
await mobilePage.screenshot({ path: mobilePath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: mobilePath });
await unlink(mobilePath).catch(() => {});
除非特别需要无损检查,否则
view_image
产物优先使用质量为85的JPEG格式。
桌面端示例:
javascript
const { unlink } = await import("node:fs/promises");
const desktopPath = `${codex.tmpDir}/desktop.jpg`;

await page.screenshot({ path: desktopPath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: desktopPath });
await unlink(desktopPath).catch(() => {});
Electron端示例:
javascript
const { unlink } = await import("node:fs/promises");
const electronPath = `${codex.tmpDir}/electron-window.jpg`;

await appWindow.screenshot({ path: electronPath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: electronPath });
await unlink(electronPath).catch(() => {});
移动端示例:
javascript
const { unlink } = await import("node:fs/promises");

if (!mobileContext) {
  mobileContext = await browser.newContext({
    viewport: { width: 390, height: 844 },
    isMobile: true,
    hasTouch: true,
  });
  mobilePage = await mobileContext.newPage();
}

await mobilePage.goto(TARGET_URL, { waitUntil: "domcontentloaded" });
const mobilePath = `${codex.tmpDir}/mobile.jpg`;
await mobilePage.screenshot({ path: mobilePath, type: "jpeg", quality: 85 });
await codex.tool("view_image", { path: mobilePath });
await unlink(mobilePath).catch(() => {});

Viewport Fit Checks (Required)

视口适配检查(必填)

Do not assume a screenshot is acceptable just because the main widget is visible. Before signoff, explicitly verify that the intended initial view matches the product requirement, using both screenshot review and numeric checks.
  • Define the intended initial view before signoff. For scrollable pages, this is the above-the-fold experience. For app-like shells, games, editors, dashboards, or tools, this is the full interactive surface plus the controls and status needed to use it.
  • Use screenshots as the primary evidence for fit. Numeric checks support the screenshots; they do not overrule visible clipping.
  • Signoff fails if any required visible region is clipped, cut off, obscured, or pushed outside the viewport in the intended initial view, even if page-level scroll metrics appear acceptable.
  • Scrolling is acceptable when the product is designed to scroll and the initial view still communicates the core experience and exposes the primary call to action or required starting context.
  • For fixed-shell interfaces, scrolling is not an acceptable workaround if it is needed to reach part of the primary interactive surface or essential controls.
  • Do not rely on document scroll metrics alone. Fixed-height shells, internal panes, and hidden-overflow containers can clip required UI while page-level scroll checks still look clean.
  • Check region bounds, not just document bounds. Verify that each required visible region fits within the viewport in the startup state.
  • For Electron or desktop apps, verify both the launched window size and placement and the renderer's initial visible layout before any manual resize or repositioning.
  • Passing viewport-fit checks only proves that the intended initial view is visible without unintended clipping or scrolling. It does not prove that the UI is visually correct or aesthetically successful.
Web or renderer check:
javascript
console.log(await page.evaluate(() => ({
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  clientWidth: document.documentElement.clientWidth,
  clientHeight: document.documentElement.clientHeight,
  scrollWidth: document.documentElement.scrollWidth,
  scrollHeight: document.documentElement.scrollHeight,
  canScrollX: document.documentElement.scrollWidth > document.documentElement.clientWidth,
  canScrollY: document.documentElement.scrollHeight > document.documentElement.clientHeight,
})));
Electron check:
javascript
console.log(await appWindow.evaluate(() => ({
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  clientWidth: document.documentElement.clientWidth,
  clientHeight: document.documentElement.clientHeight,
  scrollWidth: document.documentElement.scrollWidth,
  scrollHeight: document.documentElement.scrollHeight,
  canScrollX: document.documentElement.scrollWidth > document.documentElement.clientWidth,
  canScrollY: document.documentElement.scrollHeight > document.documentElement.clientHeight,
})));
Augment the numeric check with
getBoundingClientRect()
checks for the required visible regions in your specific UI when clipping is a realistic failure mode; document-level metrics alone are not sufficient for fixed shells.
不要仅因主要组件可见就假设截图合格。签署确认前,需同时通过截图审查和数值检查,明确验证预期初始视图是否符合产品需求。
  • 签署确认前定义预期初始视图。对于可滚动页面,这是首屏体验;对于类应用外壳、游戏、编辑器、仪表板或工具,这是完整交互表面加上使用所需的控件和状态信息。
  • 以截图作为适配检查的主要证据。数值检查仅作为截图的补充,不能推翻可见的裁剪问题。
  • 若预期初始视图中任何必要可见区域被裁剪、截断、遮挡或推出视口,即使页面级滚动指标显示正常,签署确认也不通过。
  • 当产品设计为可滚动且初始视图仍能传达核心体验并展示主要行动召唤或必要启动上下文时,滚动是可接受的。
  • 对于固定外壳界面,若需滚动才能访问部分主交互表面或必要控件,则滚动不属于可接受的解决方案。
  • 不要仅依赖文档滚动指标。固定高度外壳、内部窗格和隐藏溢出容器可能会裁剪必要UI,而页面级滚动检查仍显示正常。
  • 检查区域边界,而非仅文档边界。验证启动状态下每个必要可见区域是否适配视口。
  • 对于Electron或桌面应用,在手动调整大小或位置前,验证启动时的窗口大小、位置和渲染器初始可见布局。
  • 视口适配检查通过仅证明预期初始视图在无意外裁剪或滚动的情况下可见,不代表UI视觉正确或美学表现良好。
网页或渲染器检查:
javascript
console.log(await page.evaluate(() => ({
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  clientWidth: document.documentElement.clientWidth,
  clientHeight: document.documentElement.clientHeight,
  scrollWidth: document.documentElement.scrollWidth,
  scrollHeight: document.documentElement.scrollHeight,
  canScrollX: document.documentElement.scrollWidth > document.documentElement.clientWidth,
  canScrollY: document.documentElement.scrollHeight > document.documentElement.clientHeight,
})));
Electron检查:
javascript
console.log(await appWindow.evaluate(() => ({
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  clientWidth: document.documentElement.clientWidth,
  clientHeight: document.documentElement.clientHeight,
  scrollWidth: document.documentElement.scrollWidth,
  scrollHeight: document.documentElement.scrollHeight,
  canScrollX: document.documentElement.scrollWidth > document.documentElement.clientWidth,
  canScrollY: document.documentElement.scrollHeight > document.documentElement.clientHeight,
})));
当裁剪是潜在故障模式时,使用
getBoundingClientRect()
检查特定UI中必要可见区域的边界,以补充数值检查;仅文档级指标不足以覆盖固定外壳界面。

Dev Server

开发服务器

For local web debugging, keep the app running in a persistent TTY session. Do not rely on one-shot background commands from a short-lived shell.
Use the project's normal start command, for example:
bash
npm start
Before
page.goto(...)
, verify the chosen port is listening and the app responds.
For Electron debugging, launch the app from
js_repl
through
_electron.launch(...)
so the same session owns the process. If the Electron renderer depends on a separate dev server (for example Vite or Next), keep that server running in a persistent TTY session and then relaunch or reload the Electron app from
js_repl
.
本地网页调试时,需在持久化TTY会话中保持应用运行。不要依赖短生命周期shell中的一次性后台命令。
使用项目常规启动命令,例如:
bash
npm start
执行
page.goto(...)
前,验证所选端口是否在监听且应用可响应。
Electron调试时,通过
js_repl
_electron.launch(...)
启动应用,确保同一会话拥有进程。若Electron渲染器依赖单独的开发服务器(如Vite或Next),请在持久化TTY会话中保持该服务器运行,然后从
js_repl
重启或重新加载Electron应用。

Cleanup

清理

Only run cleanup when the task is actually finished:
  • This cleanup is manual. Exiting Codex, closing the terminal, or losing the
    js_repl
    session does not implicitly run
    electronApp.close()
    ,
    context.close()
    , or
    browser.close()
    .
  • For Electron specifically, assume the app may keep running if you leave the session without executing the cleanup cell first.
javascript
if (electronApp) {
  await electronApp.close().catch(() => {});
}

if (mobileContext) {
  await mobileContext.close().catch(() => {});
}

if (context) {
  await context.close().catch(() => {});
}

if (browser) {
  await browser.close().catch(() => {});
}

browser = undefined;
context = undefined;
page = undefined;
mobileContext = undefined;
mobilePage = undefined;
electronApp = undefined;
appWindow = undefined;

console.log("Playwright session closed");
If you plan to exit Codex immediately after debugging, run the cleanup cell first and wait for the
"Playwright session closed"
log before quitting.
仅当任务实际完成时才运行清理:
  • 此清理为手动操作。退出Codex、关闭终端或丢失
    js_repl
    会话不会隐式执行
    electronApp.close()
    context.close()
    browser.close()
  • 特别对于Electron,若未先执行清理代码就离开会话,应用可能继续运行。
javascript
if (electronApp) {
  await electronApp.close().catch(() => {});
}

if (mobileContext) {
  await mobileContext.close().catch(() => {});
}

if (context) {
  await context.close().catch(() => {});
}

if (browser) {
  await browser.close().catch(() => {});
}

browser = undefined;
context = undefined;
page = undefined;
mobileContext = undefined;
mobilePage = undefined;
electronApp = undefined;
appWindow = undefined;

console.log("Playwright session closed");
若调试后计划立即退出Codex,请先运行清理代码块,等待
"Playwright session closed"
日志输出后再退出。

Common Failure Modes

常见故障模式

  • Cannot find module 'playwright'
    : run the one-time setup in the current workspace and verify the import before using
    js_repl
    .
  • Playwright package is installed but the browser executable is missing: run
    npx playwright install chromium
    .
  • page.goto: net::ERR_CONNECTION_REFUSED
    : make sure the dev server is still running in a persistent TTY session, recheck the port, and prefer
    http://127.0.0.1:<port>
    .
  • electron.launch
    hangs, times out, or exits immediately: verify the local
    electron
    dependency, confirm the
    args
    target, and make sure any renderer dev server is already running before launch.
  • Identifier has already been declared
    : reuse the existing top-level bindings, choose a new name, or wrap the code in
    { ... }
    . Use
    js_repl_reset
    only when the kernel is genuinely stuck.
  • js_repl
    timed out or reset: rerun the bootstrap cell and recreate the session with shorter, more focused cells.
  • Browser launch or network operations fail immediately: confirm the session was started with
    --sandbox danger-full-access
    and restart that way if needed.
  • Cannot find module 'playwright'
    :在当前工作区运行一次性设置步骤,并在使用
    js_repl
    前验证导入是否成功。
  • Playwright包已安装但浏览器可执行文件缺失:运行
    npx playwright install chromium
  • page.goto: net::ERR_CONNECTION_REFUSED
    :确保开发服务器仍在持久化TTY会话中运行,重新检查端口,优先使用
    http://127.0.0.1:<port>
  • electron.launch
    挂起、超时或立即退出:验证本地
    electron
    依赖,确认
    args
    目标,且启动前渲染器开发服务器已运行。
  • Identifier has already been declared
    :复用现有顶级绑定、选择新名称或将代码包裹在
    { ... }
    中。仅当内核确实卡住时才使用
    js_repl_reset
  • js_repl
    超时或重置:重新运行引导代码块,使用更简短、聚焦的代码块重新创建会话。
  • 浏览器启动或网络操作立即失败:确认会话是使用
    --sandbox danger-full-access
    启动的,必要时以此方式重启。