# @civitas-cerebrum/element-repository 中文文档

规范地址：https://npmcn.com/package/%40civitas-cerebrum/element-repository/

版本：0\.3\.4
npm 元数据更新时间：2026-11-01T15:39:15.748Z
中文译文更新时间：2026-11-09T22:41:06.483Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40civitas-cerebrum/element-repository
原文对照：https://npmcn.com/package/%40civitas-cerebrum/element-repository/?lang=original
项目主页：https://github.com/civitas-cerebrum/element-repository#readme
源码仓库：https://github.com/civitas-cerebrum/element-repository

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

- 包体积（解压后）：182 kB，34 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：webdriverio ^9\.27\.0，@playwright/test ^1\.58\.2
- 首次发布：2026-03-23T09:46:49.231Z
- 最新版本发布：2026-11-01T15:39:15.417Z
- 近 30 天下载量：3198（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：22（近 12 个月 22 个）

## 中文简介

一个轻量级,强壮的软件包 将你的UI选择器从测试代码中解开 通过将定位器外部化成一个中央的JSON寄存器,使你的测试自动化框架更清洁,更方便维护,并方便非开发者访问.

## 安装

```sh
npm install @civitas-cerebrum/element-repository
```

也可以使用：`pnpm add @civitas-cerebrum/element-repository`、`yarn add @civitas-cerebrum/element-repository`、`bun add @civitas-cerebrum/element-repository`

## README

# 元素仓库

[![NPM Version](https://img.shields.io/npm/v/@civitas-cerebrum/element-repository?color=rgb\(88%2C%20171%2C%2070\))](https://www.npmjs.com/package/@civitas-cerebrum/element-repository)

一个轻量级,强壮的软件包 将你的UI选择器从测试代码中解开 通过将定位器外部化成一个中央的JSON寄存器,使你的测试自动化框架更清洁,更方便维护,并方便非开发者访问. 支持两者 **剧作家 (网络)** 财务报告和审定财务报表 **Appium/WebriverIO(移动)** 通过统一的API.

## \* 安装

```bash
npm i @civitas-cerebrum/element-repository
```

**同伴依赖关系 :** 用于网络测试, 安装 `@playwright/test` 或者说 `playwright`。用于移动/平台测试,安装 `webdriverio`。 。 。 .

## \- 这有什么用?

-   **0 硬码选择器 :** 将您的页面对象和步骤定义完全排除复杂的 DOM 查询.
-   **平台不可知元素 API:** 一个统一的 `Element` 与交互、状态、取出、查询和等待方法进行接口,这些方法在Playwright和WebDriverIO之间同样有效.
-   **流利的动作链 :** 对元素的多动作进行排序 `element.action().waitForState('visible').click()`。 。 。 .
-   **动态解析 :** 自动将您的 JSON 配置转换为平台内置选择器 .
-   **智能定位器 :** 阵列的内置方法,随机元素选择,文本过滤,属性过滤,和能见度检查.

## 包件合同

此软件包拥有 **元素获取 + 只有原始操作**任何带有重试、倒置、选项驱动的变体、组成、匹配树或断言框架的都属于消费者包.

**住在这里的是什么:**

-   从 JSON 仓库中查找元素
-   这个 `Element` 界面及其两个后端(`WebElement` 为剧作家, `PlatformElement` 用于Appium)
-   `ElementChain` 一个流畅的动作测序器 `Element` 原始人
-   紧紧的一通电话包装 `Locator` / WebdriverIO 元素(单击、填充、盘旋、检查、滚动IntoView等)

**消费者的生活:**

-   校验 DSL 、 断言错误格式化、 重试相匹配语义
-   指向截取自动反射 并击出回击, `{ withoutScrolling, ifPresent }` 修改
-   所列要素构成(文本/regex/带有Descendant过滤)
-   火柴树`steps.expect(...).text.toBe(...)`),可见的Chan,步骤 API 外观

**拇指规则——按执行分类,不是按名称分类.** 其躯体为单一被包裹的原始呼号的方法是原始的并停留在这里(即使听起来是可验证的-y——例如, `ElementChain.isPresent()` 刚刚 `element.isVisible()` 在队列执行后). 一种重述、作曲或格式断言错误的方法是明智的,并会移动到消费者身上.

见 `@civitas-cerebrum/element-interactions` (网络)和 `@civitas-cerebrum/singularity-engine` (网络+移动+桌面)用于智能层.

## QQ 配置

在您的工程中创建 JSON 文件以持有您的选择器 :

**`locators.json`**

```json
{
  "pages": [
    {
      "name": "HomePage",
      "elements": [
        {
          "elementName": "search-input",
          "selector": { "css": "input[name='search']" }
        },
        {
          "elementName": "submit-button",
          "selector": { "id": "btn-submit" }
        }
      ]
    },
    {
      "name": "ProductList",
      "elements": [
        {
          "elementName": "product-cards",
          "selector": { "xpath": "//article[@class='product']" }
        }
      ]
    }
  ]
}
```

### 多平台配置

使用 `platform` 字段来定义同一页面的平台特定选择器。 无页 `platform` 字段默认为 `web`。 。 。 .

```json
{
  "pages": [
    {
      "name": "LoginPage",
      "platform": "web",
      "elements": [
        { "elementName": "submitButton", "selector": { "css": "button.web-submit" } }
      ]
    },
    {
      "name": "LoginPage",
      "platform": "android",
      "elements": [
        { "elementName": "submitButton", "selector": { "accessibility id": "SubmitBtn" } }
      ]
    },
    {
      "name": "LoginPage",
      "platform": "ios",
      "elements": [
        { "elementName": "submitButton", "selector": { "predicate": "label == \"Submit\"" } }
      ]
    }
  ]
}
```

### 支持选择键

这个 `platform` 每个页面对象的字段决定使用哪种选择器格式。 若为 `platform` ,默认为 `web`。 。 。 .

#### 网络( Playwright)

| 密钥 | 决定: | 示例 |
| --- | --- | --- |
| `css` | `css=<value>` | `"css": "button.primary"` |
| `xpath` | `xpath=<value>` | `"xpath": "//button[@id='submit']"` |
| `id` | `#<value>` | `"id": "btn-submit"` |
| `text` | `text=<value>` | `"text": "Submit"` |
| `testid` | `[data-testid='<value>']` | `"testid": "login-btn"` |
| `role` | `[role='<value>']` | `"role": "button"` |
| `placeholder` | `[placeholder='<value>']` | `"placeholder": "Search..."` |
| `label` | `[aria-label='<value>']` | `"label": "Close"` |

#### 非网络/ 移动( Appium)

| 密钥 | 骆驼类别名 | 决定: | 示例 |
| --- | --- | --- | --- |
| `accessibility id` | `accessibilityId` | `~<value>` | `"accessibility id": "LoginBtn"` |
| `xpath` | —— 说 | `<value>` (拉动) | `"xpath": "//android.widget.Button"` |
| `id` | —— 说 | `#<value>` | `"id": "submit-btn"` |
| `uiautomator` | `androidUIAutomator` | `android=<value>` | `"uiautomator": "new UiSelector().text(\"Go\")"` |
| `predicate` | `iOSNsPredicateString` | `-ios predicate string:<value>` | `"predicate": "label == \"Login\""` |
| `class chain` | `iOSClassChain` | `-ios class chain:<value>` | `"class chain": "**/XCUIElementTypeButton"` |
| `class name` | `className` | `<value>` (拉动) | `"class name": "android.widget.EditText"` |

> 所有包含空格的策略密钥也接受驼峰Case的别名(例如, `"accessibilityId"` 改为 `"accessibility id"`) (中文(简体) ).

### 选择器后退链

添加一个嵌入 `fallback` 选择器处理DOM变体(每个国家,每个品牌,每个渲染器)而无需重复回波条目. 当主线在短时间( 100 ms) 内匹配零元素时, 分辨率会进入 `fallback` ——递归.

```json
{
  "elementName": "loginButton",
  "selector": {
    "css": "[data-qa='login-button']",
    "fallback": {
      "role": "button",
      "name": { "regex": "Log in|Iniciar sesión|Anmelden|Inloggen", "flags": "i" }
    }
  }
}
```

**规则:**

-   初始附属于雷波 `defaultTimeout` (15s default) ~ 倒置从未被咨询. `waitFor` 解决元素出现的时刻,所以普通案件不支付超时费用.
-   小学不附加于 `defaultTimeout` 走进去 `selector.fallback`。 。 。 .
-   无条目 `fallback` 键键完全跳过探测器——懒惰定位器像以前一样使用元素交互的动作-时间超时(30-60s).
-   链为递归: `fallback.fallback.fallback` 这是合法的.
-   倒回可以自由混合策略——CSS初级与角色+名倒回,id初级与xpath倒回等.

**最坏情况** 在节点匹配的N级链上: `N × defaultTimeout`. 在实务中倒计时涵盖了真实的DOM变体,因此第一或第二关在初级不匹配的页面上相匹配.

**常见用途:**

-   **多国DOM变体** ——一个条目为每个国家服务,而不是针对具体国家的回购文件.
-   **品牌特异性初级+通用倒置** - 更喜欢特定品牌 `data-qa`,降解为角色选择器.
-   **严重退化** ——在逐步推出期间对新旧DOM进行测试.

### 条目标记 : `provisional` 财务报告和审定财务报表 `list`

两个可选布尔键坐在旁边 `elementName` 财务报告和审定财务报表 `selector`。两个方法都无法改变元素的解决方式;它们记录了输入的内容 _表示_,所以工具化和消费包 就可以采取行动.

```json
{
  "elementName": "basketRows",
  "list": true,
  "selector": { "css": "[data-testid='basket-row']" }
},
{
  "elementName": "discountLine",
  "provisional": true,
  "selector": { "role": "row", "name": { "regex": "discount", "flags": "i" } }
}
```

-   **`list`** ——该条目为收录:其合同为"至少一对"(QQ 1),而非"确切为一元素". 消费者读它与 `getElementMeta()`; 元素交互使用它 `verifyPresence` 编号 : `getAll`默认 `false`。 。 。 .
-   **`provisional`** ——该条目尚未根据运行中的应用程序得到确认(例如从源代码或文档推断出). `getProvisional()` 列出这些条目,以便管道能够确认或拒绝运送这些条目。 默认 `false`。 。 。 .

当寄存器构建时(和静态),两个密钥都会被验证 `ElementRepository.validate(data)`: 一个值,但不包括 `true` 编号 : `false` -比如绳子 `"true"` ——错误地点出每个违法者的名字 `Page.element`。 。 。 .

## 使用量

### 初始化

建筑师拿走 **驱动程序** (打字笔) `Page` 或网络驱动程序 `Browser`作为第一个论点:

```typescript
import { ElementRepository } from '@civitas-cerebrum/element-repository';

// Option A: Pass a file path (relative to project root)
const repo = new ElementRepository(page, 'tests/data/locators.json');

// Option B: Pass parsed JSON data directly
import locatorData from '../data/locators.json';
const repo = new ElementRepository(page, locatorData);

// Option C: With a custom timeout (default: 15000ms)
const repo = new ElementRepository(page, locatorData, 30000);

// Access the bound driver
const driver = repo.driver; // returns the Page/Browser passed to the constructor
```

### 获取元素

所有方法的使用 `(elementName, pageName)` 统一命令和返回 `Element` 对象 :

```typescript
test('Search and select random product', async ({ page }) => {
  const repo = new ElementRepository(page, 'tests/data/locators.json');

  // Get a single element (defaults to first match)
  const searchInput = await repo.get('search-input', 'HomePage');
  await searchInput.fill('Trousers');

  // Get with a specific strategy
  const thirdCard = await repo.get('product-cards', 'ProductList', {
    strategy: SelectionStrategy.INDEX, index: 2
  });

  // Select a random element from a list
  const randomProduct = await repo.getRandom('product-cards', 'ProductList');
  await randomProduct?.click();

  // Find by text
  const specificProduct = await repo.getByText('product-cards', 'ProductList', 'Blue Chinos');
  await specificProduct?.click();

  // Find by HTML attribute
  const activeCard = await repo.getByAttribute('product-cards', 'ProductList', 'data-status', 'active');

  // Get by index
  const secondCard = await repo.getByIndex('product-cards', 'ProductList', 1);

  // Get the first visible element
  const visibleModal = await repo.getVisible('modal', 'HomePage');

  // Filter by ARIA role
  const navLink = await repo.getByRole('nav-links', 'HomePage', 'link');
});
```

### 流利的动作链

每个 `Element` 暴露一个 `action(timeout?)` 返回可导的方法 `ElementChain` 建会者. 队列多个动作并用单个执行它们 `await`数字 :

```typescript
const element = await repo.get('submitButton', 'LoginPage');

// Interaction chain
await element.action(5000)
  .waitForState('visible')
  .click()

// Click without scrolling (for dropdown/flyout items)
await element.action()
  .waitForState('attached')
  .click({ withoutScrolling: true })

// Fill and verify
await element.action()
  .fill('hello@test.com')
  .verifyText('hello@test.com')

// Verification chain
await element.action(5000)
  .verifyPresence()
  .verifyText('Submit')
  .verifyAttribute('type', 'submit')

// Extraction (terminal — returns value)
const text = await element.action().getText()
const href = await element.action().getAttribute('href')
const count = await element.action().getCount()
```

#### 可用链式方法

**相互作用:** `click(options?)`, (中文(简体) ), `clickIfPresent(options?)`, (中文(简体) ), `hover()`, (中文(简体) ), `fill(text)`, (中文(简体) ), `clear()`, (中文(简体) ), `check()`, (中文(简体) ), `uncheck()`, (中文(简体) ), `doubleClick()`, (中文(简体) ), `scrollIntoView()`, (中文(简体) ), `pressSequentially(text, delay?)`, (中文(简体) ), `dispatchEvent(event)`

**核查:** `verifyPresence()`, (中文(简体) ), `verifyAbsence()`, (中文(简体) ), `verifyText(expected?)`, (中文(简体) ), `verifyTextContains(text)`, (中文(简体) ), `verifyAttribute(name, value)`, (中文(简体) ), `verifyEnabled()`, (中文(简体) ), `verifyDisabled()`, (中文(简体) ), `verifyChecked()`, (中文(简体) ), `verifyCount(options)`

**摘录( 直线):** `getText()`, (中文(简体) ), `getAttribute(name)`, (中文(简体) ), `getInputValue()`, (中文(简体) ), `getCount()`, (中文(简体) ), `getRaw()`, (中文(简体) ), `isPresent()`

**正在等待中 :** `waitForState(state)`

## API 参考

### 元素仓库

#### `constructor(driver, dataOrPath, defaultTimeout?)`

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `driver` | `any` | 剧作家 `Page` 或网络驱动程序 `Browser`编号 :`Driver` 实例 |
| `dataOrPath` | `string | PageRepository` | JSON 文件或解析的 JSON 对象的路径 |
| `defaultTimeout` | `number` | 默认等待超时 ms( 默认 ): `15000`(中文(简体) ) |

#### `get(elementName, pageName, options?)`

返回单个 `Element`。默认为第一个匹配。 过 `ElementResolutionOptions` 来控制选择 :

```typescript
import { SelectionStrategy } from '@civitas-cerebrum/element-repository';

await repo.get('button', 'Page')                                                    // first (default)
await repo.get('button', 'Page', { strategy: SelectionStrategy.INDEX, index: 2 })   // by index
await repo.get('button', 'Page', { strategy: SelectionStrategy.RANDOM })             // random
await repo.get('button', 'Page', { strategy: SelectionStrategy.TEXT, value: 'Sub' }) // by text
await repo.get('button', 'Page', { strategy: SelectionStrategy.ATTRIBUTE,
                                   attribute: 'data-id', value: 'row-2' })           // by attribute value
await repo.get('button', 'Page', { strategy: SelectionStrategy.ALL })                // un-narrowed (for counts)
```

> 一个未处理的战略值会丢出一个描述性错误 — 分辨率永远不会 当一个策略被请求时,悄悄地回到第一个匹配元素.

#### `getAll(elementName, pageName)`

返回所有匹配的数组 `Element` 对象.

#### `getRandom(elementName, pageName, strict?)`

随机从所有匹配中选择一个元素 .

#### `getByText(elementName, pageName, desiredText, strict?)`

返回包含指定文本的第一个元素。 先试一试,再回试.

#### `getByAttribute(elementName, pageName, attribute, value, options?)`

返回 HTML 属性符合给定值的第一个元素.

**选项 :** `exact` (布尔), `strict` (布尔)

#### `getByIndex(elementName, pageName, index, strict?)`

在指定的零基索引中返回元素.

#### `getVisible(elementName, pageName, strict?)`

返回第一个可见元素,过滤出隐藏的重复.

#### `getByRole(elementName, pageName, role, strict?)`

按元素过滤 `role` HTML属性.

#### `getSelector(elementName, pageName)`

返回一个平台格式的选择器字符串(同步).

#### `getSelectorRaw(elementName, pageName)`

回返 `{ strategy, value }` ——原始选择器没有平台格式化.

#### `getElementMeta(elementName, pageName)`

回返 `{ provisional, list }` 对条目,适用默认值(`false`。同步;只读存储器数据.

#### `getProvisional()`

回返 `[{ pageName, elementName }]` 每个标记条目 `"provisional": true`,按照仓库顺序.

#### `ElementRepository.validate(data)` (静态)

验证没有驱动程序(构造者也称它)的条目级密钥 。 掷出何时 `provisional` 或者说 `list` 以非布尔值存在.

#### `getPagePlatform(pageName)`

返回页面的平台字符串( E)`'web'`, (中文(简体) ), `'android'`, (中文(简体) ), `'ios'`) (中文(简体) ).

#### 严格模式

全体 `get*` 返回的方法 `Element | null` 接受选项 `strict` 参数 :

-   **`strict: false`** (默认)——返回 `null` 当找不到匹配时.
-   **`strict: true`** 抛出一个 `Error` 当找不到匹配时.

### 元素接口

全体 `get*` 方法返回一个 `Element` 一个平台不可知的界面, 包入一个 Playwright `Locator` (单位:千美元)`WebElement`或 WebDriverIO 元素(`PlatformElement`) (中文(简体) ).

#### 相互作用方法

| 方法 | 说明 |
| --- | --- |
| `click(options?)` | 单击元素。 回返 `Promise<Element>`。 。 。 . |
| `fill(text, options?)` | 清取并取满文. 回返 `Promise<Element>`。 。 。 . |
| `clear(options?)` | 清除元素的值。 回返 `Promise<Element>`。 。 。 . |
| `check(options?)` | 检查复选框/收音机。 回返 `Promise<Element>`。 。 。 . |
| `uncheck(options?)` | 取消检查复选框。 回返 `Promise<Element>`。 。 。 . |
| `hover(options?)` | 绕过元素 回返 `Promise<Element>`。 。 。 . |
| `doubleClick(options?)` | 双击. 回返 `Promise<Element>`。 。 。 . |
| `scrollIntoView(options?)` | 卷入观想. 回返 `Promise<Element>`。 。 。 . |
| `pressSequentially(text, delay?, options?)` | 按角色分类 回返 `Promise<Element>`。 。 。 . |
| `setInputFiles(filePath, options?)` | 设置文件输入值 . **只有网络.** |
| `dispatchEvent(event)` | 发布DOM活动. **只有网络.** |

所有交互方法都接受可选 `{ timeout?: number }` 并返回元素用于简单的链条.

#### 状态、采掘、查询和等待

| 方法 | 说明 |
| --- | --- |
| `isVisible()` | 回返 `true` 如果可见. |
| `isEnabled()` | 回返 `true` 如果启用的话. |
| `isChecked()` | 回返 `true` 如果选中的话. |
| `raw()` | 返回选择器字符串. |
| `textContent()` | 返回文本内容或 `null`。 。 。 . |
| `getAttribute(name)` | 返回属性值或 `null`。 。 。 . |
| `inputValue()` | 返回当前输入值. |
| `count()` | 返回匹配元素计数 . |
| `all()` | 返回所有匹配元素的数组 . |
| `first()` | 返回第一个匹配 . |
| `nth(index)` | 在索引中返回元素. |
| `filter({ hasText })` | 按文本内容过滤. |
| `locateChild(selector)` | 找到一个后人. |
| `waitFor(options?)` | 等待状态 : `"visible"`, (中文(简体) ), `"hidden"`, (中文(简体) ), `"attached"`, (中文(简体) ), `"detached"`。 。 。 . |
| `action(timeout?)` | 返回流畅 `ElementChain` 建会者. |

### QQ 类型安全

```typescript
import { Element, isWeb, isPlatform } from '@civitas-cerebrum/element-repository';

const el = await repo.get('submitButton', 'LoginPage');

if (isWeb(el)) {
  // Access the underlying Playwright Locator
  await (el as WebElement).locator.click();
}

if (isPlatform(el)) {
  // Access the WebDriverIO selector
  console.log((el as PlatformElement).selector);
}
```

## · 突破变化(v.1.0)

| 变动 | 移徙 |
| --- | --- |
| 需要构造器 `driver` 作为第一个参数 | `new ElementRepository(data)` → `new ElementRepository(page, data)` |
| 方法减少 `driver`编号 :`page` 第1个段落 | `repo.get(page, 'Page', 'el')` → `repo.get('el', 'Page')` |
| 参数顺序被翻转到 `(elementName, pageName)` | 在所有呼叫中交换参数 |
| 元素方法返回 `Promise<Element>` | 曾经 `Promise<void>` - 无需采取行动,除非你依赖 `void` |
| `raw()` 返回实际选择符字符串 | 曾经 `locator.toString()` - 可能有所改进 |

## 许可证

麻省理工学院

## 常见问题

### @civitas-cerebrum/element-repository 是什么？

一个轻量级,强壮的软件包 将你的UI选择器从测试代码中解开 通过将定位器外部化成一个中央的JSON寄存器,使你的测试自动化框架更清洁,更方便维护,并方便非开发者访问\.

### 如何安装 @civitas-cerebrum/element-repository？

在项目目录运行 npm install @civitas-cerebrum/element-repository，或使用 pnpm add / yarn add / bun add。

### @civitas-cerebrum/element-repository 支持 TypeScript 吗？

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

### @civitas-cerebrum/element-repository 支持 ESM 还是 CommonJS？

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

### @civitas-cerebrum/element-repository 可以商用吗？

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

### @civitas-cerebrum/element-repository 的下载量有多少？

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

### @civitas-cerebrum/element-repository 还在维护吗？

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

