@blotch/angular-lynx-testing-library

中文文档
22.2.18发布于 2026/11/08TypeScriptES Module构建来源已验证近 30 天下载 3489

AngularLynx 应用程序的测试工具

中文使用文档
文档目录25 节
  1. 安装
  2. 设置
  3. 没有角CLI
  4. API 密码
  5. render(component, options?)
  6. cleanup()
  7. waitForUpdate()
  8. fireEvent
  9. eventMap
  10. *ByAccessibilityLabel
  11. *ByRole
  12. screen , (中文(简体) ), within() 财务报告和审定财务报表 getQueriesForEleme…
  13. queries
  14. 使用量
  15. 渲染组件
  16. 注射提供者
  17. 反应信号
  18. 处理事件
  19. 控制流量—— @if
  20. 控制流量—— @for
  21. 结构说明
  22. 单行径模型
  23. 投影管道
  24. 事件发送
  25. 与 React/Vue Lynx 测试库的比较

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

测试公用事业 角线 apps. Renders Angular 独立组件通过 Lynx PAPI 输入一棵 JSDOM 树,然后通过 @ testing-library/dom (英语) 查询,也可以读取 Lynx 的 accessibility-* 属性.

安装

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 设置它们:

// 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 与它相连的询问.

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

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

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

选项 :

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

返回值:

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

cleanup()

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

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

afterEach(cleanup);

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

waitForUpdate()

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

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 事件中使用指定的助手 :

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>'大小一旦装入,说.

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

要直接发送原始 DOM 事件 :

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发送 上升的反应链, {} 剩下的钱.

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

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

*ByAccessibilityLabel

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

const { getByAccessibilityLabel } = await render(Toolbar);

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

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

*ByRole

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

const { getByRole } = await render(Toolbar);

fireEvent.tap(getByRole('button', { name: 'Save' }));
expect(getByRole('button', { name: 'Week', selected: true })).toBeTruthy();
accessibility-traits作用
buttonbutton
linklink
imageimg
headerheading
searchsearchbox
adjustableslider
tabbartablist
  • 作用: 从桌子上的每个特征。 一个 <image> 是一个 img 除非设定 accessibility-traits。 。 。 .
  • 可达: 屏幕阅读器到达 <text>, (中文(简体) ), <image>,其他要素 accessibility-element="true"外边 accessibility-elements-hiddenA级 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')。 。 。 .

使用量

渲染组件

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!');
});

注射提供者

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');
});

反应信号

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

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)等 (中文(简体) ).

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

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

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)仅主
自动清理嵌入 varPTL_SKIP_AUTO_CLEANUP—— 说ATL_SKIP_AUTO_CLEANUP
renderHook()✓—— 说—— 说
快照序列器(参考文献)—— 说—— 说
虚拟插件✓—— 说—— (中文(简体) )ng test 设立)
命名为 FireEvent 助手30岁以上16个页:1
线索意识起火✓✓- (单线)
Async 状态助手waitSchedule()waitForUpdate()waitForUpdate()

版本与发布记录

@blotch/angular-lynx-testing-library 共发布 20 个版本,首次发布于 2026/10/12;过去 12 个月发布了 20 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
22.2.18latest2026/11/08今天
22.2.172026/11/08今天
22.2.162026/11/07今天
22.2.152026/11/071 天前
22.2.142026/11/071 天前
22.2.132026/11/062 天前

关于 @blotch/angular-lynx-testing-library 的常见问题

@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 和提交记录判断。