vite-plugin-vue-testid 1.1.0 → 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/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +40 -1
- package/package.json +3 -2
- package/README.en.md +0 -377
package/README.en.md
DELETED
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
# vite-plugin-vue-testid
|
|
2
|
-
|
|
3
|
-
A Vite plugin that auto-injects `data-testid` into Vue component templates via compile-time + runtime transforms, tailored for ant-design-vue 4.x. Dramatically reduces the effort of locating elements in E2E tests.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Compile-time injection** — Automatically adds `data-testid` to component templates via Vue compiler `nodeTransform`
|
|
8
|
-
- **Runtime injection** — Dynamically injects testIds into ant-design-vue teleported overlays (DatePicker, Cascader, Select, TreeSelect, etc.)
|
|
9
|
-
- **`<a-table>` support** — bodyCell slot components receive unique testIds in the format `columnName + rowIndex + componentName + counter`
|
|
10
|
-
- **Auto index destructuring** — If `#bodyCell="{ column, record }"` is missing `index`, the plugin patches it automatically
|
|
11
|
-
- **Preserves existing testIds** — Elements with manually assigned `data-testid` are skipped
|
|
12
|
-
- **Dual injection for teleport components** — DatePicker / RangePicker / Modal receive both `id` + `data-testid`
|
|
13
|
-
- **Date panel mode awareness** — date / month / year / decade panels are distinguished automatically
|
|
14
|
-
- **MutationObserver re-injection** — New DOM elements from panel switching, tree node expand/collapse are detected and injected automatically
|
|
15
|
-
- **Extensible** — Custom component lists, testId generation rules, and overlay injection strategies are supported
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
pnpm add -D vite-plugin-vue-testid
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
> Requirements: `vite >= 5.0.0`, `vue >= 3.3.0`, `@vue/compiler-core >= 3.5.0`
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Quick Start
|
|
30
|
-
|
|
31
|
-
### 1. Compile-time injection (`vite.config.ts`)
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
// vite.config.ts
|
|
35
|
-
import { defineConfig } from 'vite'
|
|
36
|
-
import vue from '@vitejs/plugin-vue'
|
|
37
|
-
import { createVueTestIdTransform } from 'vite-plugin-vue-testid'
|
|
38
|
-
|
|
39
|
-
export default defineConfig({
|
|
40
|
-
plugins: [
|
|
41
|
-
vue({
|
|
42
|
-
template: {
|
|
43
|
-
compilerOptions: {
|
|
44
|
-
nodeTransforms: [createVueTestIdTransform()]
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
]
|
|
49
|
-
})
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 2. Runtime injection (`main.ts`)
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
// main.ts
|
|
56
|
-
import { createApp } from 'vue'
|
|
57
|
-
import App from './App.vue'
|
|
58
|
-
import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
59
|
-
|
|
60
|
-
const app = createApp(App)
|
|
61
|
-
app.mount('#app')
|
|
62
|
-
|
|
63
|
-
// Call after mount to observe teleported panels and auto-inject testIds
|
|
64
|
-
setupAntDropdownTestIds()
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Generated testId Examples
|
|
70
|
-
|
|
71
|
-
### Regular Components
|
|
72
|
-
|
|
73
|
-
```html
|
|
74
|
-
<!-- Source -->
|
|
75
|
-
<a-input />
|
|
76
|
-
<a-select />
|
|
77
|
-
<a-button />
|
|
78
|
-
|
|
79
|
-
<!-- Compiled -->
|
|
80
|
-
<a-input data-testid="a-input-0" />
|
|
81
|
-
<a-select data-testid="a-select-1" />
|
|
82
|
-
<a-button data-testid="a-button-2" />
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Teleport Components (Dual Injection)
|
|
86
|
-
|
|
87
|
-
```html
|
|
88
|
-
<!-- Source -->
|
|
89
|
-
<a-date-picker />
|
|
90
|
-
<a-modal v-model:visible="show" title="Notice" />
|
|
91
|
-
|
|
92
|
-
<!-- Compiled -->
|
|
93
|
-
<a-date-picker id="a-date-picker-0" data-testid="a-date-picker-0" />
|
|
94
|
-
<a-modal id="a-modal-1" data-testid="a-modal-1" />
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### `<a-table>` bodyCell Components
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
Format: ${column.dataIndex}-${index}-${componentName}-${counter}
|
|
101
|
-
|
|
102
|
-
Examples:
|
|
103
|
-
- label-0-a-input-4 (label column, row 0)
|
|
104
|
-
- value-2-a-input-5 (value column, row 2)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Runtime DatePicker Panel
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
a-date-picker-0-dropdown # panel container
|
|
111
|
-
a-date-picker-0-dropdown-prev # previous month
|
|
112
|
-
a-date-picker-0-dropdown-next # next month
|
|
113
|
-
a-date-picker-0-dropdown-header-date # header container
|
|
114
|
-
a-date-picker-0-dropdown-header-month-btn # header month button
|
|
115
|
-
a-date-picker-0-dropdown-header-year-btn # header year button
|
|
116
|
-
a-date-picker-0-dropdown-date-2026-06-15 # date cell
|
|
117
|
-
a-date-picker-0-dropdown-ok # OK button
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
After switching to month/year panel (auto re-injected by MutationObserver):
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
a-date-picker-0-dropdown-header-month # month panel header
|
|
124
|
-
a-date-picker-0-dropdown-month-06 # June
|
|
125
|
-
a-date-picker-0-dropdown-header-year # year panel header
|
|
126
|
-
a-date-picker-0-dropdown-year-2026 # year 2026
|
|
127
|
-
a-date-picker-0-dropdown-decade-2020-2029 # decade 2020-2029
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Runtime RangePicker Panel
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
a-range-picker-0-dropdown-prev-left # left panel prev month
|
|
134
|
-
a-range-picker-0-dropdown-date-2026-05-15-left # left panel date
|
|
135
|
-
a-range-picker-0-dropdown-date-2026-06-20-right # right panel date
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Runtime TimePicker
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
a-time-picker-0-dropdown-time-column-0 # hour column
|
|
142
|
-
a-time-picker-0-dropdown-time-08 # 08 hour
|
|
143
|
-
a-time-picker-0-dropdown-time-column-1 # minute column
|
|
144
|
-
a-time-picker-0-dropdown-time-30 # 30 minutes
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Runtime Cascader
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
a-cascader-0-dropdown # panel container
|
|
151
|
-
a-cascader-0-dropdown-menu-0 # level 1 menu
|
|
152
|
-
a-cascader-0-dropdown-item-Zhejiang # Zhejiang option
|
|
153
|
-
a-cascader-0-dropdown-menu-1 # level 2 menu
|
|
154
|
-
a-cascader-0-dropdown-item-Hangzhou # Hangzhou option
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Runtime Select
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
a-select-0-dropdown # dropdown container
|
|
161
|
-
a-select-0-dropdown-option-Option1 # option
|
|
162
|
-
a-select-0-dropdown-option-Option2 # option
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Runtime TreeSelect
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
a-tree-select-0-dropdown # dropdown container
|
|
169
|
-
a-tree-select-0-dropdown-tree-root-1 # root node
|
|
170
|
-
a-tree-select-0-dropdown-tree-switcher-expand-root-1 # expand icon (collapsed)
|
|
171
|
-
a-tree-select-0-dropdown-tree-switcher-collapse-root-1 # expand icon (expanded)
|
|
172
|
-
a-tree-select-0-dropdown-tree-parent-1 # parent node (injected after expand)
|
|
173
|
-
a-tree-select-0-dropdown-tree-my-leaf # leaf node
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Compile-time + Runtime Menu
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
a-menu-0 # menu container (compile-time)
|
|
180
|
-
a-menu-item-Menu-Item-1 # menu item (runtime, text-based)
|
|
181
|
-
a-menu-item-Sub-Item-3 # submenu item
|
|
182
|
-
a-sub-menu-Submenu # submenu (runtime)
|
|
183
|
-
a-menu-item-group-Group # menu item group (runtime)
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
## Configuration
|
|
189
|
-
|
|
190
|
-
### Compile-time Options
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
import { createVueTestIdTransform } from 'vite-plugin-vue-testid'
|
|
194
|
-
|
|
195
|
-
createVueTestIdTransform({
|
|
196
|
-
/**
|
|
197
|
-
* Component tag names to inject testIds into (kebab-case)
|
|
198
|
-
* @default see "Default Supported Components" below
|
|
199
|
-
*/
|
|
200
|
-
components: ['a-input', 'a-select', 'my-button'],
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Custom testId generation function
|
|
204
|
-
* @param tagName component tag name
|
|
205
|
-
* @param count global increment counter
|
|
206
|
-
* @returns testId string
|
|
207
|
-
*/
|
|
208
|
-
generateId: (tagName, count) => `myapp-${tagName}-${count}`,
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* testId attribute name (use 'data-cy' for Cypress)
|
|
212
|
-
* @default 'data-testid'
|
|
213
|
-
*/
|
|
214
|
-
attributeName: 'data-cy',
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Components that also receive an id attribute
|
|
218
|
-
* @default ['a-date-picker', 'a-range-picker', 'a-time-picker', 'a-modal']
|
|
219
|
-
*/
|
|
220
|
-
idComponents: ['a-date-picker', 'a-modal'],
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* id attribute name
|
|
224
|
-
* @default 'id'
|
|
225
|
-
*/
|
|
226
|
-
idAttributeName: 'id',
|
|
227
|
-
})
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### Runtime Options
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
234
|
-
|
|
235
|
-
// Default config
|
|
236
|
-
const cleanup = setupAntDropdownTestIds()
|
|
237
|
-
|
|
238
|
-
// Custom config
|
|
239
|
-
const cleanup = setupAntDropdownTestIds({
|
|
240
|
-
/**
|
|
241
|
-
* testId attribute name, must match compile-time config
|
|
242
|
-
* @default 'data-testid'
|
|
243
|
-
*/
|
|
244
|
-
attributeName: 'data-testid',
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Custom panel selector → injection strategy mapping
|
|
248
|
-
* key: CSS selector
|
|
249
|
-
* value: injection function; set to undefined to disable a built-in panel
|
|
250
|
-
*/
|
|
251
|
-
panels: {
|
|
252
|
-
'.ant-picker-dropdown': undefined, // disable built-in Picker panel injection
|
|
253
|
-
'.my-custom-dropdown': (ctx) => {
|
|
254
|
-
// ctx.container — panel container HTMLElement
|
|
255
|
-
// ctx.trigger — trigger HTMLElement | null
|
|
256
|
-
// ctx.triggerTestId — the trigger's testId value
|
|
257
|
-
// ctx.attrName — testId attribute name
|
|
258
|
-
const prefix = `${ctx.triggerTestId}-dropdown`
|
|
259
|
-
ctx.container.setAttribute(ctx.attrName, prefix)
|
|
260
|
-
// ... custom injection logic
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
// Clean up observer on component unmount
|
|
266
|
-
onUnmounted(cleanup)
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### One-off Manual Injection
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
import { injectCurrentDropdowns } from 'vite-plugin-vue-testid/runtime'
|
|
273
|
-
|
|
274
|
-
// For SSR / hydration scenarios where MutationObserver is not desired
|
|
275
|
-
injectCurrentDropdowns({ attributeName: 'data-testid' })
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## Default Supported Components
|
|
281
|
-
|
|
282
|
-
| Component | Compile-time | Runtime |
|
|
283
|
-
|-----------|:------------:|:-------:|
|
|
284
|
-
| `a-input` | ✅ | — |
|
|
285
|
-
| `a-textarea` | ✅ | — |
|
|
286
|
-
| `a-input-number` | ✅ | — |
|
|
287
|
-
| `a-select` | ✅ | ✅ dropdown |
|
|
288
|
-
| `a-cascader` | ✅ | ✅ cascader panel |
|
|
289
|
-
| `a-tree-select` | ✅ | ✅ tree select panel |
|
|
290
|
-
| `a-radio-group` | ✅ | — |
|
|
291
|
-
| `a-checkbox-group` | ✅ | — |
|
|
292
|
-
| `a-date-picker` | ✅ id+testid | ✅ date panel |
|
|
293
|
-
| `a-range-picker` | ✅ id+testid | ✅ dual panel |
|
|
294
|
-
| `a-time-picker` | ✅ id+testid | ✅ time panel |
|
|
295
|
-
| `a-switch` | ✅ | — |
|
|
296
|
-
| `a-slider` | ✅ | — |
|
|
297
|
-
| `a-rate` | ✅ | — |
|
|
298
|
-
| `a-upload` | ✅ | — |
|
|
299
|
-
| `a-form-item` | ✅ | — |
|
|
300
|
-
| `a-button` | ✅ | — |
|
|
301
|
-
| `a-modal` | ✅ id+testid | — |
|
|
302
|
-
| `a-menu` | ✅ | — |
|
|
303
|
-
| `a-menu-item` | ✅ id | 🔧 runtime (text) |
|
|
304
|
-
| `a-sub-menu` | ✅ id | 🔧 runtime (text) |
|
|
305
|
-
| `a-menu-item-group` | ✅ id | 🔧 runtime (text) |
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
## Custom Panel Injection Strategies
|
|
310
|
-
|
|
311
|
-
Support overlays from other UI libraries by providing custom strategies:
|
|
312
|
-
|
|
313
|
-
```ts
|
|
314
|
-
import type { PanelInjectStrategy } from 'vite-plugin-vue-testid/runtime'
|
|
315
|
-
|
|
316
|
-
// Example: inject testIds into Element Plus overlays
|
|
317
|
-
const injectElDropdown: PanelInjectStrategy = (ctx) => {
|
|
318
|
-
const { container, triggerTestId, attrName } = ctx
|
|
319
|
-
const prefix = `${triggerTestId}-dropdown`
|
|
320
|
-
|
|
321
|
-
container.setAttribute(attrName, prefix)
|
|
322
|
-
|
|
323
|
-
container.querySelectorAll('.el-select-dropdown__item').forEach((item) => {
|
|
324
|
-
const el = item as HTMLElement
|
|
325
|
-
const text = el.textContent?.trim() || ''
|
|
326
|
-
el.setAttribute(attrName, `${prefix}-option-${text}`)
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
setupAntDropdownTestIds({
|
|
331
|
-
panels: {
|
|
332
|
-
'.el-select-dropdown': injectElDropdown,
|
|
333
|
-
'.el-cascader-dropdown': injectElDropdown,
|
|
334
|
-
},
|
|
335
|
-
})
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
|
-
## API Reference
|
|
341
|
-
|
|
342
|
-
### Compile-time
|
|
343
|
-
|
|
344
|
-
| Export | Type | Description |
|
|
345
|
-
|--------|------|-------------|
|
|
346
|
-
| `createVueTestIdTransform(opts?)` | `(node: RootNode \| TemplateChildNode) => void` | Creates a Vue compiler nodeTransform |
|
|
347
|
-
| `VueTestIdTransformOptions` | `interface` | Compile-time options type |
|
|
348
|
-
|
|
349
|
-
### Runtime (`/runtime` subpath)
|
|
350
|
-
|
|
351
|
-
| Export | Type | Description |
|
|
352
|
-
|--------|------|-------------|
|
|
353
|
-
| `setupAntDropdownTestIds(opts?)` | `() => void` | Starts MutationObserver to auto-inject overlay testIds, returns cleanup function |
|
|
354
|
-
| `injectCurrentDropdowns(opts?)` | `void` | One-off manual injection of currently visible overlays |
|
|
355
|
-
| `RuntimeOptions` | `interface` | Runtime options type |
|
|
356
|
-
| `PanelContext` | `interface` | Panel injection context type |
|
|
357
|
-
| `PanelInjectStrategy` | `(ctx: PanelContext) => void` | Panel injection strategy function type |
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
## How It Works
|
|
362
|
-
|
|
363
|
-
### Compile-time
|
|
364
|
-
|
|
365
|
-
Uses Vue's compiler `nodeTransform` hook to traverse template nodes at AST stage. When target component tags are matched, `data-testid` attribute nodes are appended directly. Since injection happens at compile time, there is zero runtime performance impact.
|
|
366
|
-
|
|
367
|
-
### Runtime
|
|
368
|
-
|
|
369
|
-
ant-design-vue 4.x components like DatePicker, Select, and Cascader use Teleport to render overlays into `document.body`, making them unreachable at compile time. The runtime module uses `MutationObserver` to watch DOM changes under `body`, matches ant-design panel CSS classes, and automatically injects trigger-associated testIds into panel elements.
|
|
370
|
-
|
|
371
|
-
For components like DatePicker with `inheritAttrs: false`, `data-testid` injected at compile time cannot reach the DOM. The plugin works around this by also injecting an `id` attribute (which ant-design-vue explicitly forwards to the internal `<input>`) as a fallback identifier.
|
|
372
|
-
|
|
373
|
-
---
|
|
374
|
-
|
|
375
|
-
## License
|
|
376
|
-
|
|
377
|
-
MIT
|