# @blotch/angular-lynx-testing-library 中文文档

规范地址：https://npmcn.com/package/%40blotch/angular-lynx-testing-library/

版本：22\.2\.18
npm 元数据更新时间：2026-11-08T01:59:40.081Z
中文译文更新时间：2026-11-08T08:08:40.271Z
软件包许可：未声明

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

原始 npm 页面：https://www.npmjs.com/package/%40blotch/angular-lynx-testing-library
原文对照：https://npmcn.com/package/%40blotch/angular-lynx-testing-library/?lang=original
项目主页：https://github.com/Blotch-Smart-Frames/AngularLynx#readme
源码仓库：https://github.com/Blotch-Smart-Frames/AngularLynx

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

- 包体积（解压后）：121 kB，26 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：3 个
- peer 依赖：@angular/core ^22\.2\.0，@angular/compiler ^22\.2\.0，@blotch/angular-lynx ^22\.2\.0，@angular/platform-browser ^22\.2\.0
- 首次发布：2026-10-12T03:00:13.380Z
- 最新版本发布：2026-11-08T01:59:39.696Z
- 近 30 天下载量：3489（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：20（近 12 个月 20 个）

## 中文简介

AngularLynx 应用程序的测试工具

## 安装

```sh
npm install @blotch/angular-lynx-testing-library
```

也可以使用：`pnpm add @blotch/angular-lynx-testing-library`、`yarn add @blotch/angular-lynx-testing-library`、`bun add @blotch/angular-lynx-testing-library`

## README

# @blotch/angular-lynx-测试-图书馆

测试公用事业 [角线](https://github.com/Blotch-Smart-Frames/AngularLynx) apps. Renders Angular 独立组件通过 Lynx PAPI 输入一棵 JSDOM 树,然后通过 [@ testing-library/dom (英语)](https://testing-library.com/docs/dom-testing-library/intro) 查询,也可以读取 Lynx 的 `accessibility-*` 属性.

## 安装

```sh
ng generate @blotch/angular-lynx:add-testing
```

它安装了这个软件包(和Vitest和jsdom,如果项目没有它们; `ng new` 和确保 `ng test` 运行通过AngularLynx的单元测试构建器. 用手安装它: `npm install --save-dev @blotch/angular-lynx-testing-library`。 。 。 .

## 设置

无。 测试运行于 `ng test`建造者`@blotch/angular-lynx-build:unit-test`,哪个 `ng add` 设置)在测试前加载此包的设置,只要项目有它. 没有 Vitest 配置或设置文件可以写入 .

设置:

-   启用 Angular JIT 汇编(`@angular/compiler`),用于运行时编译的组件
-   安装 `LynxTestingEnv` 在 jsdom 窗口上
-   切换到 Lynx 主线程( PAPI 函数所在)
-   Polyfills PAPI 功能测试环境不执行(`__GetParent`, (中文(简体) ), `__QuerySelector`页:1
-   制造 `lynx.reportError` 就像Lynx那样 回去警告你 而不是扔出去 所以 `@boundary` 以默认错误处理器显示其倒置
-   安装 `@testing-library/jest-dom` 匹配点

`ng test` 像应用程序一样,提前编译测试,严格检查模板,就像Angular Language Service所做的:使用 Lynx 元素导入的组件 `LYNX_ELEMENTS` 从 `@blotch/angular-lynx`。下面的例子.

### 没有角CLI

在纯 Vitest 下, 自动加载设置, 并定义 AngularLynx 运行时读取的编译时的标记( rsbuilt 插件在应用程序中定义它们, 以及 `ng test` 设置它们:

```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  define: {
    __DEV__: JSON.stringify(true),
    __MAIN_THREAD__: JSON.stringify(true),
    __WEB__: JSON.stringify(false),
    __PROFILE__: JSON.stringify(false),
    __ENABLE_SSR__: JSON.stringify(false),
  },
  // Angular's JIT compiler reads the metadata of legacy decorators.
  oxc: { decorator: { legacy: true } },
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['@blotch/angular-lynx-testing-library/setup'],
  },
});
```

## API 密码

### `render(component, options?)`

Bootstrap 独立角构件并返回 JSDOM 容器 `@testing-library/dom` 与它相连的询问.

```ts
import { render } from '@blotch/angular-lynx-testing-library';

const { container, getByText, queryByRole } = await render(MyComponent);
```

`render` 实值 **自动同步** - 这个 `await`角质 `createApplication`,然后把组件绑住。 初始变化检测周期在承诺解决之前运行,因此容器可以立即查询.

**选项 :**

| 选项 | 类型 | 说明 |
| --- | --- | --- |
| `providers` | `(Provider | EnvironmentProviders)[]` | 更多供应商注入应用程序(服务、信使、模拟). |
| `queries` | `Queries` | 自定义要绑定到 `container`图书馆旁边. |
| `inputs` | `RenderInputs<C>` | 元件输入的值,按出名键为模板绑定(一个别名输入的别名). 设定在第一次更改检测之前 . |

```ts
const { getByText, rerender } = await render(Greeting, {
  inputs: { name: 'Lynx' },
});

// Keeps the component and changes only the inputs passed.
await rerender({ inputs: { name: 'Angular' } });
```

`rerender({ inputs })` 只能更改输入 `render()` 设定: 角置入 `inputBinding()`,而带有输入约束的组件拒绝其他输入(和 `componentRef.setInput()`) (中文(简体) ). 给 `render()` 输入的起始值.

`provideZonelessChangeDetection()` 财务报告和审定财务报表 `provideRenderer()` ——你不需要提供.

**返回值:**

| 财产 | 类型 | 说明 |
| --- | --- | --- |
| `container` | `Element` | 司法事务司 `<page>` 根元素。 所有的孩子都是后代. |
| `unmount` | `() => void` | 摧毁Angular应用程序. |
| `rerender` | `({ inputs }) => Promise<RenderResult>` 或者说 `(component, options?) => Promise<RenderResult>` | 变化投入 `render()` 设置,保留组件;或使另一个组件处于其位置. |
| `...queries` | —— 说 | 每个查询在 [`queries`](https://npmcn.com/package/%40blotch/angular-lynx-testing-library/#readme-queries) 有义务 `container` (单位:千美元)`getByText`, (中文(简体) ), `getByRole`, (中文(简体) ), `getByAccessibilityLabel`(见a/c.5/49/70,附件). |

### `cleanup()`

摧毁当前 Angular app并重置 Lynx 环境 。 每次测试后自动调用——你只需要这个手动控制.

```ts
import { cleanup } from '@blotch/angular-lynx-testing-library';

afterEach(cleanup);
```

设定 `ATL_SKIP_AUTO_CLEANUP=true` 以禁用自动清理.

### `waitForUpdate()`

等Angular的无区变化检测调度器冲出待反应更新,再强制同步CD循环.

```ts
import { waitForUpdate } from '@blotch/angular-lynx-testing-library';

const count = signal(0);
count.set(1);
await waitForUpdate();
// DOM now reflects count = 1
```

在事件处理器外发生任何信号突变后调用此功能( 例如: direct `.set()` 在测试代码中).

下 `vi.useFakeTimers()`改变探测器 等待假钟, 所以没有什么能使真正的钟. `waitForUpdate()` 等待一个零延迟 `setTimeout` 然后:开始,移动时钟(`await vi.advanceTimersByTimeAsync(1)`等待着它.

### `fireEvent`

JSDOM分子的Fire Lynx事件. 在标准 Lynx 事件中使用指定的助手 :

```ts
import { fireEvent } from '@blotch/angular-lynx-testing-library';

fireEvent.tap(element);
fireEvent.input(element, { value: 'hello' });
fireEvent.scroll(element, { scrollTop: 100 });
```

一个被命名的帮助者运行一个处理器,在同一顺序下:

-   **触摸事件发泡.** 水龙头,长按或触摸 运行元素自己的 `(bindtap)` 或者说 `(catchtap)`,然后它的先辈们在上页的路上。 所以,在一行 内可压卡 到达卡的 `(click)`。 。 。 . `(capture-bind…)` 财务报告和审定财务报表 `(capture-catch…)` 处理者在他们之前跑, 从一页下.
-   **鼠标、轮子和关键事件泡沫**,正如 Lynx 在桌面和网络上发送.
-   **A 类 `catch` 处理器停止事件** 运行地点: a `(catchtap)` 面板可以防止触摸到背后的背景.
-   **其他事件则继续发生.** `scroll`, (中文(简体) ), `input`, (中文(简体) ), `layoutchange` 其余的只能运行 元素自己的处理器.

`init.bubbles` 覆盖事件本身: `fireEvent.tap(element, { bubbles: false })` 只运行元素的处理器.

`init.eventType` 射击一种处理器 在元素上,没有其他的: `fireEvent.tap(element, { eventType: 'catchEvent' })` 发送 DOM 事件 `"catchEvent:tap"`,它只达到元素的 `(catchtap)`。 。 。 .

`init.detail` 是事件本身的数据,作为设备发送: a `<image>`'大小一旦装入,说.

```ts
fireEvent.load(image, { detail: { width: 240, height: 340 } });
```

要直接发送原始 DOM 事件 :

```ts
fireEvent(element, new Event('bindEvent:tap'));
```

A 类 `CustomEvent`是因为 `detail` 也到达处理器: `fireEvent(element, new CustomEvent('bindEvent:load', { detail }))`。 。 。 .

**可用的命名助手 :**

`tap` • 妇女 `longtap` • 妇女 `touchstart` • 妇女 `touchmove` • 妇女 `touchcancel` • 妇女 `touchend` • 妇女 `longpress` • 妇女 `keydown` • 妇女 `keyup` • 妇女 `mousedown` • 妇女 `mouseup` • 妇女 `mousemove` • 妇女 `mouseclick` • 妇女 `mousedblclick` • 妇女 `mouselongpress` • 妇女 `wheel` • 妇女 `scroll` • 妇女 `scrollend` • 妇女 `scrolltoupper` • 妇女 `scrolltolower` • 妇女 `contentsizechanged` • 妇女 `scrolltoupperedge` • 妇女 `scrolltoloweredge` • 妇女 `scrolltonormalstate` • 妇女 `uiappear` • 妇女 `uidisappear` • 妇女 `focus` • 妇女 `blur` • 妇女 `input` • 妇女 `confirm` • 妇女 `layoutchange` • 妇女 `load` • 妇女 `error` • 妇女 `bgload` • 妇女 `bgerror` • 妇女 `transitionstart` • 妇女 `transitionend` • 妇女 `transitioncancel` • 妇女 `animationend`

### `eventMap`

Lynx事件登记簿 每个条目有一个 `defaultInit` 对象 : `{ bubbles: true }` 对于事件 Lynx发送 上升的反应链, `{}` 剩下的钱.

```ts
import { eventMap } from '@blotch/angular-lynx-testing-library';

console.log(Object.keys(eventMap));
// ['tap', 'longtap', 'touchstart', ...]
```

### `*ByAccessibilityLabel`

通过它们查找元素 `accessibility-label`一个屏幕阅读器读取的关于Lynx的文本 测试库 `*ByLabelText` 读取 `<label>` 财务报告和审定财务报表 `aria-*` 属性, Lynx 元素没有.

```ts
const { getByAccessibilityLabel } = await render(Toolbar);

fireEvent.tap(getByAccessibilityLabel('Close'));
```

他们来自测试图书馆的六个变体(`getBy`, (中文(简体) ), `getAllBy`, (中文(简体) ), `queryBy`, (中文(简体) ), `queryAllBy`, (中文(简体) ), `findBy`, (中文(简体) ), `findAllBy`,并取取其文本批量(字符串、 RegExp 或函数)和匹配者选项(`exact`, (中文(简体) ), `normalizer`) (中文(简体) ).

### `*ByRole`

测试库 `*ByRole`,读取 Lynx 的无障碍属性就像屏幕阅读器一样. 元素测试库给出一个仍然匹配的角色,例如 `<input>` 作为 `textbox`。 。 。 .

```ts
const { getByRole } = await render(Toolbar);

fireEvent.tap(getByRole('button', { name: 'Save' }));
expect(getByRole('button', { name: 'Week', selected: true })).toBeTruthy();
```

| `accessibility-traits` | 作用 |
| --- | --- |
| `button` | `button` |
| `link` | `link` |
| `image` | `img` |
| `header` | `heading` |
| `search` | `searchbox` |
| `adjustable` | `slider` |
| `tabbar` | `tablist` |

-   **作用**: 从桌子上的每个特征。 一个 `<image>` 是一个 `img` 除非设定 `accessibility-traits`。 。 。 .
-   **可达**: 屏幕阅读器到达 `<text>`, (中文(简体) ), `<image>`,其他要素 `accessibility-element="true"`外边 `accessibility-elements-hidden`A级 `tabbar` 从标签周围的容器中读取,所以不需要 `accessibility-element`。其他的则只匹配 `hidden: true`。 。 。 .
-   **`name`**页:1 `accessibility-label`,或一个 `<text>`自己发的短信 A 类 `<view>` 因为iOS不读,所以不能从里面的文字中获得名字.
-   **`selected`**页:1 `selected` 特征,如 `accessibility-traits="button,selected"`,该活动 `routerLinkActive` 元素获得,以及 `[lynxAccessibilityTraits]="{ selected: true }"` 增加数字。 iOS读取了逗号分隔列表. Android 读取一个特性(`button` 或者说 `image`,并将列表作为 `none`。 。 。 .
-   **`value: { text }`**页:1 `accessibility-value`。 。 。 .
-   **其他阿里亚州** (单位:千美元)`checked`, (中文(简体) ), `pressed`, (中文(简体) ), `expanded`, (中文(简体) ), `level`,.)没有Lynx属性,因此Lynx元素与它们不相匹配.

### `screen`, (中文(简体) ), `within()` 财务报告和审定财务报表 `getQueriesForElement()`

测试图书馆,有以上查询: `screen` 绑定每个查询 `document.body`,以及 `within(element)` 绑住他们 `element`。 。 。 . `getQueriesForElement` 实值 `within`。 。 。 .

### `queries`

每个查询,不绑定(每个先取容器). 把它分散到你自己的询问中 把它们放在旁边 每个查询也单独导出, 如 `getByAccessibilityLabel(container, 'Close')`。 。 。 .

## 使用量

### 渲染组件

```ts
import { Component } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
import { render } from '@blotch/angular-lynx-testing-library';

@Component({
  selector: 'greeting',
  imports: [LYNX_ELEMENTS],
  template: `<text>Hello, {{ name }}!</text>`,
})
class GreetingComponent {
  name = 'Lynx';
}

it('renders the greeting', async () => {
  const { container } = await render(GreetingComponent);
  expect(container.querySelector('text')!.textContent).toBe('Hello, Lynx!');
});
```

### 注射提供者

```ts
import { Component, InjectionToken, inject } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
import { render } from '@blotch/angular-lynx-testing-library';

const GREETING = new InjectionToken<string>('GREETING');

@Component({
  selector: 'test-greeting',
  imports: [LYNX_ELEMENTS],
  template: `<text>{{ greeting() }}</text>`,
})
class GreetingComponent {
  greeting = inject(GREETING);
}

it('uses the injected value', async () => {
  const { container } = await render(GreetingComponent, {
    providers: [{ provide: GREETING, useValue: 'Hola' }],
  });
  expect(container.querySelector('text')!.textContent).toBe('Hola');
});
```

### 反应信号

定义组件外的信号,这样组件和测试都可以访问:

```ts
import { Component, signal } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
import { render, waitForUpdate } from '@blotch/angular-lynx-testing-library';

const count = signal(0);

@Component({
  selector: 'test-counter',
  imports: [LYNX_ELEMENTS],
  template: `<text>{{ count() }}</text>`,
})
class CounterComponent {
  count = count;
}

it('updates when the signal changes', async () => {
  const { container } = await render(CounterComponent);
  expect(container.querySelector('text')!.textContent).toBe('0');

  count.set(42);
  await waitForUpdate();

  expect(container.querySelector('text')!.textContent).toBe('42');
});
```

### 处理事件

AngularLynx 事件绑定使用 Lynx 前缀 : `(bindtap)`, (中文(简体) ), `(catchtap)`, (中文(简体) ), `(bindinput)`等 (中文(简体) ).

```ts
import { Component, signal } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
import {
  render,
  fireEvent,
  waitForUpdate,
} from '@blotch/angular-lynx-testing-library';

@Component({
  selector: 'test-button',
  imports: [LYNX_ELEMENTS],
  template: `
    <view (bindtap)="increment()">
      <text>{{ count() }}</text>
    </view>
  `,
})
class ButtonComponent {
  count = signal(0);
  increment() {
    this.count.update((n) => n + 1);
  }
}

it('increments on tap', async () => {
  const { container } = await render(ButtonComponent);

  fireEvent.tap(container.querySelector('view')!);
  await waitForUpdate();

  expect(container.querySelector('text')!.textContent).toBe('1');
});
```

### 控制流量—— `@if`

```ts
it('shows content conditionally', async () => {
  const visible = signal(true);

  @Component({
    selector: 'test-if',
    imports: [LYNX_ELEMENTS],
    template: `@if (visible()) {
      <text>Hello</text>
    }`,
  })
  class IfComponent {
    visible = visible;
  }

  const { container } = await render(IfComponent);
  expect(container.querySelector('text')).not.toBeNull();

  visible.set(false);
  await waitForUpdate();
  expect(container.querySelector('text')).toBeNull();
});
```

### 控制流量—— `@for`

```ts
it('renders a dynamic list', async () => {
  const items = signal(['A', 'B', 'C']);

  @Component({
    selector: 'test-for',
    imports: [LYNX_ELEMENTS],
    template: `@for (item of items(); track item) {
      <text>{{ item }}</text>
    }`,
  })
  class ListComponent {
    items = items;
  }

  const { container } = await render(ListComponent);
  expect(container.querySelectorAll('text').length).toBe(3);

  items.set(['A', 'B', 'C', 'D']);
  await waitForUpdate();
  expect(container.querySelectorAll('text').length).toBe(4);
});
```

## 结构说明

### 单行径模型

角链接运行 **完全在 Lynx 主线上** (单位:千美元)`__MAIN_THREAD__ = true`) (中文(简体) ). 这与React Lynx和Vue Lynx不同,它们将渲染分出两个线程:

| 图书馆 | 线索模型 |
| --- | --- |
| 反应 Lynx | 双:背景线程运行 React, 主线程应用 PAPI ops |
| 韦林克 | 双:背景线程运行 Vue, 主线程应用 PAPI 操作 |
| **角线** | **单向:角成像器直接在主线上调用 PAPI** |

在测试中,这意味着Angular留在主线上,即PAPI的功能所在(PAPI)`__CreatePage`, (中文(简体) ), `__CreateView`)总是有的. 环境切换到背景线程只用来回答AngularLynx发送到那里的一个呼叫,例如 `LynxLocalStorage`和开关直接返回.

### 投影管道

```
render(Component)
  └─ bootstrapApplication(Component, {
       provideZonelessChangeDetection(),
       provideRenderer(),
       ...providers
     })
       └─ LynxDocument.createRootElement()
            └─ __CreatePage()  →  <page> appended to document.body
       └─ Angular renders component tree
            └─ __CreateView / __CreateText / …  →  JSDOM children
       └─ LynxRendererFactory2.end()
            └─ __FlushElementTree()  (no-op in tests)
  └─ appRef.tick()  →  force synchronous CD
  └─ return { container: elementTree.root, ...queries }
```

### 事件发送

通过 AngularLynx 登记事件处理器 `__AddEvent(element, eventType, eventName, { type: 'worklet', value: callback })`。测试环境将它作为 DOM 事件听众存储在密钥下 `"${eventType}:${eventName}"` (例如:. `"bindEvent:tap"`) (中文(简体) ).

`fireEvent.tap(el)` 将 Lynx 的事件响应链从页面下行到 `el` 并备份,并呼叫每个元素的听众 对于它的处理器类型,停止一个 `catch` 一个 听者打电话 `runWorklet(callback, [event])` 直接在主线上引用角控器。 一个出事 `"bindEvent:tap"` DOM活动没有用: `(catchtap)` 祖师相传 `"catchEvent:tap"` 监听者并运行操作器上方.

模板绑定 — PAPI 注册 — DOM 听器密钥 :

| 模板 | 听器密钥 |
| --- | --- |
| `(bindtap)` | `bindEvent:tap` |
| `(catchtap)` | `catchEvent:tap` |
| `(bindinput)` | `bindEvent:input` |
| `(bindscroll)` | `bindEvent:scroll` |

## 与 React/Vue Lynx 测试库的比较

| 特性 | 反应 Lynx | 韦林克 | 角线 |
| --- | --- | --- | --- |
| `render()` 返回时 | 同步 | 同步 | **自动同步** |
| 线索模型 | 双( BG+ MT) | 双( BG+ MT) | **仅主** |
| 自动清理嵌入 var | `PTL_SKIP_AUTO_CLEANUP` | —— 说 | `ATL_SKIP_AUTO_CLEANUP` |
| `renderHook()` | ✓ | —— 说 | —— 说 |
| 快照序列器 | (参考文献) | —— 说 | —— 说 |
| 虚拟插件 | ✓ | —— 说 | —— (中文(简体) )`ng test` 设立) |
| 命名为 FireEvent 助手 | 30岁以上 | 16个 | 页:1 |
| 线索意识起火 | ✓ | ✓ | \- (单线) |
| Async 状态助手 | `waitSchedule()` | `waitForUpdate()` | `waitForUpdate()` |

## 常见问题

### @blotch/angular-lynx-testing-library 是什么？

AngularLynx 应用程序的测试工具

### 如何安装 @blotch/angular-lynx-testing-library？

在项目目录运行 npm install @blotch/angular-lynx-testing-library，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@angular/core（^22\.2\.0）、@angular/compiler（^22\.2\.0）、@blotch/angular-lynx（^22\.2\.0）、@angular/platform-browser（^22\.2\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @blotch/angular-lynx-testing-library 支持 TypeScript 吗？

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

### @blotch/angular-lynx-testing-library 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @blotch/angular-lynx-testing-library 可以商用吗？

@blotch/angular-lynx-testing-library 使用 未声明 许可。package\.json 没有声明许可证。没有许可证的代码默认保留全部权利，商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括，不构成法律意见，请以许可原文为准。

### @blotch/angular-lynx-testing-library 的下载量有多少？

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

### @blotch/angular-lynx-testing-library 还在维护吗？

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

