vite-plugin-vue-testid 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +302 -198
- package/README.zh.md +481 -0
- package/package.json +3 -2
- package/README.en.md +0 -377
package/README.zh.md
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# vite-plugin-vue-testid
|
|
2
|
+
|
|
3
|
+
[English](./README.md)
|
|
4
|
+
|
|
5
|
+
编译期 + 运行时自动为 Vue 组件模板注入 `data-testid` 的 Vite 插件,专为 ant-design-vue 4.x 适配,大幅降低 E2E 测试中的元素定位成本。
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- **纯运行时全覆盖** — 通过 MutationObserver 为所有 ant-design-vue 组件根元素、子元素、teleport 面板自动注入 `data-testid`,无需编译期配置即可使用
|
|
10
|
+
- **编译期可选增强** — 为业务自定义组件(如 `<MyInput />`)在模板编译期注入唯一稳定的 testid,通过 `inheritAttrs` 自动透传到内部 antd 组件
|
|
11
|
+
- **多根组件修复** — 自动为多根子组件的 antd 根元素添加 `v-bind="$attrs"`,确保 testid 正确透传
|
|
12
|
+
- **自定义 prefixCls** — 完美支持 ConfigProvider 的自定义 CSS 类名前缀
|
|
13
|
+
- **自定义组件映射** — 支持为第三方 UI 库或自定义组件扩展匹配规则
|
|
14
|
+
- **面板全覆盖** — DatePicker (date/month/year/decade)、RangePicker、TimePicker、Cascader、Select、TreeSelect
|
|
15
|
+
- **子元素注入** — InputNumber 内部输入框及增减按钮、Slider handle、Rate 星星、Upload file input、Tabs tab/pane
|
|
16
|
+
- **Menu 子元素** — 基于文本内容自动注入 menu-item、sub-menu、menu-item-group
|
|
17
|
+
- **不会覆盖已有 testId** — 手动标注了 `data-testid` 的元素会被跳过
|
|
18
|
+
- **MutationObserver 动态监听** — 面板切换、树节点展开、异步组件加载后自动注入新 DOM
|
|
19
|
+
- **一次性注入模式** — SSE/hydration 场景可直接调用,不使用 MutationObserver
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -D vite-plugin-vue-testid
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> 要求:`vite >= 5.0.0`,`vue >= 3.3.0`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 快速开始
|
|
34
|
+
|
|
35
|
+
推荐**方式 A**(纯运行时),最简单且覆盖最全。如果需要对业务自定义组件生成稳定不变的 testid,选择**方式 B**。
|
|
36
|
+
|
|
37
|
+
### 方式 A:纯运行时(推荐,零编译期配置)
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// vite.config.ts — 不需要任何插件配置
|
|
41
|
+
import { defineConfig } from 'vite'
|
|
42
|
+
import vue from '@vitejs/plugin-vue'
|
|
43
|
+
|
|
44
|
+
export default defineConfig({
|
|
45
|
+
plugins: [vue()]
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
// main.ts
|
|
51
|
+
import { createApp } from 'vue'
|
|
52
|
+
import App from './App.vue'
|
|
53
|
+
import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
54
|
+
|
|
55
|
+
const app = createApp(App)
|
|
56
|
+
app.mount('#app')
|
|
57
|
+
|
|
58
|
+
// 在 mount 之后调用一次即可
|
|
59
|
+
setupAntTestIds()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 方式 B:编译期 + 运行时(业务自定义组件需要稳定 testid)
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// vite.config.ts
|
|
66
|
+
import { defineConfig } from 'vite'
|
|
67
|
+
import vue from '@vitejs/plugin-vue'
|
|
68
|
+
import { testIdTransforms } from 'vite-plugin-vue-testid'
|
|
69
|
+
|
|
70
|
+
export default defineConfig({
|
|
71
|
+
plugins: [
|
|
72
|
+
vue({
|
|
73
|
+
template: {
|
|
74
|
+
compilerOptions: {
|
|
75
|
+
nodeTransforms: testIdTransforms()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// main.ts — 与方式 A 相同
|
|
85
|
+
import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
86
|
+
setupAntTestIds()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 方式 C:仅编译期(面板不注入 testid,不推荐)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// vite.config.ts
|
|
93
|
+
import vue from '@vitejs/plugin-vue'
|
|
94
|
+
import { testIdTransforms } from 'vite-plugin-vue-testid'
|
|
95
|
+
|
|
96
|
+
export default defineConfig({
|
|
97
|
+
plugins: [
|
|
98
|
+
vue({
|
|
99
|
+
template: {
|
|
100
|
+
compilerOptions: {
|
|
101
|
+
nodeTransforms: testIdTransforms()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
})
|
|
107
|
+
// main.ts — 不调用 setupAntTestIds()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 生成的 testId 示例
|
|
113
|
+
|
|
114
|
+
### 运行时注入:组件根元素
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<!-- 运行时自动匹配 ant-design-vue 组件,全局递增编号 -->
|
|
118
|
+
<div class="ant-input-number" data-testid="a-input-number-0">
|
|
119
|
+
<div class="ant-select" data-testid="a-select-1" id="a-select-1">
|
|
120
|
+
<div class="ant-picker" data-testid="a-date-picker-2" id="a-date-picker-2">
|
|
121
|
+
<button class="ant-btn" data-testid="a-button-3">
|
|
122
|
+
<input class="ant-input" data-testid="a-input-4">
|
|
123
|
+
<span class="ant-slider" data-testid="a-slider-5">
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 运行时注入:组件子元素
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
<!-- InputNumber:内部 input + 增减按钮 -->
|
|
130
|
+
a-input-number-0-input # 内部 <input>
|
|
131
|
+
a-input-number-0-up # 增加按钮
|
|
132
|
+
a-input-number-0-down # 减少按钮
|
|
133
|
+
|
|
134
|
+
<!-- Slider:各 handle -->
|
|
135
|
+
a-slider-5-handle-0 # 第1个 handle
|
|
136
|
+
a-slider-5-handle-1 # 第2个 handle(range 模式)
|
|
137
|
+
|
|
138
|
+
<!-- Rate:各星星 -->
|
|
139
|
+
a-rate-0-star-0 # 第1颗星
|
|
140
|
+
a-rate-0-star-1 # 第2颗星
|
|
141
|
+
|
|
142
|
+
<!-- Upload:内部 file input -->
|
|
143
|
+
a-upload-0-input # <input type="file">
|
|
144
|
+
|
|
145
|
+
<!-- Tabs:标签项和内容面板 -->
|
|
146
|
+
a-tabs-0-tab-0-标签一 # 第1个 tab
|
|
147
|
+
a-tabs-0-tab-1-标签二 # 第2个 tab
|
|
148
|
+
a-tabs-0-pane-0-标签一 # 第1个 pane
|
|
149
|
+
a-tabs-0-add # 新增标签按钮(editable tabs)
|
|
150
|
+
a-tabs-0-more # 更多按钮
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 运行时注入:DatePicker 面板
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
a-date-picker-0-dropdown # 面板容器
|
|
157
|
+
a-date-picker-0-dropdown-super-prev # 上一年
|
|
158
|
+
a-date-picker-0-dropdown-prev # 上一月
|
|
159
|
+
a-date-picker-0-dropdown-next # 下一月
|
|
160
|
+
a-date-picker-0-dropdown-super-next # 下一年
|
|
161
|
+
a-date-picker-0-dropdown-header-date # header 容器
|
|
162
|
+
a-date-picker-0-dropdown-header-month-btn # header 月份按钮
|
|
163
|
+
a-date-picker-0-dropdown-header-year-btn # header 年份按钮
|
|
164
|
+
a-date-picker-0-dropdown-date-2026-06-15 # 日期单元格
|
|
165
|
+
a-date-picker-0-dropdown-ok # 确定按钮
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
切换到月/年/年代面板后(MutationObserver 自动重注入):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
a-date-picker-0-dropdown-header-month # 月面板 header
|
|
172
|
+
a-date-picker-0-dropdown-month-06 # 6月
|
|
173
|
+
a-date-picker-0-dropdown-header-year # 年面板 header
|
|
174
|
+
a-date-picker-0-dropdown-year-2026 # 2026年
|
|
175
|
+
a-date-picker-0-dropdown-decade-2020-2029 # 2020-2029 年代
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 运行时注入:RangePicker 面板
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
a-range-picker-0-dropdown-super-prev-left # 左侧面板上一年
|
|
182
|
+
a-range-picker-0-dropdown-prev-left # 左侧面板上一月
|
|
183
|
+
a-range-picker-0-dropdown-next-right # 右侧面板下一月
|
|
184
|
+
a-range-picker-0-dropdown-date-2026-05-15-left # 左侧面板日期
|
|
185
|
+
a-range-picker-0-dropdown-date-2026-06-20-right # 右侧面板日期
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 运行时注入:TimePicker
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
a-time-picker-0-dropdown-time-column-0 # 时列
|
|
192
|
+
a-time-picker-0-dropdown-time-0-08 # 08时
|
|
193
|
+
a-time-picker-0-dropdown-time-column-1 # 分列
|
|
194
|
+
a-time-picker-0-dropdown-time-1-30 # 30分
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 运行时注入:Preset 快捷范围
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
a-date-picker-0-dropdown-preset-本周 # preset 标签
|
|
201
|
+
a-date-picker-0-dropdown-preset-本月 # preset 标签
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 运行时注入:Cascader
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
a-cascader-0-dropdown # 面板容器
|
|
208
|
+
a-cascader-0-dropdown-menu-0 # 第1级菜单
|
|
209
|
+
a-cascader-0-dropdown-item-浙江 # 浙江选项
|
|
210
|
+
a-cascader-0-dropdown-menu-1 # 第2级菜单
|
|
211
|
+
a-cascader-0-dropdown-item-杭州 # 杭州选项
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 运行时注入:Select / TreeSelect
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
a-select-0-dropdown # 下拉容器
|
|
218
|
+
a-select-0-dropdown-option-选项1 # 选项
|
|
219
|
+
a-select-0-dropdown-option-选项2 # 选项
|
|
220
|
+
|
|
221
|
+
a-tree-select-0-dropdown # 下拉容器
|
|
222
|
+
a-tree-select-0-dropdown-tree-根节点 # 树节点
|
|
223
|
+
a-tree-select-0-dropdown-tree-switcher-expand-根节点 # 展开图标(可展开)
|
|
224
|
+
a-tree-select-0-dropdown-tree-switcher-collapse-根节点 # 展开图标(已展开)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 运行时注入:Menu 子元素
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
a-menu-0 # 菜单容器(由 injectComponentRoots 注入)
|
|
231
|
+
a-menu-item-菜单项1 # 菜单项(运行时,基于文本内容)
|
|
232
|
+
a-menu-item-子项3 # 子菜单内的菜单项
|
|
233
|
+
a-sub-menu-子菜单 # 子菜单
|
|
234
|
+
a-menu-item-group-分组标题 # 菜单分组标题
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 编译期注入:业务自定义组件
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
格式:{tagName}-{perTemplateIndex}
|
|
241
|
+
|
|
242
|
+
<!-- 源码 -->
|
|
243
|
+
<MyInput />
|
|
244
|
+
<MySelect />
|
|
245
|
+
<MyInput />
|
|
246
|
+
|
|
247
|
+
<!-- 编译后 -->
|
|
248
|
+
<MyInput data-testid="MyInput-0" />
|
|
249
|
+
<MySelect data-testid="MySelect-0" />
|
|
250
|
+
<MyInput data-testid="MyInput-1" />
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
> **设计说明**:编译期 transform 只对**业务自定义组件**注入 testid(通过 `inheritAttrs` 透传到内部 antd 组件),不对 antd 组件直接注入。antd 组件的 testid 全部由运行时负责(全局计数器保证唯一)。
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 配置
|
|
258
|
+
|
|
259
|
+
### 编译期配置
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { testIdTransforms } from 'vite-plugin-vue-testid'
|
|
263
|
+
|
|
264
|
+
testIdTransforms({
|
|
265
|
+
/**
|
|
266
|
+
* testId 属性名
|
|
267
|
+
* @default 'data-testid'
|
|
268
|
+
*/
|
|
269
|
+
attributeName: 'data-testid',
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* ant-design-vue 的 tag 前缀
|
|
273
|
+
* @default 'a-'
|
|
274
|
+
*/
|
|
275
|
+
antPrefix: 'a-',
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 额外需要注入 testid 的自定义组件前缀
|
|
279
|
+
* @default []
|
|
280
|
+
*/
|
|
281
|
+
customPrefixes: ['myapp-', 'el-'],
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 运行时配置
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
289
|
+
|
|
290
|
+
const cleanup = setupAntTestIds({
|
|
291
|
+
/**
|
|
292
|
+
* testId 属性名,需与编译期配置一致
|
|
293
|
+
* @default 'data-testid'
|
|
294
|
+
*/
|
|
295
|
+
attributeName: 'data-testid',
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* ant-design-vue CSS 类名前缀,对应 ConfigProvider 的 prefixCls
|
|
299
|
+
* @default 'ant'
|
|
300
|
+
*/
|
|
301
|
+
prefixCls: 'ant',
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 自定义组件 CSS 选择器 → testid 前缀映射
|
|
305
|
+
* 会与默认映射合并(自定义覆盖默认)
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* components: {
|
|
309
|
+
* '.el-input': 'el-input',
|
|
310
|
+
* '.el-button': 'el-button',
|
|
311
|
+
* }
|
|
312
|
+
*/
|
|
313
|
+
components: {
|
|
314
|
+
'.el-input': 'el-input',
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 自定义面板选择器 → 注入策略映射
|
|
319
|
+
* key: CSS 选择器
|
|
320
|
+
* value: 注入函数,设为 undefined 可禁用某个默认面板
|
|
321
|
+
*/
|
|
322
|
+
panels: {
|
|
323
|
+
'.ant-picker-dropdown': undefined, // 禁用内置 Picker 面板注入
|
|
324
|
+
'.my-custom-dropdown': (ctx) => {
|
|
325
|
+
const prefix = `${ctx.triggerTestId}-dropdown`
|
|
326
|
+
ctx.container.setAttribute(ctx.attrName, prefix)
|
|
327
|
+
// ... 自定义注入逻辑
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
// 组件卸载时清理 Observer
|
|
333
|
+
// onUnmounted(cleanup)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 一次性手动注入
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
import { injectCurrentDropdowns } from 'vite-plugin-vue-testid/runtime'
|
|
340
|
+
|
|
341
|
+
// SSR / hydration 场景下,不想用 MutationObserver 时直接调用
|
|
342
|
+
injectCurrentDropdowns({ prefixCls: 'ant' })
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 默认支持的组件
|
|
348
|
+
|
|
349
|
+
### 运行时(组件根元素 + 子元素 + 面板)
|
|
350
|
+
|
|
351
|
+
| 组件 | 根元素 testid | 子元素 | 面板 |
|
|
352
|
+
|------|:---:|:---:|:---:|
|
|
353
|
+
| Input | `a-input-N` | — | — |
|
|
354
|
+
| Input.Search | `a-input-search-N` | — | — |
|
|
355
|
+
| Input.Password (affix-wrapper) | `a-input-N` | — | — |
|
|
356
|
+
| Textarea | `a-textarea-N` | — | — |
|
|
357
|
+
| InputNumber | `a-input-number-N` | input / up / down | — |
|
|
358
|
+
| Select | `a-select-N` | — | ✅ 下拉面板 |
|
|
359
|
+
| Cascader | `a-cascader-N` | — | ✅ 级联面板 |
|
|
360
|
+
| TreeSelect | `a-tree-select-N` | — | ✅ 树选择面板 |
|
|
361
|
+
| DatePicker | `a-date-picker-N` | — | ✅ 日期/月/年/年代面板 |
|
|
362
|
+
| RangePicker | `a-range-picker-N` | — | ✅ 双面板 + 时间列 |
|
|
363
|
+
| TimePicker | `a-time-picker-N` | — | ✅ 时间列 |
|
|
364
|
+
| Radio.Group | `a-radio-group-N` | — | — |
|
|
365
|
+
| Radio | `a-radio-N` | — | — |
|
|
366
|
+
| Checkbox.Group | `a-checkbox-group-N` | — | — |
|
|
367
|
+
| Checkbox | `a-checkbox-N` | — | — |
|
|
368
|
+
| Switch | `a-switch-N` | — | — |
|
|
369
|
+
| Slider | `a-slider-N` | handle-0 / handle-1 | — |
|
|
370
|
+
| Rate | `a-rate-N` | star-0 / star-1 / ... | — |
|
|
371
|
+
| Upload | `a-upload-N` | file input | — |
|
|
372
|
+
| Button | `a-button-N` | — | — |
|
|
373
|
+
| Form.Item | `a-form-item-N` | — | — |
|
|
374
|
+
| Modal | `a-modal-N` | — | — |
|
|
375
|
+
| Menu | `a-menu-N` | menu-item / sub-menu / group | — |
|
|
376
|
+
| Tabs | `a-tabs-N` | tab / pane / add / more | — |
|
|
377
|
+
|
|
378
|
+
> 所有带 `id` 注入的组件(Select、Cascader、DatePicker、RangePicker、TimePicker、Modal、Upload)同时设置 `id` 属性,值等于 testid。
|
|
379
|
+
|
|
380
|
+
### 编译期(业务自定义组件)
|
|
381
|
+
|
|
382
|
+
编译期 transform 对满足以下条件的组件注入 testid:
|
|
383
|
+
- `tagType === Component` 的自定义组件(如 `<MyInput />`)
|
|
384
|
+
- 匹配 `customPrefixes` 中任一前缀的组件
|
|
385
|
+
- **不包括** antd 组件(`a-*`),这些由运行时负责
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## 自定义面板注入策略
|
|
390
|
+
|
|
391
|
+
如果需要支持其他 UI 库的弹出面板,可以自定义策略:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import type { PanelInjectStrategy } from 'vite-plugin-vue-testid/runtime'
|
|
395
|
+
import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
396
|
+
|
|
397
|
+
// 示例:为 Element Plus 的弹出层注入 testId
|
|
398
|
+
const injectElDropdown: PanelInjectStrategy = (ctx) => {
|
|
399
|
+
const { container, triggerTestId, attrName } = ctx
|
|
400
|
+
const prefix = `${triggerTestId}-dropdown`
|
|
401
|
+
|
|
402
|
+
container.setAttribute(attrName, prefix)
|
|
403
|
+
|
|
404
|
+
container.querySelectorAll('.el-select-dropdown__item').forEach((item) => {
|
|
405
|
+
const el = item as HTMLElement
|
|
406
|
+
const text = el.textContent?.trim() || ''
|
|
407
|
+
el.setAttribute(attrName, `${prefix}-option-${text}`)
|
|
408
|
+
})
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
setupAntTestIds({
|
|
412
|
+
panels: {
|
|
413
|
+
'.el-select-dropdown': injectElDropdown,
|
|
414
|
+
'.el-cascader-dropdown': injectElDropdown,
|
|
415
|
+
},
|
|
416
|
+
})
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## API 参考
|
|
422
|
+
|
|
423
|
+
### 编译期
|
|
424
|
+
|
|
425
|
+
| 导出 | 类型 | 说明 |
|
|
426
|
+
|------|------|------|
|
|
427
|
+
| `testIdTransforms(opts?)` | `() => NodeTransform[]` | 返回 `[multiRootFixTransform, testIdInjectTransform]`,注册到 `vue()` 的 `compilerOptions.nodeTransforms` |
|
|
428
|
+
| `createTestIdTransforms(opts?)` | `() => NodeTransform[]` | 同上,完整导出名 |
|
|
429
|
+
| `createTestIdInjectTransform(opts?)` | `() => NodeTransform` | 仅创建 testid 注入 transform(不含 multi-root fix) |
|
|
430
|
+
| `createMultiRootFixTransform(opts?)` | `() => NodeTransform` | 仅创建多根组件修复 transform |
|
|
431
|
+
| `TransformOptions` | `interface` | `{ attributeName?, antPrefix?, customPrefixes? }` |
|
|
432
|
+
|
|
433
|
+
### 运行时(`/runtime` 子路径)
|
|
434
|
+
|
|
435
|
+
| 导出 | 类型 | 说明 |
|
|
436
|
+
|------|------|------|
|
|
437
|
+
| `setupAntTestIds(opts?)` | `() => () => void` | 启动 MutationObserver,返回清理函数 |
|
|
438
|
+
| `injectCurrentDropdowns(opts?)` | `() => void` | 一次性手动注入当前 DOM 中已有的组件和面板 |
|
|
439
|
+
| `RuntimeOptions` | `interface` | `{ attributeName?, prefixCls?, components?, panels? }` |
|
|
440
|
+
| `PanelContext` | `interface` | `{ container, trigger, triggerTestId, attrName }` |
|
|
441
|
+
| `PanelInjectStrategy` | `(ctx: PanelContext) => void` | 面板注入策略函数类型 |
|
|
442
|
+
|
|
443
|
+
### Vite 插件
|
|
444
|
+
|
|
445
|
+
| 导出 | 类型 | 说明 |
|
|
446
|
+
|------|------|------|
|
|
447
|
+
| `vueTestIdPlugin(opts?)` | `Plugin` | Vite 插件(占位符,当前无实际功能) |
|
|
448
|
+
|
|
449
|
+
### 已废弃
|
|
450
|
+
|
|
451
|
+
| 导出 | 替代 |
|
|
452
|
+
|------|------|
|
|
453
|
+
| `setupAntDropdownTestIds` | 使用 `setupAntTestIds` |
|
|
454
|
+
| `createVueTestIdTransform` | 使用 `testIdTransforms` |
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## 原理
|
|
459
|
+
|
|
460
|
+
### 运行时(核心)
|
|
461
|
+
|
|
462
|
+
`setupAntTestIds()` 通过 `MutationObserver` 监听 `document.body` 的 DOM 变化,对新插入的节点依次执行:
|
|
463
|
+
|
|
464
|
+
1. **组件根注入** — 遍历 CSS 选择器匹配 antd 组件根元素,注入全局递增编号的 testid。同时通过 skip 列表排除组件包装器内部的 input/textarea
|
|
465
|
+
2. **面板注入** — 检测 teleport 面板容器(picker-dropdown / cascader-dropdown / select-dropdown 等),通过 `findActiveTrigger` 找到触发面板的组件,关联注入
|
|
466
|
+
3. **子元素注入** — 对 InputNumber / Slider / Rate / Upload / Tabs 的特殊子元素注入带后缀的 testid
|
|
467
|
+
4. **Menu 注入** — 由于 Menu 组件的 `inheritAttrs: false`,额外遍历 menu-item / sub-menu / menu-item-group 注入基于文本的 testid
|
|
468
|
+
|
|
469
|
+
面板容器内也使用 MutationObserver 监听子节点变化(如面板模式切换、树节点展开),自动重新注入。
|
|
470
|
+
|
|
471
|
+
### 编译期(可选)
|
|
472
|
+
|
|
473
|
+
通过 Vue 编译器的 `nodeTransform` 钩子在 AST 阶段为业务自定义组件注入 `data-testid="{tagName}-{index}"`。由于注入在父模板中完成,testid 通过 Vue 的 `fallthroughAttrs` 机制自动透传到子组件的根元素。
|
|
474
|
+
|
|
475
|
+
对于多根组件(Fragment),`inheritAttrs` 不生效,`createMultiRootFixTransform` 自动为第一个 antd 组件根元素添加 `v-bind="$attrs"` 以确保透传。
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## License
|
|
480
|
+
|
|
481
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-vue-testid",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Vite plugin to auto-inject data-testid into Vue component templates for E2E testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
-
"README.
|
|
21
|
+
"README.md",
|
|
22
|
+
"README.zh.md"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
24
25
|
"build": "tsup && tsc -p tsconfig.build.json",
|