# @clipboard-health/playwright-toolkit 中文文档

规范地址：https://npmcn.com/package/%40clipboard-health/playwright-toolkit/

版本：1\.5\.15
npm 元数据更新时间：2026-11-05T04:56:10.888Z
中文译文更新时间：2026-11-10T01:57:29.986Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40clipboard-health/playwright-toolkit
原文对照：https://npmcn.com/package/%40clipboard-health/playwright-toolkit/?lang=original
项目主页：https://github.com/ClipboardHealth/core-utils#readme
源码仓库：https://github.com/ClipboardHealth/core-utils

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：216 kB，29 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- 运行依赖：2 个
- peer 依赖：@playwright/test \>=1\.50\.0
- 首次发布：2026-07-16T16:58:48.364Z
- 最新版本发布：2026-11-05T04:56:10.715Z
- 近 30 天下载量：11913（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：51（近 12 个月 51 个）

## 中文简介

共享反花样原生作 Clippboard 健康游戏套房.

## 安装

```sh
npm install @clipboard-health/playwright-toolkit
```

也可以使用：`pnpm add @clipboard-health/playwright-toolkit`、`yarn add @clipboard-health/playwright-toolkit`、`bun add @clipboard-health/playwright-toolkit`

## README

# `@clipboard-health/playwright-toolkit`

共享反花样原生作 Clippboard 健康游戏套房.

该套件拥有重试政策,APM相关,共享admin-token craching,部署-资产检查,JSON响应终生捕获,Mailpit投票,Cognitto登录诊断,并设置重试分类. 消耗寄存器只保留配置和特定域的匹配.

## 安装

```bash
npm install --save-dev @clipboard-health/playwright-toolkit
```

`@playwright/test` 是同级依赖。 该套件支持Playwright 1.50并更新.

## API 地图

| 当地能力 | 包替换 |
| --- | --- |
| `retryWithBail` | `runWithRetry({ mode: { kind: "classified", ... } })` |
| `retryUntilPassOrTimeout` | `runWithRetry({ mode: { kind: "poll", ... } })` |
| 复制已复制的可追溯父页面固定 | `createTraceparentFixtures()` |
| 管理令牌承诺缓存和文件锁定 | `generateAdminAuthToken()` 或者说 `getOrCreateAdminAuthToken()` |
| 部署的前端/流动资产循环 | `verifyDeployedAssets()` 财务报告和审定财务报表 `waitForDeployedAssets()` |
| 被推迟的 Playwright JSON 回复为 | `waitForParsedJsonResponse()` |
| 邮箱搜索/拖网 | `createMailpitClient()`, (中文(简体) ), `fetchMagicLinkFromMailpit()`, (中文(简体) ), `fetchEmailOtpCodeFromMailpit()` |
| 认知OTP 重定向调试 | `fillOtpAndWaitForCognitoRedirect()` |
| 设置 HTTP 和身份重试检查 | `classifySetupRetry()` 财务报告和审定财务报表 `isRetryableHttpStatus()` |

## 重试合同

`runWithRetry` 是此软件包中唯一的复试抽象。 它有两种模式,因为复试和投票回答不同的问题.

### 分类重试

对通常应传递第一次尝试的操作使用分类重试. 打电话者必须提供 `isTransient`。不存在重复任意失败的默认情况.

```typescript
import { runWithRetry } from "@clipboard-health/playwright-toolkit";

const result = await runWithRetry({
  operationName: "create shift offer",
  operation: async () => await createShiftOffer(),
  mode: {
    kind: "classified",
    maxAttempts: 4,
    delayMs: 2000,
    isTransient: ({ error }) => isKnownCdcReadinessError(error),
  },
});

const shiftOffer = result.value;
```

一项合法的分类重试符合Flaky-critic B1合同:

-   手术是安全的重复.
-   `isTransient` 积极识别已知的瞬间状态.
-   验证、许可和其他确定性失败返回 `false`。 。 。 .
-   重试有一个固定的尝试预算.
-   过度抛出 `RetryError` 尝试计数 时间过长 终极原因 以及最后原因.

不使用像每个 `422` 作为上游。 匹配可以在不更改请求的情况下解决的特定准备状态消息或服务条件.

### 投票直到通过

使用民意调查模式来进行"先入为出"的"先入为出"探测器,其中失败意为"尚未就绪"

```typescript
const result = await runWithRetry({
  operationName: "wait for worker profile",
  operation: async () => await assertWorkerProfileReady(),
  mode: {
    kind: "poll",
    timeoutMs: 90_000,
    intervalsMs: [1000, 2000, 3000, 5000],
  },
});
```

设定 `mode.isTransient` 当探测器也可以投出决定性的失败。 正在返回 `false` 立即停下来 `reason: "non-transient"`。 。 。 .

投票模式将每次尝试与剩余超时预算相竞争,并中止尝试 `signal` 当期限到期时。 将信号传递给网络呼叫, 因此超时工作被取消:

```typescript
operation: async ({ signal }) =>
  await fetch(readinessUrl, {
    signal,
  });
```

## JSON 反应寿命

Playwright响应机构可以在文档导航后变得无法读取. 使用 `waitForParsedJsonResponse` 当一个页面动作同时等待 JSON 响应并交叉 a 导航边界。 帮助者分析每个候选人 `waitForResponse` 上游和 仅返回解析值,所以调用者不能不小心保留懒惰 `Response` 手柄. Predicate-local解析缩小了反应-寿命窗口,但无法消除它:铬可以 丢弃基本的 CDP 资源同时 `response.json()` 还有待处理.

```typescript
const workerResponsePromise = waitForParsedJsonResponse({
  page,
  timeoutMs: 30_000,
  isCandidate: ({ response }) =>
    response.ok() && new URL(response.url()).pathname === "/api/worker",
  parseCandidate: ({ body }) => workerResponseSchema.parse(body),
});

await page.reload({ waitUntil: "domcontentloaded" });

const workerResponse = await workerResponsePromise;
```

返回时 `undefined` 从 `parseCandidate` 当 URL 级别候选人有效,但其正文不是时 测试需要的反应。 如果一个候选机构读取失败 准确的铬 `Network.getResponseBody` 编号 : `No resource with given identifier found` 签名,帮助者拒绝 只有那个候选人 并且继续留在 原来的有限服务员。 它不发布另一个 请求或导航,添加重试或睡眠,或扩展超时。 无效的 JSON, 解析和计划 出错, 近似匹配的协议出错, 页面或目标关闭, 并立即发布取消 . 如果外部服务员在分类损失后超时,错误将原超时保留为 其原因,只添加带损失计数、方法、状态和经编辑路径的带界限的诊断 模板。 保持存储器专用的URL,方法,资源类型,以及消费者的策略匹配.

## 每一次检测的痕量父母固定

一次扩展存储器已有的 Playwright 测试对象 :

```typescript
import { test as base } from "@playwright/test";
import {
  createTraceparentFixtures,
  type TraceparentFixtures,
} from "@clipboard-health/playwright-toolkit";

export const test = base.extend<TraceparentFixtures>(createTraceparentFixtures());
```

自动固定器生成一个非零W3C `traceparent`,保留项目级别 `extraHTTPHeaders`,在测试机构运行前在浏览器上下文上安装合并头,并添加一个 `traceparent` 测试注释。 将已有信头传递到 `installTraceparentForTest` 如果自定义固定器手动安装了信头.

## 管理符

`generateAdminAuthToken` 运行 `cbh auth gentoken user`,重复只批准瞬态的 CLI 签名,从错误中编辑管理邮件,并在原子文件系统锁后缓存无记名符号.

```typescript
const tokenEntry = await generateAdminAuthToken({
  adminEmail: adminUser.email,
  apiEnvironmentName: "staging",
  cacheIdentity: {
    namespace: "cbh-admin-frontend",
    clientName: "admin-app",
    issuer: "clipboard-health-staging",
    tokenKind: "access",
  },
  clientName: "admin-app",
  cacheDurationMs: 10 * 60 * 1000,
  validationPolicy: "admin-session-v1",
  validateToken: async ({ authToken, signal }) => {
    const response = await fetch(`${adminApiUrl}/auth/session`, {
      headers: { Authorization: authToken },
      signal,
    });

    if (response.status === 401 || response.status === 403) {
      return false;
    }

    if (!response.ok) {
      throw new Error(`Admin token validation failed with HTTP ${response.status}`);
    }

    return true;
  },
});

const adminAuthToken = tokenEntry.authToken;
```

缓存键包含环境,邮件的SHA-256摘要,以及可选的显式缓存身份;它从未包含邮件本身. 给每个仓库一个稳定 `namespace`,并在这些值有差异时,按符号类型、客户端、受众和发行人区分证书. `generateAdminAuthToken` 自动区分不同的 `clientName` 值即使当 `cacheIdentity` 省略.

JWT 缓存新鲜度以 `cacheDurationMs` 和标志 `exp` 索赔减去60秒安全故障。 不透明无记名标志继续使用 `cacheDurationMs`设置 `jwtExpirationSafetySkewMs` 当一个套房需要不同的skew.

何时 `validateToken` 提供,工具包在保存缓存锁的同时运行只读探测器,并在缓存条目中记录成功验证,所以等待Playwright工人共享一个探测器. 给探测器一个清晰的 `validationPolicy` 当验证合同变得更为严格或有其他变化时,就予以加价; 政策改变促使重新确认。 工具包默认在30秒后中止验证,范围被限制在锁的 stale 租赁下. 通过提供的 `signal` 到网络请求,并设定 `validationTimeoutMs` 当终点需要更短的最后期限时.

返回时 `false` 只有在消费者拒绝证书时,例如HTTP `401` 或者说 `403`; 抛出 `5xx` 反应和其他验证基础设施故障。 一个被拒绝的缓存令牌被拆出,并更换一次给所有等待的工人和共享缓存文件系统的shards. 拒绝生成的令牌不会被缓存 。 探测器应核实相同的受众,并允许在不改变应用程序状态的情况下对套房需求进行分界.

缓存和锁定文件使用模式 `0600`。一个在生成前获取锁重读缓存的流程,该流程防止在 Playwright 工人、硬盘和共享的本地流程之间重复代币薄荷 `cacheDirectory`。单独的 CI 主机或装有独立文件系统的容器可能仍然可以独立地薄荷.

使用 `getOrCreateAdminAuthToken` 当寄存器需要不同的符号命令时 :

```typescript
const tokenEntry = await getOrCreateAdminAuthToken({
  adminEmail,
  apiEnvironmentName,
  cacheIdentity: {
    namespace: "cbh-mobile-app",
    audience: "monolith-api",
    clientName: "mobile-app",
    tokenKind: "access",
  },
  cacheDurationMs: 10 * 60 * 1000,
  createToken: async () => await generateTokenWithRepositoryCli(),
});
```

在消费者拒绝一个标志后 把证书交给 `forceRefresh`。来电者报告同一被拒绝的证书分享一个替代者,包括另一名工人已经刷新缓存后抵达的来电者:

```typescript
import { getOrCreateAdminAuthToken } from "@clipboard-health/playwright-toolkit";

const freshTokenEntry = await getOrCreateAdminAuthToken({
  ...tokenParams,
  forceRefresh: {
    rejectedAuthToken,
  },
});
```

保留 `forceRefresh: true` 在没有被拒绝证书的情况下,故意无条件轮换.

或者,在API中明确使用相同的默认身份的生成符号无效 `generateAdminAuthToken`数字 :

```typescript
import { invalidateGeneratedAdminAuthToken } from "@clipboard-health/playwright-toolkit";

await invalidateGeneratedAdminAuthToken({
  adminEmail,
  apiEnvironmentName,
  clientName: "admin-app",
});
```

使用 `invalidateAdminAuthToken` 页:1 `getOrCreateAdminAuthToken` 条目,通过相同 `cacheIdentity` 用于创建条目.

仅在只读或以其他方式安全重复时自动重试请求. 该工具包不重放消费者请求,因为它无法确定一个失败的突变是否具有副作用.

使用 `onCacheEvent` 安全诊断 事件曝光 `kind`,缓存键和证书指纹,可选受众,和薄荷/过期/验证时间戳. 指纹区分相继的资质,而不披露符号,电子邮件,或原始缓存身份.

## 已部署资产

仓库包裹仍然发现资产,并决定哪些文件是运行时间或指纹。 该套件拥有请求货币,超时,缓存断接,内容类型检查,瞬态HTTP重试,尝试诊断,稳定窗口投票.

```typescript
const report = await waitForDeployedAssets({
  checks: localAssetManifest.map((asset) => ({
    path: asset.path,
    url: new URL(asset.path, deploymentBaseUrl).toString(),
    method: asset.isFingerprintNamedJavaScript ? "GET" : "HEAD",
    cacheMode: asset.isRuntimeAsset ? "cache-busted" : "normal",
    expectedContentTypes: [asset.contentType],
  })),
  timeoutMs: 10 * 60 * 1000,
  pollIntervalMs: 10_000,
  stableWindowMs: 30_000,
});
```

HTTP 密码 `408`, (中文(简体) ), `425`, (中文(简体) ), `429`,以及 `5xx` 答复对于资产交付来说是短暂的。 内容类型不匹配和失败的自定义验证器是决定性的, 除非验证器返回 `isTransient: true`。 。 。 .

使用 `validateResponse` 用于特定存储库的检查,例如: `build-info.json` 承诺匹配。 验证器在调用收发包前不会消耗一个成功的响应机体,因此回调器可以直接读取.

## 邮箱

从寄存器配置创建客户端, 然后使用打入的计票器 :

```typescript
const mailpit = createMailpitClient({
  password: process.env.MAILPIT_PASSWORD ?? "",
});

const code = await fetchEmailOtpCodeFromMailpit({
  client: mailpit,
  email,
  sentAfter: codeRequestedAt,
  excludeCodes: [previousCode],
});

await page.getByLabel("Verification Code").fill(code.value);
```

粉选者搜索最新的第一, 容忍不完整的日期 搜索结果, 获取最多三个候选人 每一次探测, `404`, (中文(简体) ), `408`, (中文(简体) ), `429`,以及 `5xx`。它们返回所取值和源消息ID.

超时错误和成功结果包括民意测验诊断. 这些区别瞬间 从信件中查找错误并计数缓存提取失败的跳过 。 第一个 整个等待中三处提取缺失 仅携带 SHA-256 消息ID散列, 内容 通道、合并文本/HTML字符计数桶、候选人取货年龄和检察官办公室形状. 形状分类遵守记录的四加四检察官办公室格式;它不选择 值或更改参数. `other` 表示内置的检察官办公室分析器确认的值为: 调用者的提取器没有选中 。 样本中没有原始信件字段或提取出的秘密. 该错误为Playwright报告保留了这个证据,但没有单独的日志钩.

Misses仍然被消息ID所缓存,默认等待仍然为60秒. 这些诊断 帮助分类未来的失败;它们不能确定历史消息为何失败.

## 认知检察官办公室和登录诊断

`fillOtpAndWaitForCognitoRedirect` 显示器 `RespondToAuthChallenge` 请求 `SMS_OTP` 财务报告和审定财务报表 `EMAIL_OTP` 等待预期的重定向.

```typescript
await fillOtpAndWaitForCognitoRedirect({
  page,
  testInfo,
  otp,
  expectedUrl: /\/dashboard/,
});
```

在失败时,错误包括已消毒的Cognitto请求摘要,响应或请求失败细节,当前URL,以及可见的页面文本样本. 何时 `testInfo` 并附上经编辑的截图. `sanitizeCognitoDiagnosticText` 财务报告和审定财务报表 `isCognitoOtpChallengeRequest` 是对寄存器特定登录流量的公开.

## 设置重试分类

`classifySetupRetry` 将身份碰撞与短暂的基础设施故障区分开来:

```typescript
const classification = classifySetupRetry({
  error,
  isIdentityCollision: ({ error: candidate }) => isWorkerCreationPhoneCollision(candidate),
});
```

其结果与片面批评C1合同相同:

| 分类 | 决定 | 身份行为 |
| --- | --- | --- |
| `identity-collision` | `regenerate-identity` | 在下次尝试前生成新的电话、电子邮件或ID . |
| `transient` | `retry-same-identity` | 重复同样的请求和身份. |
| `deterministic` | `do-not-retry` | 立即失败. |

`isRetryableHttpStatus` 返回时 `true` 仅限 `408`, (中文(简体) ), `429`,以及 `5xx`。一个寄存器可以添加狭义 `isTransientError` 一个已知的非HTTP瞬间签名的上游.

## 移徙清单

1.  将本地帮助导入替换为包导出 .
2.  将寄存器特定URL,选择器,资产发现,以及错误信息匹配等移动到一薄包装中.
3.  保持重试分类收窄. 记录为什么手术是安全的重复.
4.  对于身份相撞,在重试操作中生成身份,这样每次相撞尝试都会得到一个新的值.
5.  删除本地助手并将其边界测试移植到包装上.
6.  运行消费存储器的 Playwright 单元测试和 E2E 设置检查.

## 常见问题

### @clipboard-health/playwright-toolkit 是什么？

共享反花样原生作 Clippboard 健康游戏套房\.

### 如何安装 @clipboard-health/playwright-toolkit？

在项目目录运行 npm install @clipboard-health/playwright-toolkit，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@playwright/test（\>=1\.50\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @clipboard-health/playwright-toolkit 支持 TypeScript 吗？

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

### @clipboard-health/playwright-toolkit 支持 ESM 还是 CommonJS？

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

### @clipboard-health/playwright-toolkit 可以商用吗？

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

### @clipboard-health/playwright-toolkit 的下载量有多少？

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

### @clipboard-health/playwright-toolkit 还在维护吗？

最新版本 1\.5\.15 发布于 2026/11/05（4 天前）；至今共发布 51 个版本，其中过去 12 个月发布了 51 个；首次发布于 2026/07/17。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

