vite-plugin-vue-testid 1.0.7 → 1.0.9
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/dist/index.d.ts +44 -51
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -152
- package/dist/runtime.d.ts +39 -33
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +273 -97
- package/package.json +2 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,64 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
/**
|
|
2
|
+
* vite-plugin-vue-testid
|
|
3
|
+
*
|
|
4
|
+
* 为 ant-design-vue 组件的 DOM 元素自动注入 data-testid 属性,
|
|
5
|
+
* 全运行时实现,无需编译期模版转换。
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // vite.config.ts — 无需特殊配置
|
|
10
|
+
* import vue from '@vitejs/plugin-vue'
|
|
11
|
+
* export default defineConfig({ plugins: [vue()] })
|
|
12
|
+
*
|
|
13
|
+
* // main.ts — 启动运行时注入
|
|
14
|
+
* import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
15
|
+
* createApp(App).mount('#app')
|
|
16
|
+
* setupAntTestIds()
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { Plugin } from 'vite';
|
|
20
|
+
export type { RuntimeOptions, PanelContext, PanelInjectStrategy } from './runtime';
|
|
21
|
+
export interface VueTestIdPluginOptions {
|
|
15
22
|
/**
|
|
16
23
|
* testId 属性名
|
|
17
24
|
* @default 'data-testid'
|
|
18
25
|
*/
|
|
19
26
|
attributeName?: string;
|
|
20
|
-
/**
|
|
21
|
-
* 需要同时注入 id + testId 的组件(如弹窗、日期选择器等 teleport 组件)
|
|
22
|
-
* @default ['a-date-picker', 'a-range-picker', 'a-time-picker', 'a-modal']
|
|
23
|
-
*/
|
|
24
|
-
idComponents?: string[];
|
|
25
|
-
/**
|
|
26
|
-
* teleport 组件的 id 属性名
|
|
27
|
-
* @default 'id'
|
|
28
|
-
*/
|
|
29
|
-
idAttributeName?: string;
|
|
30
27
|
}
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* Vite 插件(可选)。
|
|
30
|
+
* 当前仅作为占位,实际 testid 注入完全由运行时 `setupAntTestIds()` 完成。
|
|
34
31
|
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
*
|
|
32
|
+
* 如果需要在构建时做额外配置,可通过此插件传入选项。
|
|
33
|
+
*/
|
|
34
|
+
export declare function vueTestIdPlugin(_opts?: VueTestIdPluginOptions): Plugin;
|
|
35
|
+
/**
|
|
36
|
+
* @deprecated 此函数已废弃。testid 注入现在完全在运行时进行,
|
|
37
|
+
* 不再需要编译期 AST transform。请从 vite.config.ts 中移除 `nodeTransforms` 配置,
|
|
38
|
+
* 改为在 main.ts 中调用 `setupAntTestIds()`。
|
|
40
39
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* })
|
|
52
|
-
* ```
|
|
40
|
+
* @example 迁移方式
|
|
41
|
+
* ```diff
|
|
42
|
+
* // vite.config.ts
|
|
43
|
+
* - import { createVueTestIdTransform } from 'vite-plugin-vue-testid'
|
|
44
|
+
* - vue({ template: { compilerOptions: { nodeTransforms: [
|
|
45
|
+
* - createVueTestIdTransform()
|
|
46
|
+
* - ]}}})
|
|
47
|
+
|
|
48
|
+
* + import vue from '@vitejs/plugin-vue'
|
|
49
|
+
* + vue()
|
|
53
50
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* components: ['a-input', 'a-select', 'el-input'],
|
|
58
|
-
* generateId: (tag, n) => `myapp-${tag}-${n}`,
|
|
59
|
-
* attributeName: 'data-cy',
|
|
60
|
-
* })
|
|
51
|
+
* // main.ts
|
|
52
|
+
* + import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
53
|
+
* + setupAntTestIds()
|
|
61
54
|
* ```
|
|
62
55
|
*/
|
|
63
|
-
export declare function createVueTestIdTransform(
|
|
56
|
+
export declare function createVueTestIdTransform(): () => void;
|
|
64
57
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAIlF,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAItE;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,IAAI,CASrD"}
|
package/dist/index.js
CHANGED
|
@@ -1,161 +1,19 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
|
|
3
|
-
var DEFAULT_COMPONENTS = [
|
|
4
|
-
"a-input",
|
|
5
|
-
"a-textarea",
|
|
6
|
-
"a-input-number",
|
|
7
|
-
"a-select",
|
|
8
|
-
"a-cascader",
|
|
9
|
-
"a-tree-select",
|
|
10
|
-
"a-radio-group",
|
|
11
|
-
"a-checkbox-group",
|
|
12
|
-
"a-date-picker",
|
|
13
|
-
"a-range-picker",
|
|
14
|
-
"a-time-picker",
|
|
15
|
-
"a-switch",
|
|
16
|
-
"a-slider",
|
|
17
|
-
"a-rate",
|
|
18
|
-
"a-upload",
|
|
19
|
-
"a-form-item",
|
|
20
|
-
"a-button",
|
|
21
|
-
"a-modal",
|
|
22
|
-
"a-menu",
|
|
23
|
-
"a-menu-item",
|
|
24
|
-
"a-sub-menu",
|
|
25
|
-
"a-menu-item-group"
|
|
26
|
-
];
|
|
27
|
-
var DEFAULT_ID_COMPONENTS = [
|
|
28
|
-
"a-date-picker",
|
|
29
|
-
"a-range-picker",
|
|
30
|
-
"a-time-picker",
|
|
31
|
-
"a-modal",
|
|
32
|
-
"a-upload",
|
|
33
|
-
"a-menu-item",
|
|
34
|
-
"a-sub-menu",
|
|
35
|
-
"a-menu-item-group"
|
|
36
|
-
];
|
|
37
|
-
function resolveOptions(raw) {
|
|
2
|
+
function vueTestIdPlugin(_opts) {
|
|
38
3
|
return {
|
|
39
|
-
|
|
40
|
-
generateId: raw?.generateId ?? ((tag, n) => `${tag}-${n}`),
|
|
41
|
-
attributeName: raw?.attributeName ?? "data-testid",
|
|
42
|
-
idComponents: raw?.idComponents ?? DEFAULT_ID_COMPONENTS,
|
|
43
|
-
idAttributeName: raw?.idAttributeName ?? "id"
|
|
4
|
+
name: "vite-plugin-vue-testid"
|
|
44
5
|
};
|
|
45
6
|
}
|
|
46
|
-
function
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
} else if (exp.type === NodeTypes.COMPOUND_EXPRESSION) {
|
|
53
|
-
const hasIndex = exp.children.some((c) => {
|
|
54
|
-
if (typeof c === "string") return /\bindex\b/.test(c);
|
|
55
|
-
if (typeof c === "symbol") return false;
|
|
56
|
-
if (c.type === NodeTypes.TEXT || c.type === NodeTypes.SIMPLE_EXPRESSION) {
|
|
57
|
-
return /\bindex\b/.test(c.content);
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
});
|
|
61
|
-
if (hasIndex) return;
|
|
62
|
-
for (let i = exp.children.length - 1; i >= 0; i--) {
|
|
63
|
-
const c = exp.children[i];
|
|
64
|
-
if (typeof c === "string") {
|
|
65
|
-
exp.children[i] = c.replace(/}$/, ", index}");
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
if (typeof c !== "symbol" && c.type === NodeTypes.TEXT) {
|
|
69
|
-
c.content = c.content.replace(/}$/, ", index}");
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
7
|
+
function createVueTestIdTransform() {
|
|
8
|
+
if (typeof console !== "undefined") {
|
|
9
|
+
console.warn(
|
|
10
|
+
"[vite-plugin-vue-testid] createVueTestIdTransform() is deprecated. Remove nodeTransforms from vite.config.ts and call setupAntTestIds() in main.ts instead. See https://github.com/... for migration guide."
|
|
11
|
+
);
|
|
73
12
|
}
|
|
74
|
-
|
|
75
|
-
function hasExistingTestId(node, attrName) {
|
|
76
|
-
return node.props.some(
|
|
77
|
-
(p) => p.type === NodeTypes.ATTRIBUTE && p.name === attrName || p.type === NodeTypes.DIRECTIVE && p.name === "bind" && p.arg && p.arg.type === NodeTypes.SIMPLE_EXPRESSION && p.arg.content === attrName
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
function createStaticAttr(name, value, loc) {
|
|
81
|
-
return {
|
|
82
|
-
type: NodeTypes.ATTRIBUTE,
|
|
83
|
-
name,
|
|
84
|
-
nameLoc: loc,
|
|
85
|
-
value: { type: NodeTypes.TEXT, content: value, loc },
|
|
86
|
-
loc
|
|
13
|
+
return () => {
|
|
87
14
|
};
|
|
88
15
|
}
|
|
89
|
-
function createBindDirective(attrName, exprContent, loc) {
|
|
90
|
-
return {
|
|
91
|
-
type: NodeTypes.DIRECTIVE,
|
|
92
|
-
name: "bind",
|
|
93
|
-
arg: {
|
|
94
|
-
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
95
|
-
content: attrName,
|
|
96
|
-
isStatic: true,
|
|
97
|
-
constType: ConstantTypes.CAN_STRINGIFY,
|
|
98
|
-
loc
|
|
99
|
-
},
|
|
100
|
-
exp: {
|
|
101
|
-
type: NodeTypes.SIMPLE_EXPRESSION,
|
|
102
|
-
content: exprContent,
|
|
103
|
-
isStatic: false,
|
|
104
|
-
constType: ConstantTypes.NOT_CONSTANT,
|
|
105
|
-
loc
|
|
106
|
-
},
|
|
107
|
-
modifiers: [],
|
|
108
|
-
loc
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function createASTTransform(opts) {
|
|
112
|
-
let count = 0;
|
|
113
|
-
let insideTableDepth = 0;
|
|
114
|
-
let insideBodyCellDepth = 0;
|
|
115
|
-
const componentSet = new Set(opts.components);
|
|
116
|
-
const idComponentSet = new Set(opts.idComponents);
|
|
117
|
-
return (node) => {
|
|
118
|
-
if (node.type !== NodeTypes.ELEMENT) return;
|
|
119
|
-
const tagName = node.tag.toLowerCase();
|
|
120
|
-
if (tagName === "a-table") {
|
|
121
|
-
insideTableDepth++;
|
|
122
|
-
return () => {
|
|
123
|
-
insideTableDepth--;
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
if (tagName === "template" && insideTableDepth > 0) {
|
|
127
|
-
const slotDir = node.props.find(
|
|
128
|
-
(p) => p.type === NodeTypes.DIRECTIVE && p.name === "slot" && p.arg && p.arg.type === NodeTypes.SIMPLE_EXPRESSION && p.arg.content === "bodyCell"
|
|
129
|
-
);
|
|
130
|
-
if (slotDir && slotDir.type === NodeTypes.DIRECTIVE) {
|
|
131
|
-
ensureSlotIndex(slotDir.exp);
|
|
132
|
-
insideBodyCellDepth++;
|
|
133
|
-
return () => {
|
|
134
|
-
insideBodyCellDepth--;
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (insideTableDepth > 0 && insideBodyCellDepth === 0) return;
|
|
139
|
-
if (!componentSet.has(tagName)) return;
|
|
140
|
-
if (hasExistingTestId(node, opts.attributeName)) return;
|
|
141
|
-
const testIdValue = opts.generateId(tagName, count++);
|
|
142
|
-
if (insideBodyCellDepth > 0) {
|
|
143
|
-
const expr = `\`\${column?.dataIndex || column?.key || 'cell'}-\${index}-${testIdValue}\``;
|
|
144
|
-
node.props.push(createBindDirective(opts.attributeName, expr, node.loc));
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
if (idComponentSet.has(tagName)) {
|
|
148
|
-
node.props.push(createStaticAttr(opts.idAttributeName, testIdValue, node.loc));
|
|
149
|
-
node.props.push(createStaticAttr(opts.attributeName, testIdValue, node.loc));
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
node.props.push(createStaticAttr(opts.attributeName, testIdValue, node.loc));
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function createVueTestIdTransform(rawOpts) {
|
|
156
|
-
const opts = resolveOptions(rawOpts);
|
|
157
|
-
return createASTTransform(opts);
|
|
158
|
-
}
|
|
159
16
|
export {
|
|
160
|
-
createVueTestIdTransform
|
|
17
|
+
createVueTestIdTransform,
|
|
18
|
+
vueTestIdPlugin
|
|
161
19
|
};
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 运行时 testid 注入:通过 MutationObserver 为 ant-design-vue 组件的 DOM 元素
|
|
3
|
+
* 自动注入 data-testid,解决编译期注入的诸多限制。
|
|
4
4
|
*
|
|
5
|
-
* 使用方式(在 main.ts
|
|
5
|
+
* 使用方式(在 main.ts 中调用一次即可):
|
|
6
6
|
* ```ts
|
|
7
|
-
* import {
|
|
7
|
+
* import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* createApp(App).mount('#app')
|
|
10
|
+
* setupAntTestIds()
|
|
10
11
|
* ```
|
|
11
12
|
*/
|
|
12
13
|
export interface RuntimeOptions {
|
|
13
14
|
/**
|
|
14
|
-
* testId
|
|
15
|
+
* testId 属性名
|
|
15
16
|
* @default 'data-testid'
|
|
16
17
|
*/
|
|
17
18
|
attributeName?: string;
|
|
19
|
+
/**
|
|
20
|
+
* 自定义组件 CSS 选择器 → testid 前缀映射。
|
|
21
|
+
* 会与默认映射合并(自定义覆盖默认)。
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* setupAntTestIds({
|
|
26
|
+
* components: {
|
|
27
|
+
* '.el-input': 'el-input',
|
|
28
|
+
* '.el-button': 'el-button',
|
|
29
|
+
* }
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
components?: Record<string, string>;
|
|
18
34
|
/**
|
|
19
35
|
* 需要监听的 teleport 面板 CSS 选择器映射
|
|
20
36
|
* key: 面板容器选择器
|
|
@@ -34,41 +50,31 @@ export interface PanelContext {
|
|
|
34
50
|
}
|
|
35
51
|
export type PanelInjectStrategy = (ctx: PanelContext) => void;
|
|
36
52
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
53
|
+
* 启动全运行时 testid 注入。
|
|
54
|
+
*
|
|
55
|
+
* 覆盖范围:
|
|
56
|
+
* - 所有 ant-design-vue 组件根元素(input / select / button / picker / ...)
|
|
57
|
+
* - teleport 弹出层面板(日期选择器 / 级联选择器 / 下拉选择器)
|
|
58
|
+
* - Menu 子元素(inheritAttrs:false 兜底)
|
|
59
|
+
* - Upload 内部 <input type="file">
|
|
60
|
+
*
|
|
61
|
+
* @returns 清理函数,调用后停止监听
|
|
39
62
|
*
|
|
40
63
|
* @example
|
|
41
64
|
* ```ts
|
|
42
65
|
* // main.ts
|
|
43
|
-
* import {
|
|
66
|
+
* import { setupAntTestIds } from 'vite-plugin-vue-testid/runtime'
|
|
44
67
|
*
|
|
45
68
|
* createApp(App).mount('#app')
|
|
46
|
-
*
|
|
47
|
-
* ```
|
|
48
|
-
*
|
|
49
|
-
* testId 注入结果示例:
|
|
50
|
-
* ```
|
|
51
|
-
* // 日期面板(初始)
|
|
52
|
-
* a-date-picker-0-dropdown // 面板容器
|
|
53
|
-
* a-date-picker-0-dropdown-prev // 上月/上一级
|
|
54
|
-
* a-date-picker-0-dropdown-next // 下月/下一级
|
|
55
|
-
* a-date-picker-0-dropdown-header-date // header 容器
|
|
56
|
-
* a-date-picker-0-dropdown-header-month-btn // header 内的月份按钮("Jun"/"6月")
|
|
57
|
-
* a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮("2026")
|
|
58
|
-
* a-date-picker-0-dropdown-date-2026-06-15 // 日期单元格
|
|
59
|
-
* a-date-picker-0-dropdown-ok // 确定按钮
|
|
60
|
-
*
|
|
61
|
-
* // 点击月份按钮 → 月面板(MutationObserver 自动重注入)
|
|
62
|
-
* a-date-picker-0-dropdown-header-month // header 容器
|
|
63
|
-
* a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮
|
|
64
|
-
* a-date-picker-0-dropdown-month-06 // 6月按钮
|
|
65
|
-
*
|
|
66
|
-
* // 点击年份按钮 → 年面板
|
|
67
|
-
* a-date-picker-0-dropdown-header-year // header 容器
|
|
68
|
-
* a-date-picker-0-dropdown-year-2026 // 2026年按钮
|
|
69
|
+
* setupAntTestIds()
|
|
69
70
|
* ```
|
|
70
71
|
*/
|
|
71
|
-
export declare function
|
|
72
|
+
export declare function setupAntTestIds(opts?: RuntimeOptions): () => void;
|
|
73
|
+
/**
|
|
74
|
+
* @deprecated 使用 {@link setupAntTestIds} 替代。
|
|
75
|
+
* 保留此别名以兼容现有代码。
|
|
76
|
+
*/
|
|
77
|
+
export declare const setupAntDropdownTestIds: typeof setupAntTestIds;
|
|
72
78
|
/**
|
|
73
79
|
* 手动为当前已存在或即将出现的弹出面板注入 testId(一次性补丁)。
|
|
74
80
|
* 适用于 SSR/hydration 或不想用 MutationObserver 的场景。
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC,CAAA;CACzD;AAED,MAAM,WAAW,YAAY;IAC3B,kBAAkB;IAClB,SAAS,EAAE,WAAW,CAAA;IACtB,iBAAiB;IACjB,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,oBAAoB;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAA;AA6c7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE,cAAmB,GAAG,MAAM,IAAI,CA4KrE;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,wBAAkB,CAAA;AAMtD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,cAAmB,GAAG,IAAI,CA4GtE"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,68 @@
|
|
|
1
1
|
// src/runtime.ts
|
|
2
|
+
var DEFAULT_COMPONENT_MATCHERS = [
|
|
3
|
+
// ── 输入框变体(按 specificity 降序)──
|
|
4
|
+
{ selector: ".ant-input-search", prefix: "a-input-search" },
|
|
5
|
+
{ selector: ".ant-input-affix-wrapper", prefix: "a-input" },
|
|
6
|
+
{ selector: ".ant-input-textarea", prefix: "a-textarea" },
|
|
7
|
+
{ selector: ".ant-input-number", prefix: "a-input-number" },
|
|
8
|
+
// ── 选择器 ──
|
|
9
|
+
{ selector: ".ant-select", prefix: "a-select", withId: true },
|
|
10
|
+
{ selector: ".ant-cascader", prefix: "a-cascader", withId: true },
|
|
11
|
+
{ selector: ".ant-tree-select", prefix: "a-tree-select" },
|
|
12
|
+
// ── 日期/时间 ──
|
|
13
|
+
{ selector: ".ant-picker", prefix: "a-date-picker", withId: true },
|
|
14
|
+
// ── 表单组件 ──
|
|
15
|
+
{ selector: ".ant-radio-group", prefix: "a-radio-group" },
|
|
16
|
+
{ selector: ".ant-radio-wrapper", prefix: "a-radio" },
|
|
17
|
+
{ selector: ".ant-checkbox-group", prefix: "a-checkbox-group" },
|
|
18
|
+
{ selector: ".ant-checkbox-wrapper", prefix: "a-checkbox" },
|
|
19
|
+
{ selector: ".ant-switch", prefix: "a-switch" },
|
|
20
|
+
{ selector: ".ant-slider", prefix: "a-slider" },
|
|
21
|
+
{ selector: ".ant-rate", prefix: "a-rate" },
|
|
22
|
+
{ selector: ".ant-form-item", prefix: "a-form-item" },
|
|
23
|
+
// ── 按钮 & 上传 ──
|
|
24
|
+
{ selector: ".ant-btn", prefix: "a-button" },
|
|
25
|
+
{ selector: ".ant-upload", prefix: "a-upload", withId: true },
|
|
26
|
+
// ── 弹窗 ──
|
|
27
|
+
{ selector: ".ant-modal-wrap", prefix: "a-modal", withId: true },
|
|
28
|
+
// ── 菜单 ──
|
|
29
|
+
{ selector: ".ant-menu", prefix: "a-menu" },
|
|
30
|
+
// ── 纯 input / textarea(必须排在最后)──
|
|
31
|
+
// 只有不在其他组件包裹内时才注入(通过 closest 排除)
|
|
32
|
+
{ selector: "input.ant-input", prefix: "a-input" },
|
|
33
|
+
{ selector: "textarea.ant-input", prefix: "a-textarea" }
|
|
34
|
+
];
|
|
35
|
+
var SKIP_PARENT_SELECTORS = [
|
|
36
|
+
".ant-input-affix-wrapper",
|
|
37
|
+
".ant-input-textarea",
|
|
38
|
+
".ant-input-search",
|
|
39
|
+
".ant-input-number",
|
|
40
|
+
".ant-picker",
|
|
41
|
+
".ant-select",
|
|
42
|
+
".ant-cascader"
|
|
43
|
+
];
|
|
44
|
+
function isInnerInput(el) {
|
|
45
|
+
if (el.tagName !== "INPUT" && el.tagName !== "TEXTAREA") return false;
|
|
46
|
+
for (const sel of SKIP_PARENT_SELECTORS) {
|
|
47
|
+
if (el.closest(sel)) return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
function injectComponentRoots(root, matchers, attrName, counter) {
|
|
52
|
+
for (const { selector, prefix, withId } of matchers) {
|
|
53
|
+
const candidates = root.matches(selector) ? [root] : Array.from(root.querySelectorAll(selector));
|
|
54
|
+
for (const el of candidates) {
|
|
55
|
+
if (!(el instanceof HTMLElement)) continue;
|
|
56
|
+
if (el.hasAttribute(attrName)) continue;
|
|
57
|
+
if (isInnerInput(el)) continue;
|
|
58
|
+
const testId = `${prefix}-${counter.value++}`;
|
|
59
|
+
el.setAttribute(attrName, testId);
|
|
60
|
+
if (withId && !el.hasAttribute("id")) {
|
|
61
|
+
el.setAttribute("id", testId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
2
66
|
function getPanelMode(panel) {
|
|
3
67
|
if (panel.classList.contains("ant-picker-month-panel")) return "month";
|
|
4
68
|
if (panel.classList.contains("ant-picker-year-panel")) return "year";
|
|
@@ -42,23 +106,6 @@ function injectHeaderViewButtons(panel, prefix, attrName, suffix) {
|
|
|
42
106
|
el.setAttribute(attrName, `${prefix}-${name}${suffix}`);
|
|
43
107
|
});
|
|
44
108
|
}
|
|
45
|
-
var _observedContainers = /* @__PURE__ */ new WeakSet();
|
|
46
|
-
function injectSinglePanel(panel, prefix, attrName, suffix) {
|
|
47
|
-
const mode = getPanelMode(panel);
|
|
48
|
-
const setAttr = (selector, name) => {
|
|
49
|
-
const el = panel.querySelector(selector);
|
|
50
|
-
if (el && !el.hasAttribute(attrName)) {
|
|
51
|
-
el.setAttribute(attrName, `${prefix}-${name}${suffix}`);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
setAttr(".ant-picker-header-super-prev-btn", "super-prev");
|
|
55
|
-
setAttr(".ant-picker-header-prev-btn", "prev");
|
|
56
|
-
setAttr(".ant-picker-header-next-btn", "next");
|
|
57
|
-
setAttr(".ant-picker-header-super-next-btn", "super-next");
|
|
58
|
-
setAttr(".ant-picker-header-view", `header-${mode}`);
|
|
59
|
-
injectHeaderViewButtons(panel, prefix, attrName, suffix);
|
|
60
|
-
injectPanelCells(panel, prefix, attrName, suffix);
|
|
61
|
-
}
|
|
62
109
|
function injectPanelCells(panel, prefix, attrName, suffix) {
|
|
63
110
|
const mode = getPanelMode(panel);
|
|
64
111
|
const cells = panel.querySelectorAll(".ant-picker-cell-inner");
|
|
@@ -73,20 +120,32 @@ function injectPanelCells(panel, prefix, attrName, suffix) {
|
|
|
73
120
|
el.setAttribute(attrName, `${prefix}-month-${label}${suffix}`);
|
|
74
121
|
break;
|
|
75
122
|
case "year":
|
|
76
|
-
|
|
77
|
-
el.setAttribute(attrName, `${prefix}-year-${label}${suffix}`);
|
|
123
|
+
el.setAttribute(attrName, `${prefix}-year-${raw}${suffix}`);
|
|
78
124
|
break;
|
|
79
125
|
case "decade":
|
|
80
|
-
|
|
81
|
-
el.setAttribute(attrName, `${prefix}-decade-${label}${suffix}`);
|
|
126
|
+
el.setAttribute(attrName, `${prefix}-decade-${raw}${suffix}`);
|
|
82
127
|
break;
|
|
83
128
|
default:
|
|
84
|
-
|
|
85
|
-
el.setAttribute(attrName, `${prefix}-date-${label}${suffix}`);
|
|
129
|
+
el.setAttribute(attrName, `${prefix}-date-${raw}${suffix}`);
|
|
86
130
|
break;
|
|
87
131
|
}
|
|
88
132
|
});
|
|
89
|
-
|
|
133
|
+
}
|
|
134
|
+
function injectSinglePanel(panel, prefix, attrName, suffix) {
|
|
135
|
+
const mode = getPanelMode(panel);
|
|
136
|
+
const setAttr = (selector, name) => {
|
|
137
|
+
const el = panel.querySelector(selector);
|
|
138
|
+
if (el && !el.hasAttribute(attrName)) {
|
|
139
|
+
el.setAttribute(attrName, `${prefix}-${name}${suffix}`);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
setAttr(".ant-picker-header-super-prev-btn", "super-prev");
|
|
143
|
+
setAttr(".ant-picker-header-prev-btn", "prev");
|
|
144
|
+
setAttr(".ant-picker-header-next-btn", "next");
|
|
145
|
+
setAttr(".ant-picker-header-super-next-btn", "super-next");
|
|
146
|
+
setAttr(".ant-picker-header-view", `header-${mode}`);
|
|
147
|
+
injectHeaderViewButtons(panel, prefix, attrName, suffix);
|
|
148
|
+
injectPanelCells(panel, prefix, attrName, suffix);
|
|
90
149
|
}
|
|
91
150
|
function injectPickerPanel(ctx) {
|
|
92
151
|
const { container, triggerTestId, attrName } = ctx;
|
|
@@ -202,14 +261,39 @@ function injectSelectPanel(ctx) {
|
|
|
202
261
|
observer.observe(container, { childList: true, subtree: true });
|
|
203
262
|
}
|
|
204
263
|
}
|
|
264
|
+
function injectMenuItems(attrName, root = document) {
|
|
265
|
+
const itemRules = [
|
|
266
|
+
{ selector: ".ant-menu-item", prefix: "a-menu-item-" },
|
|
267
|
+
{ selector: ".ant-menu-submenu", textSelector: ".ant-menu-submenu-title", prefix: "a-sub-menu-" },
|
|
268
|
+
{ selector: ".ant-menu-item-group", textSelector: ".ant-menu-item-group-title", prefix: "a-menu-item-group-" }
|
|
269
|
+
];
|
|
270
|
+
for (const rule of itemRules) {
|
|
271
|
+
const items = root.querySelectorAll(rule.selector);
|
|
272
|
+
items.forEach((item) => {
|
|
273
|
+
const el = item;
|
|
274
|
+
if (el.hasAttribute(attrName)) return;
|
|
275
|
+
const id = el.getAttribute("id");
|
|
276
|
+
if (id) {
|
|
277
|
+
el.setAttribute(attrName, id);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const textTarget = rule.textSelector ? el.querySelector(rule.textSelector) : el;
|
|
281
|
+
const text = (textTarget?.textContent || "").trim().replace(/\s+/g, "-");
|
|
282
|
+
if (text) {
|
|
283
|
+
el.setAttribute(attrName, rule.prefix + text);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
205
288
|
var DEFAULT_PANELS = {
|
|
206
289
|
".ant-picker-dropdown": injectPickerPanel,
|
|
207
290
|
".ant-cascader-dropdown": injectCascaderPanel,
|
|
208
291
|
".ant-select-dropdown": injectSelectPanel
|
|
209
292
|
};
|
|
293
|
+
var _observedContainers = /* @__PURE__ */ new WeakSet();
|
|
210
294
|
function getTriggerTestId(trigger, attrName) {
|
|
211
|
-
if (!trigger) return "unknown
|
|
212
|
-
return trigger.getAttribute(attrName) || trigger.getAttribute("id") || "unknown
|
|
295
|
+
if (!trigger) return "unknown";
|
|
296
|
+
return trigger.getAttribute(attrName) || trigger.getAttribute("id") || "unknown";
|
|
213
297
|
}
|
|
214
298
|
function findActiveTrigger(attrName) {
|
|
215
299
|
const focused = document.querySelector(".ant-picker-focused");
|
|
@@ -230,90 +314,137 @@ function findActiveTrigger(attrName) {
|
|
|
230
314
|
if (expanded) return expanded;
|
|
231
315
|
return null;
|
|
232
316
|
}
|
|
233
|
-
function
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (id) {
|
|
243
|
-
el.setAttribute(attrName, id);
|
|
244
|
-
return;
|
|
317
|
+
function setupAntTestIds(opts = {}) {
|
|
318
|
+
const attrName = opts.attributeName ?? "data-testid";
|
|
319
|
+
const panels = { ...DEFAULT_PANELS, ...opts.panels };
|
|
320
|
+
let matchers = DEFAULT_COMPONENT_MATCHERS;
|
|
321
|
+
if (opts.components) {
|
|
322
|
+
const customKeys = new Set(Object.keys(opts.components));
|
|
323
|
+
matchers = matchers.filter((m) => !customKeys.has(m.selector));
|
|
324
|
+
for (const [selector, prefix] of Object.entries(opts.components)) {
|
|
325
|
+
matchers.push({ selector, prefix });
|
|
245
326
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
327
|
+
}
|
|
328
|
+
const counter = { value: 0 };
|
|
329
|
+
function processNode(node) {
|
|
330
|
+
if (!(node instanceof HTMLElement)) return;
|
|
331
|
+
injectComponentRoots(node, matchers, attrName, counter);
|
|
332
|
+
injectMenuItems(attrName, node);
|
|
333
|
+
for (const [selector, strategy] of Object.entries(panels)) {
|
|
334
|
+
const containers = node.matches(selector) ? [node] : Array.from(node.querySelectorAll(selector));
|
|
335
|
+
for (const container of containers) {
|
|
336
|
+
if (!(container instanceof HTMLElement)) continue;
|
|
337
|
+
if (container.hasAttribute(attrName)) continue;
|
|
338
|
+
if (strategy) {
|
|
339
|
+
queueMicrotask(() => {
|
|
340
|
+
const trigger = findActiveTrigger(attrName);
|
|
341
|
+
const triggerTestId = getTriggerTestId(trigger, attrName);
|
|
342
|
+
strategy({ container, trigger, triggerTestId, attrName });
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
250
346
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
347
|
+
const inputNumbers = node.matches(`.ant-input-number[${attrName}]`) ? [node] : Array.from(node.querySelectorAll(`.ant-input-number[${attrName}]`));
|
|
348
|
+
for (const inputNumber of inputNumbers) {
|
|
349
|
+
if (!(inputNumber instanceof HTMLElement)) continue;
|
|
350
|
+
const testId = inputNumber.getAttribute(attrName) || "";
|
|
351
|
+
if (!testId) continue;
|
|
352
|
+
const input = inputNumber.querySelector(".ant-input-number-input");
|
|
353
|
+
if (input && !input.hasAttribute(attrName)) {
|
|
354
|
+
input.setAttribute(attrName, testId);
|
|
355
|
+
}
|
|
356
|
+
const upHandler = inputNumber.querySelector(".ant-input-number-handler-up");
|
|
357
|
+
if (upHandler && !upHandler.hasAttribute(attrName)) {
|
|
358
|
+
upHandler.setAttribute(attrName, `${testId}-up`);
|
|
359
|
+
}
|
|
360
|
+
const downHandler = inputNumber.querySelector(".ant-input-number-handler-down");
|
|
361
|
+
if (downHandler && !downHandler.hasAttribute(attrName)) {
|
|
362
|
+
downHandler.setAttribute(attrName, `${testId}-down`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const sliders = node.matches(`.ant-slider[${attrName}]`) ? [node] : Array.from(node.querySelectorAll(`.ant-slider[${attrName}]`));
|
|
366
|
+
for (const slider of sliders) {
|
|
367
|
+
if (!(slider instanceof HTMLElement)) continue;
|
|
368
|
+
const testId = slider.getAttribute(attrName) || "";
|
|
369
|
+
if (!testId) continue;
|
|
370
|
+
const handles = slider.querySelectorAll(".ant-slider-handle");
|
|
371
|
+
handles.forEach((h, i) => {
|
|
372
|
+
const el = h;
|
|
373
|
+
if (!el.hasAttribute(attrName)) {
|
|
374
|
+
el.setAttribute(attrName, `${testId}-handle-${i}`);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const rates = node.matches(`.ant-rate[${attrName}]`) ? [node] : Array.from(node.querySelectorAll(`.ant-rate[${attrName}]`));
|
|
379
|
+
for (const rate of rates) {
|
|
380
|
+
if (!(rate instanceof HTMLElement)) continue;
|
|
381
|
+
const testId = rate.getAttribute(attrName) || "";
|
|
382
|
+
if (!testId) continue;
|
|
383
|
+
const stars = rate.querySelectorAll(".ant-rate-star");
|
|
384
|
+
stars.forEach((s, i) => {
|
|
385
|
+
const el = s;
|
|
386
|
+
if (!el.hasAttribute(attrName)) {
|
|
387
|
+
el.setAttribute(attrName, `${testId}-star-${i}`);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const uploadByTestId = node.matches(`.ant-upload[${attrName}]`) ? [node] : Array.from(node.querySelectorAll(`.ant-upload[${attrName}]`));
|
|
392
|
+
const uploadById = uploadByTestId.length === 0 ? node.matches(".ant-upload[id]") ? [node] : Array.from(node.querySelectorAll(".ant-upload[id]")) : [];
|
|
393
|
+
for (const upload of [...uploadByTestId, ...uploadById]) {
|
|
394
|
+
if (!(upload instanceof HTMLElement)) continue;
|
|
395
|
+
const testId = upload.getAttribute(attrName) || upload.getAttribute("id") || "";
|
|
396
|
+
if (!testId) continue;
|
|
397
|
+
const input = upload.querySelector('input[type="file"]');
|
|
398
|
+
if (input && !input.hasAttribute(attrName)) {
|
|
399
|
+
input.setAttribute(attrName, `${testId}-input`);
|
|
400
|
+
if (!upload.hasAttribute(attrName)) {
|
|
401
|
+
upload.setAttribute(attrName, testId);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
255
404
|
}
|
|
256
|
-
const items = root.querySelectorAll(rule.selector);
|
|
257
|
-
items.forEach((item) => {
|
|
258
|
-
injectSingle(item, rule);
|
|
259
|
-
});
|
|
260
405
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
406
|
+
let pending = false;
|
|
407
|
+
let pendingNodes = [];
|
|
408
|
+
const flushPending = () => {
|
|
409
|
+
pending = false;
|
|
410
|
+
const nodes = pendingNodes;
|
|
411
|
+
pendingNodes = [];
|
|
412
|
+
for (const node of nodes) {
|
|
413
|
+
processNode(node);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
265
416
|
const observer = new MutationObserver((mutations) => {
|
|
266
417
|
for (const mutation of mutations) {
|
|
267
418
|
for (const node of mutation.addedNodes) {
|
|
268
|
-
|
|
269
|
-
for (const [selector, strategy] of Object.entries(panels)) {
|
|
270
|
-
const containers = node.matches(selector) ? [node] : Array.from(node.querySelectorAll(selector));
|
|
271
|
-
for (const container of containers) {
|
|
272
|
-
if (!(container instanceof HTMLElement)) continue;
|
|
273
|
-
if (container.hasAttribute(attrName)) continue;
|
|
274
|
-
if (strategy) {
|
|
275
|
-
queueMicrotask(() => {
|
|
276
|
-
const trigger = findActiveTrigger(attrName);
|
|
277
|
-
const triggerTestId = getTriggerTestId(trigger, attrName);
|
|
278
|
-
const ctx = {
|
|
279
|
-
container,
|
|
280
|
-
trigger,
|
|
281
|
-
triggerTestId,
|
|
282
|
-
attrName
|
|
283
|
-
};
|
|
284
|
-
strategy(ctx);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
const uploadByTestId = node.matches(`.ant-upload[${attrName}]`) ? [node] : Array.from(node.querySelectorAll(`.ant-upload[${attrName}]`));
|
|
290
|
-
const uploadById = uploadByTestId.length === 0 ? node.matches(`.ant-upload[id]`) ? [node] : Array.from(node.querySelectorAll(`.ant-upload[id]`)) : [];
|
|
291
|
-
const uploads = [...uploadByTestId, ...uploadById];
|
|
292
|
-
for (const upload of uploads) {
|
|
293
|
-
if (!(upload instanceof HTMLElement)) continue;
|
|
294
|
-
const testId = upload.getAttribute(attrName) || upload.getAttribute("id") || "";
|
|
295
|
-
if (!testId) continue;
|
|
296
|
-
const input = upload.querySelector('input[type="file"]');
|
|
297
|
-
if (input && !input.hasAttribute(attrName)) {
|
|
298
|
-
input.setAttribute(attrName, `${testId}-input`);
|
|
299
|
-
if (!upload.hasAttribute(attrName)) {
|
|
300
|
-
upload.setAttribute(attrName, testId);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
injectMenuItems(attrName, node instanceof HTMLElement ? node : document);
|
|
419
|
+
pendingNodes.push(node);
|
|
305
420
|
}
|
|
306
421
|
}
|
|
422
|
+
if (!pending) {
|
|
423
|
+
pending = true;
|
|
424
|
+
requestAnimationFrame(flushPending);
|
|
425
|
+
}
|
|
307
426
|
});
|
|
308
427
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
428
|
+
processNode(document.body);
|
|
429
|
+
return () => {
|
|
430
|
+
observer.disconnect();
|
|
431
|
+
pendingNodes = [];
|
|
432
|
+
};
|
|
313
433
|
}
|
|
434
|
+
var setupAntDropdownTestIds = setupAntTestIds;
|
|
314
435
|
function injectCurrentDropdowns(opts = {}) {
|
|
315
436
|
const attrName = opts.attributeName ?? "data-testid";
|
|
316
437
|
const panels = { ...DEFAULT_PANELS, ...opts.panels };
|
|
438
|
+
const counter = { value: 0 };
|
|
439
|
+
let matchers = DEFAULT_COMPONENT_MATCHERS;
|
|
440
|
+
if (opts.components) {
|
|
441
|
+
const customKeys = new Set(Object.keys(opts.components));
|
|
442
|
+
matchers = matchers.filter((m) => !customKeys.has(m.selector));
|
|
443
|
+
for (const [selector, prefix] of Object.entries(opts.components)) {
|
|
444
|
+
matchers.push({ selector, prefix });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
injectComponentRoots(document.body, matchers, attrName, counter);
|
|
317
448
|
for (const [selector, strategy] of Object.entries(panels)) {
|
|
318
449
|
const containers = document.querySelectorAll(selector);
|
|
319
450
|
containers.forEach((container) => {
|
|
@@ -325,9 +456,53 @@ function injectCurrentDropdowns(opts = {}) {
|
|
|
325
456
|
strategy({ container, trigger, triggerTestId, attrName });
|
|
326
457
|
});
|
|
327
458
|
}
|
|
459
|
+
const inputNumbers = document.querySelectorAll(`.ant-input-number[${attrName}]`);
|
|
460
|
+
inputNumbers.forEach((inputNumber) => {
|
|
461
|
+
if (!(inputNumber instanceof HTMLElement)) return;
|
|
462
|
+
const testId = inputNumber.getAttribute(attrName) || "";
|
|
463
|
+
if (!testId) return;
|
|
464
|
+
const input = inputNumber.querySelector(".ant-input-number-input");
|
|
465
|
+
if (input && !input.hasAttribute(attrName)) {
|
|
466
|
+
input.setAttribute(attrName, testId);
|
|
467
|
+
}
|
|
468
|
+
const upHandler = inputNumber.querySelector(".ant-input-number-handler-up");
|
|
469
|
+
if (upHandler && !upHandler.hasAttribute(attrName)) {
|
|
470
|
+
upHandler.setAttribute(attrName, `${testId}-up`);
|
|
471
|
+
}
|
|
472
|
+
const downHandler = inputNumber.querySelector(".ant-input-number-handler-down");
|
|
473
|
+
if (downHandler && !downHandler.hasAttribute(attrName)) {
|
|
474
|
+
downHandler.setAttribute(attrName, `${testId}-down`);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
const sliders = document.querySelectorAll(`.ant-slider[${attrName}]`);
|
|
478
|
+
sliders.forEach((slider) => {
|
|
479
|
+
if (!(slider instanceof HTMLElement)) return;
|
|
480
|
+
const testId = slider.getAttribute(attrName) || "";
|
|
481
|
+
if (!testId) return;
|
|
482
|
+
const handles = slider.querySelectorAll(".ant-slider-handle");
|
|
483
|
+
handles.forEach((h, i) => {
|
|
484
|
+
const el = h;
|
|
485
|
+
if (!el.hasAttribute(attrName)) {
|
|
486
|
+
el.setAttribute(attrName, `${testId}-handle-${i}`);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
const rates = document.querySelectorAll(`.ant-rate[${attrName}]`);
|
|
491
|
+
rates.forEach((rate) => {
|
|
492
|
+
if (!(rate instanceof HTMLElement)) return;
|
|
493
|
+
const testId = rate.getAttribute(attrName) || "";
|
|
494
|
+
if (!testId) return;
|
|
495
|
+
const stars = rate.querySelectorAll(".ant-rate-star");
|
|
496
|
+
stars.forEach((s, i) => {
|
|
497
|
+
const el = s;
|
|
498
|
+
if (!el.hasAttribute(attrName)) {
|
|
499
|
+
el.setAttribute(attrName, `${testId}-star-${i}`);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
});
|
|
328
503
|
const uploads = [
|
|
329
504
|
...Array.from(document.querySelectorAll(`.ant-upload[${attrName}]`)),
|
|
330
|
-
...Array.from(document.querySelectorAll(
|
|
505
|
+
...Array.from(document.querySelectorAll(".ant-upload[id]"))
|
|
331
506
|
];
|
|
332
507
|
const seen = /* @__PURE__ */ new Set();
|
|
333
508
|
uploads.forEach((upload) => {
|
|
@@ -348,5 +523,6 @@ function injectCurrentDropdowns(opts = {}) {
|
|
|
348
523
|
}
|
|
349
524
|
export {
|
|
350
525
|
injectCurrentDropdowns,
|
|
351
|
-
setupAntDropdownTestIds
|
|
526
|
+
setupAntDropdownTestIds,
|
|
527
|
+
setupAntTestIds
|
|
352
528
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-vue-testid",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
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",
|
|
@@ -42,12 +42,9 @@
|
|
|
42
42
|
"vite": ">=5.0.0",
|
|
43
43
|
"vue": ">=3.3.0"
|
|
44
44
|
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"@vue/compiler-core": "^3.5.0"
|
|
47
|
-
},
|
|
48
45
|
"devDependencies": {
|
|
49
46
|
"tsup": "^8.5.1",
|
|
50
47
|
"typescript": "~6.0.2",
|
|
51
48
|
"vite": "^8.0.12"
|
|
52
49
|
}
|
|
53
|
-
}
|
|
50
|
+
}
|