@clipboard-health/playwright-reporter-llm

中文文档
2.12.15发布于 2026/11/05MITTypeScriptCommonJS近 30 天下载 2.4万

Playwright reporter,为LLM代理输出结构化的JSON. 最小控制台输出,平分计划,容易被过滤出故障.

中文使用文档
文档目录13 节
  1. 目录
  2. 安装
  3. 使用量
  4. 选项
  5. 主控台输出
  6. 要读什么,按任务
  7. 最小失败实例
  8. Flaky 测试实例( pass- vs- fault 比较)
  9. 完整实例报告
  10. 外地参考
  11. 浏览器生命周期附件合同
  12. 为什么Playwright不是内置的JSON记者?
  13. 地方发展指挥部

@clippboard-health/ playwright-reporter-llm 页面存档备份,存于互联网档案馆 @clippboard-health/ playwright-reporter-llm

Playwright reporter,为LLM代理输出结构化的JSON. 最小控制台输出,平分计划,容易被过滤出故障.

目录

安装

npm install @clipboard-health/playwright-reporter-llm
// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  reporter: [
    ["@clipboard-health/playwright-reporter-llm", { outputFile: "test-results/llm-report.json" }],
  ],
});

使用量

选项

选项默认说明
outputFiletest-results/llm-report.jsonJSON 输出路径

主控台输出

.......F..........F.S....
26 tests | 23 passed | 2 failed | 1 skipped (4.2s)
Report: test-results/llm-report.json

要读什么,按任务

任务字段
出入/故障概览summary
曲折失败过滤器 tests[] 以 status === "failed",然后读取 errors[0].{message,diff,location,snippet}
诊断定位器分离tests[].errors[0].{apiName,selector,actionLog} 失败 tests[].attempts[].error.{apiName,selector,actionLog} 对于失败的动作的解析定位日志
识别碎片过滤器 tests[] 以 flaky === true; 比较 attempts[] 状态
重建故障时间表选一个尝试 status !== "passed" 改为: .timeline[] (步骤+网络+控制台,由 offsetMs(中文(简体) )
检查失败的请求tests[].attempts[].network.instances[] 过滤器通过 status >= 400,或加入 groups[groupId].{failureText,wasAborted}
查找请求正文tests[].attempts[].network.bodies[instance.requestBodyRef | instance.responseBodyRef]
关联浏览器和后端生命周期tests[].attempts[].network.instances[].clientLifecycle 加号 .traceId 编号 : .spanId 编号 : .requestId 编号 : .correlationId
调试未发现的页面错误tests[].attempts[].consoleMessages[] 过滤器通过 type 内 "error" | "pageerror" | "page-crashed"
视觉调试tests[].attempts[].failureArtifacts.{screenshotBase64,videoPath}

最小失败实例

走开 attempts[].timeline[] (步骤+网络+控制台,由 offsetMs)重建失败. 每个时间线网络条目包含一个 networkId 你可以解决 attempts[].network.instances[] 详细情况 attempts[].network.groups[] 编号 : bodies[] 用于共享形状/负载上下文:

{
  "schemaVersion": 3,
  "summary": {
    "total": 10,
    "passed": 9,
    "failed": 1,
    "flaky": 0,
    "skipped": 0,
    "timedOut": 0,
    "interrupted": 0
  },
  "tests": [
    {
      "id": "abc123",
      "title": "Checkout > applies discount code",
      "status": "failed",
      "flaky": false,
      "location": { "file": "tests/checkout.spec.ts", "line": 42, "column": 5 },
      "errors": [
        {
          "message": "Expected: 90\nReceived: 100",
          "diff": { "expected": "90", "actual": "100" },
          "location": { "file": "tests/checkout.spec.ts", "line": 58, "column": 7 }
        }
      ],
      "attempts": [
        {
          "attempt": 1,
          "status": "failed",
          "failureArtifacts": { "screenshotBase64": "iVBORw0KGgo...", "videoPath": "video.webm" },
          "timeline": [
            {
              "kind": "step",
              "offsetMs": 1050,
              "title": "click Apply",
              "category": "test.step",
              "durationMs": 40,
              "depth": 0
            },
            {
              "kind": "network",
              "offsetMs": 1100,
              "networkId": "n3",
              "method": "POST",
              "url": "https://api.example.com/discount",
              "status": 500
            },
            {
              "kind": "console",
              "offsetMs": 1240,
              "type": "pageerror",
              "text": "Uncaught TypeError: discount is undefined"
            },
            {
              "kind": "step",
              "offsetMs": 1300,
              "title": "expect total to equal 90",
              "category": "pw:api",
              "durationMs": 5,
              "depth": 0,
              "error": "Expected: 90\nReceived: 100"
            }
          ]
        }
      ]
    }
  ]
}

读取为: 点击“ 应用 ” 后端返回 500 ” 。 决定 networkId: "n3" 反对 attempts[0].network.instances 按请求 traceId 编号 : spanId 编号 : requestId /体推器; groups[instance.groupId] (c) 保持形状和总数(发生率,第一/最后冲抵).

Flaky 测试实例( pass- vs- fault 比较)

对于尝试1失败、尝试2失败的试验,两个时限之间的分歧是诊断。 查找第一个不同条目:

{
  "tests": [
    {
      "title": "Checkout > shows confirmation",
      "status": "passed",
      "flaky": true,
      "attempts": [
        {
          "attempt": 1,
          "status": "failed",
          "timeline": [
            {
              "kind": "step",
              "offsetMs": 800,
              "title": "goto /checkout",
              "category": "test.step",
              "durationMs": 120,
              "depth": 0
            },
            {
              "kind": "network",
              "offsetMs": 950,
              "networkId": "n0",
              "method": "GET",
              "url": "https://api.example.com/cart",
              "status": 200
            },
            {
              "kind": "step",
              "offsetMs": 1400,
              "title": "expect banner visible",
              "category": "pw:api",
              "durationMs": 5000,
              "depth": 0,
              "error": "Timeout 5000ms exceeded"
            }
          ]
        },
        {
          "attempt": 2,
          "status": "passed",
          "timeline": [
            {
              "kind": "step",
              "offsetMs": 800,
              "title": "goto /checkout",
              "category": "test.step",
              "durationMs": 120,
              "depth": 0
            },
            {
              "kind": "network",
              "offsetMs": 950,
              "networkId": "n0",
              "method": "GET",
              "url": "https://api.example.com/cart",
              "status": 200
            },
            {
              "kind": "network",
              "offsetMs": 1100,
              "networkId": "n1",
              "method": "GET",
              "url": "https://api.example.com/inventory",
              "status": 200
            },
            {
              "kind": "step",
              "offsetMs": 1350,
              "title": "expect banner visible",
              "category": "pw:api",
              "durationMs": 40,
              "depth": 0
            }
          ]
        }
      ]
    }
  ]
}

分歧:通过的努力增加了 /inventory 称失败的尝试没有。 这要么是僵硬的前端缓存,要么是比赛——不是片状测试,是真正的bug.

完整实例报告

见 docs/example-report.json 对于包含代表性可选字段的完整报告(网络计时、重定向链、头条、附件、步巢、多尝试重试).

外地参考

  • summary -- 快速通过/失效计数
  • tests[].errors[].message - ANSI- stripped, clean 错误文本
  • tests[].errors[].diff -- 从断言错误中提取的预期/实际
  • tests[].errors[].location -- 准确的文件和失败行
  • tests[].errors[].{apiName,selector,actionLog} 编号 : tests[].attempts[].error.{apiName,selector,actionLog} - 无法在可用的跟踪日志中显示 Playwright 动作上下文, 包括在片面测试中失败的尝试 。 用于定位和解离的 DOM 失败; 动作日志消息上限为 512 个字符, 每个错误最多 20 个条目, 并带有省略标记
  • tests[].flaky -- 如果测试在重试后通过, 则为真
  • tests[].attempts[] - 全面重试历史,包括每个试题状态、时间、标准、附件、步骤和网络
  • tests[].attempts[].consoleMessages[] -- 警告/错误/页码/页码/页码/页码微量条目(2KB文本封顶,带有 [truncated] 标记,每次尝试最多为50个,高信号条目优先于低信号)
  • tests[].steps 编号 : tests[].network 编号 : tests[].timeline -- 最后尝试中的便利别名
  • tests[].attempts[].timeline[] - 统一 排序offsetMs 所有保留事件数组kind: "step" | "network" | "console") (中文(简体) ). 快速时间扫描的闪烁-下行条目;所有细节保留在源数组中
  • offsetMs - 从尝试开始的毫秒 startTime。总是在步骤上(从 TestStep.startTime) (中文(简体) ). 在网络条目上可选(来自微量) _monotonicTime 或者说 startedDateTime,通过微量转换 context-options 主播)和控制台条目(来自微量单调) time 字段 + 锚 。 追踪缺失时缺席 context-options 活动。 没有条目 offsetMs 从时间表中排除
  • tests[].attempts[].network - 一个三级 NetworkReport 分开 实例 (发生了什么事), 组 (共享形状),以及 机构 (负载). 访问模式 :
    • 过滤器 network.instances[] 以 status, (中文(简体) ), redirectFromId, (中文(简体) ), traceId等查找特定事件
    • 扫描 network.groups[groupId] 用于集合形状信息(resourceType, (中文(简体) ), failureText, (中文(简体) ), wasAborted, (中文(简体) ), occurrenceCount, (中文(简体) ), retainedInstanceCount, (中文(简体) ), suppressedInstanceCount, (中文(简体) ), evictedInstanceCount, (中文(简体) ), firstOffsetMs, (中文(简体) ), lastOffsetMs(中文(简体) )
    • 决定 network.bodies[instance.requestBodyRef] 编号 : [instance.responseBodyRef] jSON/文本有效载荷(2KB盖有) [truncated] 标记, canonicalized: false (v3.0) (中文(简体) )
    • network.summary 提供端到端会计: observedInstances === retainedInstances + instancesDroppedByFilter + instancesDroppedByGroupCap + instancesDroppedByInstanceCap + instancesSuppressedAsDuplicate + instancesEvictedAfterAdmission对于每一个群体, occurrenceCount === retainedInstanceCount + suppressedInstanceCount + evictedInstanceCount
  • 保留政策 - 案件上限为500起,团体上限为200起,尸体上限为100起。 低信号静态资产(script/stylesheet/image/font/media有2xx且无故障)被投放到过滤器. 同一形状的复制被取出样本:前3个总是承认,后1-in-10. 在压力下,驱逐按优先级划分(5xxx > 可操作连接/DNS/TLS故障 > 4xxx > 成功xhr/fatch > 平地中止 > 未知 > 其他已知的 > 静态资产) < ——关系拒绝而不是动摇.
  • tests[].attempts[].network.instances[].{traceId,spanId,requestId,correlationId} - 说吧 traceId 财务报告和审定财务报表 spanId 来自响应W3C traceparent 当可用时。 如果回复无法读取, Datadog 请求标题(x-datadog-trace-id 编号 : x-datadog-parent-id优先于相冲突的请求 traceparent 并实现从无符号小数到零加分小数的正态. requestId 财务报告和审定财务报表 correlationId 来自 x-request-id 编号 : x-correlation-id。错误的和全零的痕量识别符被拒绝.
  • tests[].attempts[].network.instances[].clientLifecycle - 添加剂chema-v3浏览器生命周期诊断 browser-network-lifecycle JSON附着。 记者通过方法,出处,消毒路径模板,以及最近的请求启动时间来加入记录. 生命周期的区别 no_response_headers, (中文(简体) ), headers_without_body_completion, (中文(简体) ), network_failure,以及 completed,并可以保留提供的请求/答复/补全/失败时间、待时状态、不透明的关联识别符、协议/连接元数据以及编码字节计数.
  • 重定向 - 说吧 instance.redirectFromId 编号 : instance.redirectToId 引用兄弟姐妹实例 id; 将图表走入重建链条.
  • tests[].attempts[].failureArtifacts -- 对于失败/出厂/中断的尝试: screenshotBase64 (基座64-编码截图,最大为512KB), videoPath (第一个视频附件路径). 无法获取截图或视频时完全关闭
  • tests[].attachments[].path -- 相对于 Playwright 输出 Dir
  • tests[].stdout 编号 : tests[].stderr - 上限为4KB [truncated] 标记

浏览器生命周期附件合同

生产商应进口 encodeBrowserLifecycleAttachment 而不是定义自己的计划或尺寸限制。 编码器对记录和命令进行消毒 no_response_headers, (中文(简体) ), headers_without_body_completion,以及 network_failure 常规前的记录 completed ,并返回一个保证不超过共享 64 KiB 限制的天体:

import {
  encodeBrowserLifecycleAttachment,
  type BrowserLifecycleRecord,
} from "@clipboard-health/playwright-reporter-llm";

const records: BrowserLifecycleRecord[] = [];
const encoded = encodeBrowserLifecycleAttachment({ records });

await testInfo.attach("browser-network-lifecycle", {
  body: encoded.body,
  contentType: "application/json",
});

BROWSER_LIFECYCLE_ATTACHMENT_SCHEMA 曝光版本,最大字节和最大记录. encoded.attachment.truncated 当呼叫者提前报告截取或编码器丢弃无效或超限记录时,即设定;结果也显示、包含和丢弃记录数.

编码器释放出schema 第2版. 每个版本都识别出一个线形; 第2版使用制作方的嵌入式生命周期事件:

{
  "schemaVersion": 2,
  "truncated": false,
  "records": [
    {
      "method": "GET",
      "origin": "https://api.example.com",
      "pathTemplate": "/api/v1/:workplaceId/cases",
      "playwrightRequestKey": "request-17",
      "cdpRequestId": "1234.56",
      "loaderId": "loader-1",
      "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
      "spanId": "00f067aa0ba902b7",
      "apiGatewayRequestId": "A0XEghTXPHcEScg=",
      "requestStarted": {
        "cdp": {
          "utc": "2026-07-20T18:35:43.100Z",
          "monotonicMilliseconds": 12345.1
        }
      },
      "responseReceived": {
        "cdp": {
          "utc": "2026-07-20T18:35:43.168Z",
          "monotonicMilliseconds": 12413.1
        }
      },
      "loadingFinished": {
        "cdp": {
          "utc": "2026-07-20T18:35:43.170Z",
          "monotonicMilliseconds": 12415.1
        }
      },
      "protocol": "h2",
      "connection": {
        "id": 17,
        "reused": true,
        "remoteEndpoint": {
          "ipAddress": "10.0.0.12",
          "port": 443
        }
      },
      "encodedBytes": {
        "data": 421,
        "responseHeaders": 256,
        "total": 677
      },
      "classification": "completed"
    }
  ]
}

对于失败, loadingFailed 可以提供嵌入式 CDP 或 Playwright 时间戳加 errorText (仅限于铬) net::ERR_* 价值, canceled, (中文(简体) ), blockedReason,以及 corsErrorStatus. 记者喜欢CDP时间戳并倒回Playwright. 未知字段被丢弃 。 原出处减少为方案/主机/端口,查询和从中去除片段文本 pathTemplate,标识符被检查格式/长度,并且只释放了已记录的许可列表.

固定件可以附加编码为 browser-network-lifecycle 或者说 browser-network-lifecycle.json 有内容类型 application/json,直接输入 body 或通过 path。记者独立执行相同的字节和记录限制; clientLifecycle.truncated 标记制片人或记者侧截取。 遗留的平面版本一的读取器仍然用于现有的附件,但新制作者只能通过共享编码器来发出版本二.

为什么Playwright不是内置的JSON记者?

这个图书馆专门供特工使用:

  • JSON而不是减分. 其他以LLM为主的记者会发出一个减分摘要,这个摘要容易被读取并难于后处理. 特工们不能廉价地过滤到"只是测试失败"或"只是4xx/5xx请求失败的尝试"而不重新分解出道词. JSON有平地,有记录的图案 让特工 jq 或索引到它们需要的字段.
  • 更好的片状测试诊断. 以统一、时间顺序重试历史 timeline[] 步骤、网络和操纵器 失败的尝试与过去的努力之间的分歧通常是诊断(见 flash 测试示例) (中文(简体) ).
  • 更好的后端追踪关联. traceId 财务报告和审定财务报表 spanId 从响应W3C解析 traceparent 头部,带有Datadog request-context为无法读取的回放,因此代理可以从失败测试跳到实际选择的后端上下文.
  • 更好的信号过滤. 网络优先级保留,控制台过滤,并允许列出标题. 代理在噪音上读取未过滤到的痕量会烧掉活物;这位记者在前面进行过滤.

地方发展指挥部

见 package.json scripts 用于命令列表.

版本与发布记录

@clipboard-health/playwright-reporter-llm 共发布 182 个版本,首次发布于 2026/02/26;过去 12 个月发布了 182 个版本。

版本发布日期距今
2.12.15latest2026/11/054 天前
2.12.142026/11/055 天前
2.12.132026/11/045 天前
2.12.122026/10/3110 天前
2.12.112026/10/2812 天前
2.12.102026/10/2813 天前

关于 @clipboard-health/playwright-reporter-llm 的常见问题

@clipboard-health/playwright-reporter-llm 是什么?

Playwright reporter,为LLM代理输出结构化的JSON. 最小控制台输出,平分计划,容易被过滤出故障.

如何安装 @clipboard-health/playwright-reporter-llm?

在项目目录运行 npm install @clipboard-health/playwright-reporter-llm,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@playwright/test(>=1.50.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@clipboard-health/playwright-reporter-llm 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@clipboard-health/playwright-reporter-llm 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@clipboard-health/playwright-reporter-llm 可以商用吗?

@clipboard-health/playwright-reporter-llm 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@clipboard-health/playwright-reporter-llm 的下载量有多少?

根据 npm 官方下载统计,@clipboard-health/playwright-reporter-llm 在 2026/10/10 – 2026/11/08 这 30 天内被下载 24,159 次,日均约 805 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@clipboard-health/playwright-reporter-llm 还在维护吗?

最新版本 2.12.15 发布于 2026/11/05(4 天前);至今共发布 182 个版本,其中过去 12 个月发布了 182 个;首次发布于 2026/02/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。