@testid/antd-testid-runtime 1.0.8 → 1.0.10

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.mts CHANGED
@@ -1,3 +1,52 @@
1
+ /**
2
+ * UI 库适配器接口 — adapters/types.ts
3
+ *
4
+ * 将 UI 库相关的 CSS class 命名、标签前缀、交互元素识别等逻辑
5
+ * 从核心 Observer 中解耦,实现可插拔的 UI 库支持。
6
+ *
7
+ * 每个适配器描述一个 UI 库的浮层组件识别规则,
8
+ * 多个适配器可组合使用 (如同时使用 Ant Design Vue + Element UI)。
9
+ */
10
+ /**
11
+ * 支持的浮层类型
12
+ */
13
+ type PopupType = 'modal' | 'drawer' | 'select' | 'datePicker' | 'popconfirm' | 'dropdown' | 'tooltip' | 'message';
14
+ /**
15
+ * UI 库适配器接口
16
+ *
17
+ * 实现此接口即可让 Observer 自动识别该 UI 库的浮层组件。
18
+ */
19
+ interface UiAdapter {
20
+ /** 适配器名称 (调试用) */
21
+ readonly name: string;
22
+ /** CSS 类名前缀数组 (如 AntD: ['ant'], Element: ['el']) */
23
+ readonly cssPrefixes: string[];
24
+ /**
25
+ * 浮层 CSS class 后缀映射
26
+ *
27
+ * 结构: PopupType → string[][],两层数组含义:
28
+ * - 外层: OR 关系,任一内层命中即匹配
29
+ * - 内层: AND 关系,所有 class 必须同时出现
30
+ *
31
+ * 后缀不含前缀,由 buildPopupClassMap 动态拼接。
32
+ */
33
+ readonly popupClassSuffixMap: Record<PopupType, string[][]>;
34
+ /**
35
+ * 交互标签名列表 (用于 onlyInteractive 模式)
36
+ *
37
+ * 包含 UI 库的组件标签名 (如 'a-button', 'el-input') 和原生标签 (如 'button', 'input')。
38
+ * Observer 会合并所有适配器的 interactiveTags 进行判断。
39
+ */
40
+ readonly interactiveTags: string[];
41
+ /**
42
+ * 标签前缀正则 (用于 getSimpleTag 去除 UI 库前缀)
43
+ *
44
+ * 例: AntD → /^a-/, Element → /^el-/
45
+ * 多个适配器的正则用 | 合并。
46
+ */
47
+ readonly tagPrefixPattern: RegExp;
48
+ }
49
+
1
50
  /**
2
51
  * 全局配置模块 — testMark.ts
3
52
  *
@@ -6,11 +55,9 @@
6
55
  * - 黑白名单
7
56
  * - 开关
8
57
  * - 浮层前缀映射
58
+ * - UI 库适配器
9
59
  */
10
- /**
11
- * 支持的浮层类型
12
- */
13
- type PopupType = 'modal' | 'drawer' | 'select' | 'datePicker' | 'popconfirm' | 'dropdown' | 'tooltip' | 'message';
60
+
14
61
  /**
15
62
  * 全量配置接口
16
63
  */
@@ -28,17 +75,27 @@ interface TestIdMarkConfig {
28
75
  compilePrefix: string;
29
76
  /** 页面内动态节点统一前缀 (默认 "dynamic_") */
30
77
  runtimePagePrefix: string;
31
- /** Antd 浮层组件专属前缀映射 */
78
+ /** 浮层组件专属前缀映射 */
32
79
  popupPrefixMap: Record<PopupType, string>;
33
80
  /**
34
- * Ant Design Vue CSS 类名前缀 (对应 ConfigProvider 的 prefixCls)
81
+ * UI 库适配器列表 (默认为 [antdAdapter])
82
+ *
83
+ * 每个适配器定义了一个 UI 库的浮层 CSS class 识别规则、
84
+ * 交互标签列表和标签前缀。Observer 自动合并所有适配器的规则。
35
85
  *
36
- * 支持数组,可同时匹配多个前缀体系:
37
- * 默认: ['ant'] → 匹配 .ant-modal, .ant-picker-dropdown 等
38
- * 若项目使用 <a-config-provider prefixCls="my-ui"> 则设为 ['my-ui']
39
- * 混合使用: ['ant', 'my-ui'] → 同时匹配两个体系的所有浮层组件
86
+ * 仅使用 Ant Design Vue:
87
+ * adapters: [antdAdapter]
88
+ *
89
+ * 仅使用 Element UI:
90
+ * adapters: [elementAdapter]
91
+ *
92
+ * 同时使用两种 UI 库:
93
+ * adapters: [antdAdapter, elementAdapter]
94
+ *
95
+ * 自定义 Ant Design Vue CSS 前缀 (如 <a-config-provider prefixCls="my-ui">):
96
+ * adapters: [{ ...antdAdapter, cssPrefixes: ['my-ui'] }]
40
97
  */
41
- antdClassPrefix: string[];
98
+ adapters: UiAdapter[];
42
99
  /** 忽略不打标的 HTML 标签名 */
43
100
  ignoreTags: string[];
44
101
  /** 包含此 class 的 DOM 跳过打标 */
@@ -50,10 +107,6 @@ interface TestIdMarkConfig {
50
107
  /** 路由切换时是否重置全部浮层计数器 */
51
108
  resetPopupCounterOnRouteChange: boolean;
52
109
  }
53
- /**
54
- * 交互控件白名单 (onlyInteractive === true 时生效)
55
- */
56
- declare const INTERACTIVE_TAGS: Set<string>;
57
110
  /**
58
111
  * 默认配置对象 (enable 默认为 true,由调用方传入 DEV 判断)
59
112
  */
@@ -74,6 +127,34 @@ declare function initConfig(custom?: Partial<TestIdMarkConfig>): void;
74
127
  */
75
128
  declare function getConfig(): Readonly<TestIdMarkConfig>;
76
129
 
130
+ /**
131
+ * Ant Design Vue 适配器 — adapters/antd.ts
132
+ *
133
+ * 识别 Ant Design Vue 的浮层组件:
134
+ * - ant-modal, ant-drawer, ant-select-dropdown, ant-picker-dropdown,
135
+ * ant-popover+ant-popconfirm, ant-dropdown, ant-tooltip, ant-message
136
+ *
137
+ * 支持多版本兼容:
138
+ * - Ant Design Vue 4.x: ant-picker-dropdown
139
+ * - Ant Design Vue 1.x: ant-calendar-picker-container
140
+ */
141
+
142
+ declare const antdAdapter: UiAdapter;
143
+
144
+ /**
145
+ * Element UI 适配器 — adapters/element.ts
146
+ *
147
+ * 识别 Element UI / Element Plus 的浮层组件:
148
+ * - el-dialog, el-drawer, el-select-dropdown, el-picker-panel,
149
+ * el-message-box, el-popconfirm, el-dropdown-menu,
150
+ * el-tooltip__popper, el-message
151
+ *
152
+ * 注意: el-popper 被多种组件共用 (select/dropdown/tooltip 等),
153
+ * 不单独作为识别依据,避免误匹配。
154
+ */
155
+
156
+ declare const elementAdapter: UiAdapter;
157
+
77
158
  /**
78
159
  * 锚点局部计数器 — testIdAnchorCounter.ts
79
160
  *
@@ -205,8 +286,12 @@ declare function buildPopupTestId(type: PopupType, tag: string, counterId: numbe
205
286
  */
206
287
  declare class TestIdObserver {
207
288
  private state;
208
- /** 浮层 class 匹配映射 (根据 antdClassPrefix 动态构建) */
289
+ /** 浮层 class 匹配映射 ( adapters 动态构建) */
209
290
  private popupClassMap;
291
+ /** 合并后的交互标签列表 (来自所有适配器) */
292
+ private interactiveTags;
293
+ /** 合并后的标签前缀正则 (用于 getSimpleTag) */
294
+ private tagPrefixPattern;
210
295
  constructor();
211
296
  /**
212
297
  * 启动 MutationObserver
@@ -330,17 +415,19 @@ declare class TestIdObserver {
330
415
  /**
331
416
  * 获取元素的简化标签名
332
417
  *
333
- * 处理 Antd 组件前缀: a-button → button, a-input → input
418
+ * 去除所有适配器注册的 UI 库标签前缀:
419
+ * AntD: a-button → button, a-input → input
420
+ * Element: el-button → button, el-input → input
334
421
  */
335
422
  private getSimpleTag;
336
423
  /**
337
424
  * 判断是否可交互元素
338
425
  *
339
426
  * 可交互特征:
340
- * - 交互类标签: button, input, select, textarea
427
+ * - 匹配任意适配器的交互标签 (含原生 + UI 库前缀)
341
428
  * - onclick 属性
342
- * - role="button" / role="checkbox" / role="radio"
343
- * - cursor:pointer (不检测,因为可能从 CSS 继承,误判率高)
429
+ * - role="button" / role="checkbox" / role="radio" / role="switch"
430
+ * - tabindex 属性
344
431
  */
345
432
  private isInteractive;
346
433
  /**
@@ -397,4 +484,4 @@ declare class TestIdChecker {
397
484
  private static reportGroupDuplicates;
398
485
  }
399
486
 
400
- export { INTERACTIVE_TAGS, type ParsedBaseKey, type PopupType, TestIdChecker, type TestIdMarkConfig, TestIdObserver, buildAnchorTestId, buildPopupTestId, defaultConfig, getAnchorCounterMap, getConfig, getNextAnchorLocalIndex, getNextPopupId, getPopupCounterSnapshot, initConfig, mergeConfig, parseBaseKey, resetAllAnchorCounters, resetAllPopupCounters, resetPopupCounter };
487
+ export { type ParsedBaseKey, type PopupType, TestIdChecker, type TestIdMarkConfig, TestIdObserver, type UiAdapter, antdAdapter, buildAnchorTestId, buildPopupTestId, defaultConfig, elementAdapter, getAnchorCounterMap, getConfig, getNextAnchorLocalIndex, getNextPopupId, getPopupCounterSnapshot, initConfig, mergeConfig, parseBaseKey, resetAllAnchorCounters, resetAllPopupCounters, resetPopupCounter };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,52 @@
1
+ /**
2
+ * UI 库适配器接口 — adapters/types.ts
3
+ *
4
+ * 将 UI 库相关的 CSS class 命名、标签前缀、交互元素识别等逻辑
5
+ * 从核心 Observer 中解耦,实现可插拔的 UI 库支持。
6
+ *
7
+ * 每个适配器描述一个 UI 库的浮层组件识别规则,
8
+ * 多个适配器可组合使用 (如同时使用 Ant Design Vue + Element UI)。
9
+ */
10
+ /**
11
+ * 支持的浮层类型
12
+ */
13
+ type PopupType = 'modal' | 'drawer' | 'select' | 'datePicker' | 'popconfirm' | 'dropdown' | 'tooltip' | 'message';
14
+ /**
15
+ * UI 库适配器接口
16
+ *
17
+ * 实现此接口即可让 Observer 自动识别该 UI 库的浮层组件。
18
+ */
19
+ interface UiAdapter {
20
+ /** 适配器名称 (调试用) */
21
+ readonly name: string;
22
+ /** CSS 类名前缀数组 (如 AntD: ['ant'], Element: ['el']) */
23
+ readonly cssPrefixes: string[];
24
+ /**
25
+ * 浮层 CSS class 后缀映射
26
+ *
27
+ * 结构: PopupType → string[][],两层数组含义:
28
+ * - 外层: OR 关系,任一内层命中即匹配
29
+ * - 内层: AND 关系,所有 class 必须同时出现
30
+ *
31
+ * 后缀不含前缀,由 buildPopupClassMap 动态拼接。
32
+ */
33
+ readonly popupClassSuffixMap: Record<PopupType, string[][]>;
34
+ /**
35
+ * 交互标签名列表 (用于 onlyInteractive 模式)
36
+ *
37
+ * 包含 UI 库的组件标签名 (如 'a-button', 'el-input') 和原生标签 (如 'button', 'input')。
38
+ * Observer 会合并所有适配器的 interactiveTags 进行判断。
39
+ */
40
+ readonly interactiveTags: string[];
41
+ /**
42
+ * 标签前缀正则 (用于 getSimpleTag 去除 UI 库前缀)
43
+ *
44
+ * 例: AntD → /^a-/, Element → /^el-/
45
+ * 多个适配器的正则用 | 合并。
46
+ */
47
+ readonly tagPrefixPattern: RegExp;
48
+ }
49
+
1
50
  /**
2
51
  * 全局配置模块 — testMark.ts
3
52
  *
@@ -6,11 +55,9 @@
6
55
  * - 黑白名单
7
56
  * - 开关
8
57
  * - 浮层前缀映射
58
+ * - UI 库适配器
9
59
  */
10
- /**
11
- * 支持的浮层类型
12
- */
13
- type PopupType = 'modal' | 'drawer' | 'select' | 'datePicker' | 'popconfirm' | 'dropdown' | 'tooltip' | 'message';
60
+
14
61
  /**
15
62
  * 全量配置接口
16
63
  */
@@ -28,17 +75,27 @@ interface TestIdMarkConfig {
28
75
  compilePrefix: string;
29
76
  /** 页面内动态节点统一前缀 (默认 "dynamic_") */
30
77
  runtimePagePrefix: string;
31
- /** Antd 浮层组件专属前缀映射 */
78
+ /** 浮层组件专属前缀映射 */
32
79
  popupPrefixMap: Record<PopupType, string>;
33
80
  /**
34
- * Ant Design Vue CSS 类名前缀 (对应 ConfigProvider 的 prefixCls)
81
+ * UI 库适配器列表 (默认为 [antdAdapter])
82
+ *
83
+ * 每个适配器定义了一个 UI 库的浮层 CSS class 识别规则、
84
+ * 交互标签列表和标签前缀。Observer 自动合并所有适配器的规则。
35
85
  *
36
- * 支持数组,可同时匹配多个前缀体系:
37
- * 默认: ['ant'] → 匹配 .ant-modal, .ant-picker-dropdown 等
38
- * 若项目使用 <a-config-provider prefixCls="my-ui"> 则设为 ['my-ui']
39
- * 混合使用: ['ant', 'my-ui'] → 同时匹配两个体系的所有浮层组件
86
+ * 仅使用 Ant Design Vue:
87
+ * adapters: [antdAdapter]
88
+ *
89
+ * 仅使用 Element UI:
90
+ * adapters: [elementAdapter]
91
+ *
92
+ * 同时使用两种 UI 库:
93
+ * adapters: [antdAdapter, elementAdapter]
94
+ *
95
+ * 自定义 Ant Design Vue CSS 前缀 (如 <a-config-provider prefixCls="my-ui">):
96
+ * adapters: [{ ...antdAdapter, cssPrefixes: ['my-ui'] }]
40
97
  */
41
- antdClassPrefix: string[];
98
+ adapters: UiAdapter[];
42
99
  /** 忽略不打标的 HTML 标签名 */
43
100
  ignoreTags: string[];
44
101
  /** 包含此 class 的 DOM 跳过打标 */
@@ -50,10 +107,6 @@ interface TestIdMarkConfig {
50
107
  /** 路由切换时是否重置全部浮层计数器 */
51
108
  resetPopupCounterOnRouteChange: boolean;
52
109
  }
53
- /**
54
- * 交互控件白名单 (onlyInteractive === true 时生效)
55
- */
56
- declare const INTERACTIVE_TAGS: Set<string>;
57
110
  /**
58
111
  * 默认配置对象 (enable 默认为 true,由调用方传入 DEV 判断)
59
112
  */
@@ -74,6 +127,34 @@ declare function initConfig(custom?: Partial<TestIdMarkConfig>): void;
74
127
  */
75
128
  declare function getConfig(): Readonly<TestIdMarkConfig>;
76
129
 
130
+ /**
131
+ * Ant Design Vue 适配器 — adapters/antd.ts
132
+ *
133
+ * 识别 Ant Design Vue 的浮层组件:
134
+ * - ant-modal, ant-drawer, ant-select-dropdown, ant-picker-dropdown,
135
+ * ant-popover+ant-popconfirm, ant-dropdown, ant-tooltip, ant-message
136
+ *
137
+ * 支持多版本兼容:
138
+ * - Ant Design Vue 4.x: ant-picker-dropdown
139
+ * - Ant Design Vue 1.x: ant-calendar-picker-container
140
+ */
141
+
142
+ declare const antdAdapter: UiAdapter;
143
+
144
+ /**
145
+ * Element UI 适配器 — adapters/element.ts
146
+ *
147
+ * 识别 Element UI / Element Plus 的浮层组件:
148
+ * - el-dialog, el-drawer, el-select-dropdown, el-picker-panel,
149
+ * el-message-box, el-popconfirm, el-dropdown-menu,
150
+ * el-tooltip__popper, el-message
151
+ *
152
+ * 注意: el-popper 被多种组件共用 (select/dropdown/tooltip 等),
153
+ * 不单独作为识别依据,避免误匹配。
154
+ */
155
+
156
+ declare const elementAdapter: UiAdapter;
157
+
77
158
  /**
78
159
  * 锚点局部计数器 — testIdAnchorCounter.ts
79
160
  *
@@ -205,8 +286,12 @@ declare function buildPopupTestId(type: PopupType, tag: string, counterId: numbe
205
286
  */
206
287
  declare class TestIdObserver {
207
288
  private state;
208
- /** 浮层 class 匹配映射 (根据 antdClassPrefix 动态构建) */
289
+ /** 浮层 class 匹配映射 ( adapters 动态构建) */
209
290
  private popupClassMap;
291
+ /** 合并后的交互标签列表 (来自所有适配器) */
292
+ private interactiveTags;
293
+ /** 合并后的标签前缀正则 (用于 getSimpleTag) */
294
+ private tagPrefixPattern;
210
295
  constructor();
211
296
  /**
212
297
  * 启动 MutationObserver
@@ -330,17 +415,19 @@ declare class TestIdObserver {
330
415
  /**
331
416
  * 获取元素的简化标签名
332
417
  *
333
- * 处理 Antd 组件前缀: a-button → button, a-input → input
418
+ * 去除所有适配器注册的 UI 库标签前缀:
419
+ * AntD: a-button → button, a-input → input
420
+ * Element: el-button → button, el-input → input
334
421
  */
335
422
  private getSimpleTag;
336
423
  /**
337
424
  * 判断是否可交互元素
338
425
  *
339
426
  * 可交互特征:
340
- * - 交互类标签: button, input, select, textarea
427
+ * - 匹配任意适配器的交互标签 (含原生 + UI 库前缀)
341
428
  * - onclick 属性
342
- * - role="button" / role="checkbox" / role="radio"
343
- * - cursor:pointer (不检测,因为可能从 CSS 继承,误判率高)
429
+ * - role="button" / role="checkbox" / role="radio" / role="switch"
430
+ * - tabindex 属性
344
431
  */
345
432
  private isInteractive;
346
433
  /**
@@ -397,4 +484,4 @@ declare class TestIdChecker {
397
484
  private static reportGroupDuplicates;
398
485
  }
399
486
 
400
- export { INTERACTIVE_TAGS, type ParsedBaseKey, type PopupType, TestIdChecker, type TestIdMarkConfig, TestIdObserver, buildAnchorTestId, buildPopupTestId, defaultConfig, getAnchorCounterMap, getConfig, getNextAnchorLocalIndex, getNextPopupId, getPopupCounterSnapshot, initConfig, mergeConfig, parseBaseKey, resetAllAnchorCounters, resetAllPopupCounters, resetPopupCounter };
487
+ export { type ParsedBaseKey, type PopupType, TestIdChecker, type TestIdMarkConfig, TestIdObserver, type UiAdapter, antdAdapter, buildAnchorTestId, buildPopupTestId, defaultConfig, elementAdapter, getAnchorCounterMap, getConfig, getNextAnchorLocalIndex, getNextPopupId, getPopupCounterSnapshot, initConfig, mergeConfig, parseBaseKey, resetAllAnchorCounters, resetAllPopupCounters, resetPopupCounter };
package/dist/index.js CHANGED
@@ -20,12 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- INTERACTIVE_TAGS: () => INTERACTIVE_TAGS,
24
23
  TestIdChecker: () => TestIdChecker,
25
24
  TestIdObserver: () => TestIdObserver,
25
+ antdAdapter: () => antdAdapter,
26
26
  buildAnchorTestId: () => buildAnchorTestId,
27
27
  buildPopupTestId: () => buildPopupTestId,
28
28
  defaultConfig: () => defaultConfig,
29
+ elementAdapter: () => elementAdapter,
29
30
  getAnchorCounterMap: () => getAnchorCounterMap,
30
31
  getConfig: () => getConfig,
31
32
  getNextAnchorLocalIndex: () => getNextAnchorLocalIndex,
@@ -40,27 +41,50 @@ __export(index_exports, {
40
41
  });
41
42
  module.exports = __toCommonJS(index_exports);
42
43
 
44
+ // src/adapters/antd.ts
45
+ var popupClassSuffixMap = {
46
+ modal: [["-modal"]],
47
+ drawer: [["-drawer"]],
48
+ select: [["-select-dropdown"]],
49
+ datePicker: [
50
+ ["-picker-dropdown"],
51
+ // Ant Design Vue 4.x
52
+ ["-calendar-picker-container"]
53
+ // Ant Design Vue 1.x
54
+ ],
55
+ popconfirm: [["-popover", "-popconfirm"]],
56
+ dropdown: [["-dropdown"]],
57
+ tooltip: [["-tooltip"]],
58
+ message: [["-message"]]
59
+ };
60
+ var antdAdapter = {
61
+ name: "ant-design-vue",
62
+ cssPrefixes: ["ant"],
63
+ popupClassSuffixMap,
64
+ interactiveTags: [
65
+ "button",
66
+ "input",
67
+ "select",
68
+ "textarea",
69
+ "a-button",
70
+ "a-input",
71
+ "a-input-number",
72
+ "a-select",
73
+ "a-textarea",
74
+ "a-checkbox",
75
+ "a-radio",
76
+ "a-switch"
77
+ ],
78
+ tagPrefixPattern: /^a-/
79
+ };
80
+
43
81
  // src/config/testMark.ts
44
- var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
45
- "button",
46
- "a-button",
47
- "input",
48
- "a-input",
49
- "a-input-number",
50
- "select",
51
- "a-select",
52
- "textarea",
53
- "a-textarea",
54
- "a-checkbox",
55
- "a-radio",
56
- "a-switch"
57
- ]);
58
82
  var defaultConfig = {
59
83
  enable: true,
60
84
  globalPrefix: "",
61
85
  compilePrefix: "static_",
62
86
  runtimePagePrefix: "dynamic_",
63
- antdClassPrefix: ["ant"],
87
+ adapters: [antdAdapter],
64
88
  popupPrefixMap: {
65
89
  modal: "modal_",
66
90
  drawer: "drawer_",
@@ -116,6 +140,58 @@ function getConfig() {
116
140
  return globalConfig;
117
141
  }
118
142
 
143
+ // src/adapters/element.ts
144
+ var popupClassSuffixMap2 = {
145
+ modal: [["-dialog"]],
146
+ drawer: [["-drawer"]],
147
+ select: [
148
+ ["-select-dropdown"],
149
+ // ElSelect 下拉面板
150
+ ["-cascader__suggestion-panel"]
151
+ // ElCascader 级联选择器浮层面板
152
+ ],
153
+ datePicker: [
154
+ ["-picker-panel"],
155
+ // 日期/时间选择器面板
156
+ ["-date-range-picker__content"]
157
+ // 日期范围选择器内容区
158
+ ],
159
+ popconfirm: [
160
+ ["-message-box"],
161
+ // Element MessageBox
162
+ ["-popconfirm"]
163
+ // Element Popconfirm
164
+ ],
165
+ dropdown: [["-dropdown-menu"]],
166
+ tooltip: [
167
+ ["-tooltip__popper"],
168
+ // Element Tooltip 浮层
169
+ ["-popover"]
170
+ // Element Popover 浮层 (语义上接近 tooltip)
171
+ ],
172
+ message: [["-message"]]
173
+ };
174
+ var elementAdapter = {
175
+ name: "element-ui",
176
+ cssPrefixes: ["el"],
177
+ popupClassSuffixMap: popupClassSuffixMap2,
178
+ interactiveTags: [
179
+ "button",
180
+ "input",
181
+ "select",
182
+ "textarea",
183
+ "el-button",
184
+ "el-input",
185
+ "el-input-number",
186
+ "el-select",
187
+ "el-textarea",
188
+ "el-checkbox",
189
+ "el-radio",
190
+ "el-switch"
191
+ ],
192
+ tagPrefixPattern: /^el-/
193
+ };
194
+
119
195
  // src/utils/testIdAnchorCounter.ts
120
196
  var anchorCounterMap = /* @__PURE__ */ new Map();
121
197
  function buildAnchorKey(anchorTestId, componentName, tagName) {
@@ -184,33 +260,46 @@ function buildPopupTestId(type, tag, counterId) {
184
260
  return `${config.runtimePagePrefix}${popupPrefix}${tag}_${counterId}`;
185
261
  }
186
262
 
263
+ // src/adapters/types.ts
264
+ function mergeInteractiveTags(adapters) {
265
+ const set = /* @__PURE__ */ new Set();
266
+ for (const a of adapters) {
267
+ for (const tag of a.interactiveTags) {
268
+ set.add(tag);
269
+ }
270
+ }
271
+ return [...set];
272
+ }
273
+ function mergeTagPrefixPattern(adapters) {
274
+ const patterns = adapters.map((a) => a.tagPrefixPattern.source).filter(Boolean);
275
+ if (patterns.length === 0) return null;
276
+ return new RegExp(patterns.join("|"), "gi");
277
+ }
278
+
187
279
  // src/utils/testIdObserver.ts
188
- var POPUP_CLASS_SUFFIX_MAP = {
189
- modal: [["-modal"]],
190
- drawer: [["-drawer"]],
191
- select: [["-select-dropdown"]],
192
- datePicker: [
193
- ["-picker-dropdown"],
194
- // Ant Design Vue 4.x (新)
195
- ["-calendar-picker-container"]
196
- // Ant Design Vue 1.x (旧)
197
- ],
198
- popconfirm: [["-popover", "-popconfirm"]],
199
- dropdown: [["-dropdown"]],
200
- tooltip: [["-tooltip"]],
201
- message: [["-message"]]
202
- };
203
- function buildPopupClassMap(prefixes) {
280
+ function buildPopupClassMap(adapters) {
204
281
  const result = {};
205
- const entries = Object.entries(POPUP_CLASS_SUFFIX_MAP);
206
- for (const [type, suffixGroups] of entries) {
207
- const selectorSets = [];
208
- for (const suffixGroup of suffixGroups) {
209
- for (const prefix of prefixes) {
210
- selectorSets.push(suffixGroup.map((suffix) => `${prefix}${suffix}`));
282
+ const seen = /* @__PURE__ */ new Map();
283
+ for (const adapter of adapters) {
284
+ const entries = Object.entries(adapter.popupClassSuffixMap);
285
+ for (const [type, suffixGroups] of entries) {
286
+ if (!seen.has(type)) {
287
+ seen.set(type, /* @__PURE__ */ new Set());
288
+ result[type] = [];
289
+ }
290
+ const typeSeen = seen.get(type);
291
+ const selectorSets = result[type];
292
+ for (const suffixGroup of suffixGroups) {
293
+ for (const prefix of adapter.cssPrefixes) {
294
+ const classCombo = suffixGroup.map((suffix) => `${prefix}${suffix}`);
295
+ const key = classCombo.join("|");
296
+ if (!typeSeen.has(key)) {
297
+ typeSeen.add(key);
298
+ selectorSets.push(classCombo);
299
+ }
300
+ }
211
301
  }
212
302
  }
213
- result[type] = selectorSets;
214
303
  }
215
304
  return result;
216
305
  }
@@ -233,7 +322,9 @@ var TestIdObserver = class {
233
322
  }
234
323
  };
235
324
  const config = getConfig();
236
- this.popupClassMap = buildPopupClassMap(config.antdClassPrefix);
325
+ this.popupClassMap = buildPopupClassMap(config.adapters);
326
+ this.interactiveTags = mergeInteractiveTags(config.adapters);
327
+ this.tagPrefixPattern = mergeTagPrefixPattern(config.adapters);
237
328
  this.state = {
238
329
  observer: null,
239
330
  isRunning: false,
@@ -534,34 +625,29 @@ var TestIdObserver = class {
534
625
  /**
535
626
  * 获取元素的简化标签名
536
627
  *
537
- * 处理 Antd 组件前缀: a-button → button, a-input → input
628
+ * 去除所有适配器注册的 UI 库标签前缀:
629
+ * AntD: a-button → button, a-input → input
630
+ * Element: el-button → button, el-input → input
538
631
  */
539
632
  getSimpleTag(node) {
540
- return node.tagName.toLowerCase().replace(/^a-/, "");
633
+ let tag = node.tagName.toLowerCase();
634
+ if (this.tagPrefixPattern) {
635
+ tag = tag.replace(this.tagPrefixPattern, "");
636
+ }
637
+ return tag;
541
638
  }
542
639
  /**
543
640
  * 判断是否可交互元素
544
641
  *
545
642
  * 可交互特征:
546
- * - 交互类标签: button, input, select, textarea
643
+ * - 匹配任意适配器的交互标签 (含原生 + UI 库前缀)
547
644
  * - onclick 属性
548
- * - role="button" / role="checkbox" / role="radio"
549
- * - cursor:pointer (不检测,因为可能从 CSS 继承,误判率高)
645
+ * - role="button" / role="checkbox" / role="radio" / role="switch"
646
+ * - tabindex 属性
550
647
  */
551
648
  isInteractive(node) {
552
649
  const tag = node.tagName.toLowerCase();
553
- const interactiveTags = [
554
- "button",
555
- "a-button",
556
- "input",
557
- "a-input",
558
- "a-input-number",
559
- "select",
560
- "a-select",
561
- "textarea",
562
- "a-textarea"
563
- ];
564
- if (interactiveTags.includes(tag)) return true;
650
+ if (this.interactiveTags.includes(tag)) return true;
565
651
  if (node.hasAttribute("onclick")) return true;
566
652
  const role = node.getAttribute("role");
567
653
  if (role === "button" || role === "checkbox" || role === "radio" || role === "switch") {
@@ -747,12 +833,13 @@ var TestIdChecker = class {
747
833
  };
748
834
  // Annotate the CommonJS export names for ESM import in node:
749
835
  0 && (module.exports = {
750
- INTERACTIVE_TAGS,
751
836
  TestIdChecker,
752
837
  TestIdObserver,
838
+ antdAdapter,
753
839
  buildAnchorTestId,
754
840
  buildPopupTestId,
755
841
  defaultConfig,
842
+ elementAdapter,
756
843
  getAnchorCounterMap,
757
844
  getConfig,
758
845
  getNextAnchorLocalIndex,
package/dist/index.mjs CHANGED
@@ -1,24 +1,47 @@
1
+ // src/adapters/antd.ts
2
+ var popupClassSuffixMap = {
3
+ modal: [["-modal"]],
4
+ drawer: [["-drawer"]],
5
+ select: [["-select-dropdown"]],
6
+ datePicker: [
7
+ ["-picker-dropdown"],
8
+ // Ant Design Vue 4.x
9
+ ["-calendar-picker-container"]
10
+ // Ant Design Vue 1.x
11
+ ],
12
+ popconfirm: [["-popover", "-popconfirm"]],
13
+ dropdown: [["-dropdown"]],
14
+ tooltip: [["-tooltip"]],
15
+ message: [["-message"]]
16
+ };
17
+ var antdAdapter = {
18
+ name: "ant-design-vue",
19
+ cssPrefixes: ["ant"],
20
+ popupClassSuffixMap,
21
+ interactiveTags: [
22
+ "button",
23
+ "input",
24
+ "select",
25
+ "textarea",
26
+ "a-button",
27
+ "a-input",
28
+ "a-input-number",
29
+ "a-select",
30
+ "a-textarea",
31
+ "a-checkbox",
32
+ "a-radio",
33
+ "a-switch"
34
+ ],
35
+ tagPrefixPattern: /^a-/
36
+ };
37
+
1
38
  // src/config/testMark.ts
2
- var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
3
- "button",
4
- "a-button",
5
- "input",
6
- "a-input",
7
- "a-input-number",
8
- "select",
9
- "a-select",
10
- "textarea",
11
- "a-textarea",
12
- "a-checkbox",
13
- "a-radio",
14
- "a-switch"
15
- ]);
16
39
  var defaultConfig = {
17
40
  enable: true,
18
41
  globalPrefix: "",
19
42
  compilePrefix: "static_",
20
43
  runtimePagePrefix: "dynamic_",
21
- antdClassPrefix: ["ant"],
44
+ adapters: [antdAdapter],
22
45
  popupPrefixMap: {
23
46
  modal: "modal_",
24
47
  drawer: "drawer_",
@@ -74,6 +97,58 @@ function getConfig() {
74
97
  return globalConfig;
75
98
  }
76
99
 
100
+ // src/adapters/element.ts
101
+ var popupClassSuffixMap2 = {
102
+ modal: [["-dialog"]],
103
+ drawer: [["-drawer"]],
104
+ select: [
105
+ ["-select-dropdown"],
106
+ // ElSelect 下拉面板
107
+ ["-cascader__suggestion-panel"]
108
+ // ElCascader 级联选择器浮层面板
109
+ ],
110
+ datePicker: [
111
+ ["-picker-panel"],
112
+ // 日期/时间选择器面板
113
+ ["-date-range-picker__content"]
114
+ // 日期范围选择器内容区
115
+ ],
116
+ popconfirm: [
117
+ ["-message-box"],
118
+ // Element MessageBox
119
+ ["-popconfirm"]
120
+ // Element Popconfirm
121
+ ],
122
+ dropdown: [["-dropdown-menu"]],
123
+ tooltip: [
124
+ ["-tooltip__popper"],
125
+ // Element Tooltip 浮层
126
+ ["-popover"]
127
+ // Element Popover 浮层 (语义上接近 tooltip)
128
+ ],
129
+ message: [["-message"]]
130
+ };
131
+ var elementAdapter = {
132
+ name: "element-ui",
133
+ cssPrefixes: ["el"],
134
+ popupClassSuffixMap: popupClassSuffixMap2,
135
+ interactiveTags: [
136
+ "button",
137
+ "input",
138
+ "select",
139
+ "textarea",
140
+ "el-button",
141
+ "el-input",
142
+ "el-input-number",
143
+ "el-select",
144
+ "el-textarea",
145
+ "el-checkbox",
146
+ "el-radio",
147
+ "el-switch"
148
+ ],
149
+ tagPrefixPattern: /^el-/
150
+ };
151
+
77
152
  // src/utils/testIdAnchorCounter.ts
78
153
  var anchorCounterMap = /* @__PURE__ */ new Map();
79
154
  function buildAnchorKey(anchorTestId, componentName, tagName) {
@@ -142,33 +217,46 @@ function buildPopupTestId(type, tag, counterId) {
142
217
  return `${config.runtimePagePrefix}${popupPrefix}${tag}_${counterId}`;
143
218
  }
144
219
 
220
+ // src/adapters/types.ts
221
+ function mergeInteractiveTags(adapters) {
222
+ const set = /* @__PURE__ */ new Set();
223
+ for (const a of adapters) {
224
+ for (const tag of a.interactiveTags) {
225
+ set.add(tag);
226
+ }
227
+ }
228
+ return [...set];
229
+ }
230
+ function mergeTagPrefixPattern(adapters) {
231
+ const patterns = adapters.map((a) => a.tagPrefixPattern.source).filter(Boolean);
232
+ if (patterns.length === 0) return null;
233
+ return new RegExp(patterns.join("|"), "gi");
234
+ }
235
+
145
236
  // src/utils/testIdObserver.ts
146
- var POPUP_CLASS_SUFFIX_MAP = {
147
- modal: [["-modal"]],
148
- drawer: [["-drawer"]],
149
- select: [["-select-dropdown"]],
150
- datePicker: [
151
- ["-picker-dropdown"],
152
- // Ant Design Vue 4.x (新)
153
- ["-calendar-picker-container"]
154
- // Ant Design Vue 1.x (旧)
155
- ],
156
- popconfirm: [["-popover", "-popconfirm"]],
157
- dropdown: [["-dropdown"]],
158
- tooltip: [["-tooltip"]],
159
- message: [["-message"]]
160
- };
161
- function buildPopupClassMap(prefixes) {
237
+ function buildPopupClassMap(adapters) {
162
238
  const result = {};
163
- const entries = Object.entries(POPUP_CLASS_SUFFIX_MAP);
164
- for (const [type, suffixGroups] of entries) {
165
- const selectorSets = [];
166
- for (const suffixGroup of suffixGroups) {
167
- for (const prefix of prefixes) {
168
- selectorSets.push(suffixGroup.map((suffix) => `${prefix}${suffix}`));
239
+ const seen = /* @__PURE__ */ new Map();
240
+ for (const adapter of adapters) {
241
+ const entries = Object.entries(adapter.popupClassSuffixMap);
242
+ for (const [type, suffixGroups] of entries) {
243
+ if (!seen.has(type)) {
244
+ seen.set(type, /* @__PURE__ */ new Set());
245
+ result[type] = [];
246
+ }
247
+ const typeSeen = seen.get(type);
248
+ const selectorSets = result[type];
249
+ for (const suffixGroup of suffixGroups) {
250
+ for (const prefix of adapter.cssPrefixes) {
251
+ const classCombo = suffixGroup.map((suffix) => `${prefix}${suffix}`);
252
+ const key = classCombo.join("|");
253
+ if (!typeSeen.has(key)) {
254
+ typeSeen.add(key);
255
+ selectorSets.push(classCombo);
256
+ }
257
+ }
169
258
  }
170
259
  }
171
- result[type] = selectorSets;
172
260
  }
173
261
  return result;
174
262
  }
@@ -191,7 +279,9 @@ var TestIdObserver = class {
191
279
  }
192
280
  };
193
281
  const config = getConfig();
194
- this.popupClassMap = buildPopupClassMap(config.antdClassPrefix);
282
+ this.popupClassMap = buildPopupClassMap(config.adapters);
283
+ this.interactiveTags = mergeInteractiveTags(config.adapters);
284
+ this.tagPrefixPattern = mergeTagPrefixPattern(config.adapters);
195
285
  this.state = {
196
286
  observer: null,
197
287
  isRunning: false,
@@ -492,34 +582,29 @@ var TestIdObserver = class {
492
582
  /**
493
583
  * 获取元素的简化标签名
494
584
  *
495
- * 处理 Antd 组件前缀: a-button → button, a-input → input
585
+ * 去除所有适配器注册的 UI 库标签前缀:
586
+ * AntD: a-button → button, a-input → input
587
+ * Element: el-button → button, el-input → input
496
588
  */
497
589
  getSimpleTag(node) {
498
- return node.tagName.toLowerCase().replace(/^a-/, "");
590
+ let tag = node.tagName.toLowerCase();
591
+ if (this.tagPrefixPattern) {
592
+ tag = tag.replace(this.tagPrefixPattern, "");
593
+ }
594
+ return tag;
499
595
  }
500
596
  /**
501
597
  * 判断是否可交互元素
502
598
  *
503
599
  * 可交互特征:
504
- * - 交互类标签: button, input, select, textarea
600
+ * - 匹配任意适配器的交互标签 (含原生 + UI 库前缀)
505
601
  * - onclick 属性
506
- * - role="button" / role="checkbox" / role="radio"
507
- * - cursor:pointer (不检测,因为可能从 CSS 继承,误判率高)
602
+ * - role="button" / role="checkbox" / role="radio" / role="switch"
603
+ * - tabindex 属性
508
604
  */
509
605
  isInteractive(node) {
510
606
  const tag = node.tagName.toLowerCase();
511
- const interactiveTags = [
512
- "button",
513
- "a-button",
514
- "input",
515
- "a-input",
516
- "a-input-number",
517
- "select",
518
- "a-select",
519
- "textarea",
520
- "a-textarea"
521
- ];
522
- if (interactiveTags.includes(tag)) return true;
607
+ if (this.interactiveTags.includes(tag)) return true;
523
608
  if (node.hasAttribute("onclick")) return true;
524
609
  const role = node.getAttribute("role");
525
610
  if (role === "button" || role === "checkbox" || role === "radio" || role === "switch") {
@@ -704,12 +789,13 @@ var TestIdChecker = class {
704
789
  }
705
790
  };
706
791
  export {
707
- INTERACTIVE_TAGS,
708
792
  TestIdChecker,
709
793
  TestIdObserver,
794
+ antdAdapter,
710
795
  buildAnchorTestId,
711
796
  buildPopupTestId,
712
797
  defaultConfig,
798
+ elementAdapter,
713
799
  getAnchorCounterMap,
714
800
  getConfig,
715
801
  getNextAnchorLocalIndex,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testid/antd-testid-runtime",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "运行时兜底打标模块 — MutationObserver + 锚点计数器 + 浮层计数器 + ID 重复检测",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",