vite-plugin-vue-testid 1.0.2 → 1.0.3
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.en.md +6 -4
- package/README.md +6 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +12 -4
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -165,10 +165,12 @@ a-select-0-dropdown-option-Option2 # option
|
|
|
165
165
|
### Runtime TreeSelect
|
|
166
166
|
|
|
167
167
|
```
|
|
168
|
-
a-tree-select-0-dropdown
|
|
169
|
-
a-tree-select-0-dropdown-tree-root-1
|
|
170
|
-
a-tree-select-0-dropdown-tree-
|
|
171
|
-
a-tree-select-0-dropdown-tree-
|
|
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
|
|
172
174
|
```
|
|
173
175
|
|
|
174
176
|
---
|
package/README.md
CHANGED
|
@@ -165,10 +165,12 @@ a-select-0-dropdown-option-选项2 # 选项
|
|
|
165
165
|
### 运行时 TreeSelect
|
|
166
166
|
|
|
167
167
|
```
|
|
168
|
-
a-tree-select-0-dropdown
|
|
169
|
-
a-tree-select-0-dropdown-tree-root-1
|
|
170
|
-
a-tree-select-0-dropdown-tree-
|
|
171
|
-
a-tree-select-0-dropdown-tree-
|
|
168
|
+
a-tree-select-0-dropdown # 下拉容器
|
|
169
|
+
a-tree-select-0-dropdown-tree-root-1 # 根节点
|
|
170
|
+
a-tree-select-0-dropdown-tree-switcher-expand-root-1 # 展开图标(可展开状态)
|
|
171
|
+
a-tree-select-0-dropdown-tree-switcher-collapse-root-1 # 展开图标(已展开状态)
|
|
172
|
+
a-tree-select-0-dropdown-tree-parent-1 # 父节点(展开后注入)
|
|
173
|
+
a-tree-select-0-dropdown-tree-my-leaf # 叶子节点
|
|
172
174
|
```
|
|
173
175
|
|
|
174
176
|
---
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;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;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;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;AAoX7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,cAAmB,GAAG,MAAM,IAAI,CAwE7E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,cAAmB,GAAG,IAAI,CA4CtE"}
|
package/dist/runtime.js
CHANGED
|
@@ -172,12 +172,20 @@ function injectSelectPanel(ctx) {
|
|
|
172
172
|
const title = el.getAttribute("title") || el.textContent?.trim() || "";
|
|
173
173
|
el.setAttribute(attrName, `${prefix}-option-${title}`);
|
|
174
174
|
});
|
|
175
|
-
const treeNodes = container.querySelectorAll(".ant-select-tree-
|
|
175
|
+
const treeNodes = container.querySelectorAll(".ant-select-tree-treenode");
|
|
176
176
|
treeNodes.forEach((node) => {
|
|
177
177
|
const el = node;
|
|
178
|
-
|
|
179
|
-
const title =
|
|
180
|
-
|
|
178
|
+
const content = el.querySelector(".ant-select-tree-node-content-wrapper");
|
|
179
|
+
const title = content?.getAttribute("title") || content?.textContent?.trim() || "";
|
|
180
|
+
if (content && !content.hasAttribute(attrName)) {
|
|
181
|
+
content.setAttribute(attrName, `${prefix}-tree-${title}`);
|
|
182
|
+
}
|
|
183
|
+
const switcher = el.querySelector(".ant-select-tree-switcher");
|
|
184
|
+
if (switcher && !switcher.hasAttribute(attrName)) {
|
|
185
|
+
const isOpen = switcher.classList.contains("ant-select-tree-switcher_open");
|
|
186
|
+
const state = isOpen ? "collapse" : "expand";
|
|
187
|
+
switcher.setAttribute(attrName, `${prefix}-tree-switcher-${state}-${title}`);
|
|
188
|
+
}
|
|
181
189
|
});
|
|
182
190
|
};
|
|
183
191
|
injectAllItems();
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * 运行时辅助模块:为 ant-design-vue teleport 弹出层(日期选择器、级联选择器等)\n * 中的 DOM 元素注入 data-testid,解决编译期无法处理的定位问题。\n *\n * 使用方式(在 main.ts 或入口文件中调用一次即可):\n * ```ts\n * import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'\n *\n * setupAntDropdownTestIds()\n * ```\n */\n\nexport interface RuntimeOptions {\n /**\n * testId 属性名,需与编译期插件保持一致\n * @default 'data-testid'\n */\n attributeName?: string\n\n /**\n * 需要监听的 teleport 面板 CSS 选择器映射\n * key: 面板容器选择器\n * value: 注入策略(自定义函数 或 undefined 使用内置策略)\n */\n panels?: Record<string, PanelInjectStrategy | undefined>\n}\n\nexport interface PanelContext {\n /** 面板容器 DOM 元素 */\n container: HTMLElement\n /** 当前激活的触发器元素 */\n trigger: HTMLElement | null\n /** 触发器的 testId 值 */\n triggerTestId: string\n /** testId 属性名 */\n attrName: string\n}\n\nexport type PanelInjectStrategy = (ctx: PanelContext) => void\n\n// ──────────────────────── 内置注入策略 ────────────────────────\n\n/**\n * 判断当前 PickerPanel 处于哪种展示模式。\n * ant-design 通过 CSS 类区分:date / month / year / decade\n */\nfunction getPanelMode(panel: Element): 'date' | 'month' | 'year' | 'decade' {\n if (panel.classList.contains('ant-picker-month-panel')) return 'month'\n if (panel.classList.contains('ant-picker-year-panel')) return 'year'\n if (panel.classList.contains('ant-picker-decade-panel')) return 'decade'\n return 'date'\n}\n\n/** 月份英文缩写 */\nconst MONTH_NAMES = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',\n 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\n\n/**\n * 为 header-view 内部的独立按钮注入 testId\n * ant-design 中 header-view 在 date 模式下包含两个按钮:\n * - 月份按钮(如 \"Jun\" / \"6月\")→ header-month-btn\n * - 年份按钮(如 \"2026\") → header-year-btn\n */\nfunction injectHeaderViewButtons(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: string,\n): void {\n const headerView = panel.querySelector('.ant-picker-header-view')\n if (!headerView) return\n\n const buttons = headerView.querySelectorAll('button')\n buttons.forEach((btn) => {\n const el = btn as HTMLElement\n if (el.hasAttribute(attrName)) return\n\n const text = el.textContent?.trim().toLowerCase() || ''\n\n let name: string\n if (MONTH_NAMES.some((m) => text.startsWith(m))) {\n // 匹配 Jan/Feb/... 英文月份缩写\n name = 'header-month-btn'\n } else if (/^\\d{1,2}月?$/.test(text)) {\n // 匹配 \"1月\" \"2月\" ... \"12月\" 或纯数字 \"01\" \"1\" ~ \"12\"\n name = 'header-month-btn'\n } else if (/^\\d{4}$/.test(text)) {\n // 4 位数字年份如 \"2026\"\n name = 'header-year-btn'\n } else if (/^\\d{4}-\\d{4}$/.test(text)) {\n // 年代范围如 \"2020-2029\"\n name = 'header-decade-btn'\n } else {\n // 兜底:直接用文本内容\n name = `header-btn-${text}`\n }\n\n el.setAttribute(attrName, `${prefix}-${name}${suffix}`)\n })\n}\n\n/** 已绑定 MutationObserver 的容器,避免重复绑定 */\nconst _observedContainers = new WeakSet<Element>()\n\n/**\n * 为单个 PickerPanel 注入全部 testId:\n * - header 按钮(super-prev/prev/next/super-next)\n * - header-view(带模式后缀:header-date / header-month / header-year)\n * - header-view 内部按钮(header-month-btn / header-year-btn)\n * - 单元格(模式区分:date-xxx / month-xx / year-xxxx 等)\n */\nfunction injectSinglePanel(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: '' | '-left' | '-right',\n): void {\n const mode = getPanelMode(panel)\n\n const setAttr = (selector: string, name: string) => {\n const el = panel.querySelector(selector) as HTMLElement | null\n if (el && !el.hasAttribute(attrName)) {\n el.setAttribute(attrName, `${prefix}-${name}${suffix}`)\n }\n }\n\n setAttr('.ant-picker-header-super-prev-btn', 'super-prev')\n setAttr('.ant-picker-header-prev-btn', 'prev')\n setAttr('.ant-picker-header-next-btn', 'next')\n setAttr('.ant-picker-header-super-next-btn', 'super-next')\n setAttr('.ant-picker-header-view', `header-${mode}`)\n\n injectHeaderViewButtons(panel, prefix, attrName, suffix)\n injectPanelCells(panel, prefix, attrName, suffix)\n}\n\n/**\n * 根据面板当前模式注入单元格 testId\n *\n * date → data-testid=\"{prefix}-date-2026-06-15{suffix}\"\n * month → data-testid=\"{prefix}-month-01{suffix}\"\n * year → data-testid=\"{prefix}-year-2026{suffix}\"\n * decade → data-testid=\"{prefix}-decade-2020-2029{suffix}\"\n */\nfunction injectPanelCells(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: string,\n): void {\n const mode = getPanelMode(panel)\n const cells = panel.querySelectorAll('.ant-picker-cell-inner')\n\n cells.forEach((cell) => {\n const el = cell as HTMLElement\n if (el.hasAttribute(attrName)) return\n\n const raw = (el.getAttribute('title') || el.textContent?.trim() || '').replace(/\\s+/g, '-')\n let label: string\n\n switch (mode) {\n case 'month':\n // antd 月份单元格 title 一般是 \"2026-01\",取后两位即可\n label = raw.length >= 2 ? raw.slice(-2) : raw\n el.setAttribute(attrName, `${prefix}-month-${label}${suffix}`)\n break\n case 'year':\n label = raw\n el.setAttribute(attrName, `${prefix}-year-${label}${suffix}`)\n break\n case 'decade':\n label = raw\n el.setAttribute(attrName, `${prefix}-decade-${label}${suffix}`)\n break\n default:\n // date 模式\n label = raw\n el.setAttribute(attrName, `${prefix}-date-${label}${suffix}`)\n break\n }\n })\n}\n\n/**\n * ant-design-vue DatePicker / RangePicker / TimePicker 面板策略\n *\n * DatePicker:单面板,直接注入\n * RangePicker:左右双面板,分别标记 left / right\n * TimePicker:时间列,逐项注入\n */\nfunction injectPickerPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n // 面板容器本身\n container.setAttribute(attrName, prefix)\n\n const isRange = container.classList.contains('ant-picker-dropdown-range')\n\n /**\n * 注入容器内所有子面板(header+单元格)\n * 同时注入顶部/底部固定元素(preset、footer buttons 等)\n */\n const injectAllPanels = () => {\n const panels = container.querySelectorAll('.ant-picker-panel')\n if (isRange && panels.length >= 2) {\n injectSinglePanel(panels[0], prefix, attrName, '-left')\n injectSinglePanel(panels[1], prefix, attrName, '-right')\n } else {\n const panel = panels[0] || container\n injectSinglePanel(panel, prefix, attrName, '')\n }\n\n // preset 快捷范围标签\n const presets = container.querySelectorAll('.ant-picker-presets .ant-tag')\n presets.forEach((tag) => {\n const el = tag as HTMLElement\n if (el.hasAttribute(attrName)) return\n const text = el.textContent?.trim() || ''\n el.setAttribute(attrName, `${prefix}-preset-${text}`)\n })\n\n // 时间选择器的时间列\n const timeColumns = container.querySelectorAll('.ant-picker-time-panel-column')\n timeColumns.forEach((col, i) => {\n const el = col as HTMLElement\n if (el.hasAttribute(attrName)) return\n el.setAttribute(attrName, `${prefix}-time-column-${i}`)\n const items = col.querySelectorAll('li')\n items.forEach((item) => {\n const li = item as HTMLElement\n if (li.hasAttribute(attrName)) return\n const text = li.textContent?.trim() || ''\n li.setAttribute(attrName, `${prefix}-time-${i}-${text}`)\n })\n })\n\n // 底部按钮\n const setFooterBtn = (selector: string, name: string) => {\n const btn = container.querySelector(selector) as HTMLElement | null\n if (btn && !btn.hasAttribute(attrName)) {\n btn.setAttribute(attrName, `${prefix}-${name}`)\n }\n }\n setFooterBtn('.ant-picker-ok button', 'ok')\n setFooterBtn('.ant-picker-now button', 'now')\n setFooterBtn('.ant-picker-now-btn', 'now')\n }\n\n // 首次注入\n injectAllPanels()\n\n // ── 容器级 MutationObserver ──\n // 绑定到 dropdown 容器而非单个 panel,这样即便 ant-design\n // 在 date↔month↔year 切换时替换了 panel 元素,也能检测到变化并重注入\n if (!_observedContainers.has(container)) {\n _observedContainers.add(container)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const observer = new MutationObserver(() => {\n if (timer !== null) clearTimeout(timer)\n timer = setTimeout(() => {\n injectAllPanels()\n }, 50) // 等 Vue nextTick + DOM 全部更新\n })\n\n observer.observe(container, { childList: true, subtree: true })\n }\n}\n\n/**\n * ant-design-vue Cascader 面板策略\n */\nfunction injectCascaderPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n container.setAttribute(attrName, prefix)\n\n const menus = container.querySelectorAll('.ant-cascader-menu')\n menus.forEach((menu, i) => {\n const el = menu as HTMLElement\n el.setAttribute(attrName, `${prefix}-menu-${i}`)\n const items = menu.querySelectorAll('.ant-cascader-menu-item')\n items.forEach((item) => {\n const li = item as HTMLElement\n const text = li.textContent?.trim() || ''\n li.setAttribute(attrName, `${prefix}-item-${text}`)\n })\n })\n}\n\n/**\n * ant-design-vue Select / TreeSelect 下拉面板策略\n */\nfunction injectSelectPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n container.setAttribute(attrName, prefix)\n\n /** 注入容器内所有选项/树节点的 testId */\n const injectAllItems = () => {\n const options = container.querySelectorAll('.ant-select-item-option')\n options.forEach((opt) => {\n const el = opt as HTMLElement\n if (el.hasAttribute(attrName)) return\n const title = el.getAttribute('title') || el.textContent?.trim() || ''\n el.setAttribute(attrName, `${prefix}-option-${title}`)\n })\n\n const treeNodes = container.querySelectorAll('.ant-select-tree-node-content-wrapper')\n treeNodes.forEach((node) => {\n const el = node as HTMLElement\n if (el.hasAttribute(attrName)) return\n const title = el.getAttribute('title') || el.textContent?.trim() || ''\n el.setAttribute(attrName, `${prefix}-tree-${title}`)\n })\n }\n\n // 首次注入\n injectAllItems()\n\n // TreeSelect 展开/折叠子节点时会动态增删 DOM,需要 Observer 持续注入\n if (!_observedContainers.has(container)) {\n _observedContainers.add(container)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const observer = new MutationObserver(() => {\n if (timer !== null) clearTimeout(timer)\n timer = setTimeout(() => {\n injectAllItems()\n }, 50)\n })\n\n observer.observe(container, { childList: true, subtree: true })\n }\n}\n\n// ──────────────────────── 默认面板配置 ────────────────────────\n\nconst DEFAULT_PANELS: Record<string, PanelInjectStrategy> = {\n '.ant-picker-dropdown': injectPickerPanel,\n '.ant-cascader-dropdown': injectCascaderPanel,\n '.ant-select-dropdown': injectSelectPanel,\n}\n\n// ──────────────────────── Observer ────────────────────────\n\n/**\n * 获取触发器上的 testId 值。\n * ant-design-vue 4.x DatePicker 由于 inheritAttrs: false,data-testid 被丢弃,\n * 只有 id 被转发到了内部 input。因为编译期插件同时注入 id + data-testid(值相同),\n * 优先读 testid,兜底读 id。\n */\nfunction getTriggerTestId(trigger: HTMLElement | null, attrName: string): string {\n if (!trigger) return 'unknown-picker'\n return trigger.getAttribute(attrName) || trigger.getAttribute('id') || 'unknown-picker'\n}\n\n/**\n * 查找当前激活的触发器元素\n */\nfunction findActiveTrigger(attrName: string): HTMLElement | null {\n // 日期选择器:有 .ant-picker-focused 类\n const focused = document.querySelector('.ant-picker-focused') as HTMLElement | null\n if (focused) {\n // 1) focused 根元素自身有 testid\n if (focused.hasAttribute(attrName)) return focused\n // 2) 内部子元素有 testid(ant-design-vue 4.x 通过 getDataOrAriaProps 转发到 input)\n const inner = focused.querySelector(`[${attrName}]`) as HTMLElement | null\n if (inner) return inner\n // 3) ant-design-vue 4.x DatePicker: data-testid 被丢弃(不在 Picker 的 declared props 中),\n // 但 id 属性被显式转发到了内部 <input>。由于插件同时注入 id + data-testid 且值相同,\n // 可以通过 input[id] 找到触发器\n const input = focused.querySelector('input[id]') as HTMLElement | null\n if (input) return input\n }\n\n // Select / TreeSelect:展开时根 div 有 .ant-select-open 类,且 data-testid 正常到达 DOM\n const selectOpen = document.querySelector('.ant-select-open') as HTMLElement | null\n if (selectOpen?.hasAttribute(attrName)) return selectOpen\n\n // 级联选择器\n const cascader = document.querySelector('.ant-cascader-picker-focused') as HTMLElement | null\n if (cascader?.hasAttribute(attrName)) return cascader\n\n // 通用:查找有 testId 的最近打开的面板触发器\n // ant-design 通常在展开面板的元素上添加 aria-expanded=\"true\"\n const expanded = document.querySelector(\n `[${attrName}][aria-expanded=\"true\"]`,\n ) as HTMLElement | null\n if (expanded) return expanded\n\n return null\n}\n\n/**\n * 为 teleport 弹出层面板注入 testId。\n * 使用 MutationObserver 监听 DOM 新增节点,自动识别 ant-design 弹出面板。\n *\n * @example\n * ```ts\n * // main.ts\n * import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'\n *\n * createApp(App).mount('#app')\n * setupAntDropdownTestIds()\n * ```\n *\n * testId 注入结果示例:\n * ```\n * // 日期面板(初始)\n * a-date-picker-0-dropdown // 面板容器\n * a-date-picker-0-dropdown-prev // 上月/上一级\n * a-date-picker-0-dropdown-next // 下月/下一级\n * a-date-picker-0-dropdown-header-date // header 容器\n * a-date-picker-0-dropdown-header-month-btn // header 内的月份按钮(\"Jun\"/\"6月\")\n * a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮(\"2026\")\n * a-date-picker-0-dropdown-date-2026-06-15 // 日期单元格\n * a-date-picker-0-dropdown-ok // 确定按钮\n *\n * // 点击月份按钮 → 月面板(MutationObserver 自动重注入)\n * a-date-picker-0-dropdown-header-month // header 容器\n * a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮\n * a-date-picker-0-dropdown-month-06 // 6月按钮\n *\n * // 点击年份按钮 → 年面板\n * a-date-picker-0-dropdown-header-year // header 容器\n * a-date-picker-0-dropdown-year-2026 // 2026年按钮\n * ```\n */\nexport function setupAntDropdownTestIds(opts: RuntimeOptions = {}): () => void {\n const attrName = opts.attributeName ?? 'data-testid'\n const panels = { ...DEFAULT_PANELS, ...opts.panels }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof HTMLElement)) continue\n\n // 检查新增节点匹配哪种面板选择器\n for (const [selector, strategy] of Object.entries(panels)) {\n const containers = node.matches(selector)\n ? [node]\n : Array.from(node.querySelectorAll(selector))\n\n for (const container of containers) {\n if (!(container instanceof HTMLElement)) continue\n if (container.hasAttribute(attrName)) continue // 已注入过\n\n // 等一个微任务让 CSS 类生效,再找触发器\n if (strategy) {\n queueMicrotask(() => {\n const trigger = findActiveTrigger(attrName)\n const triggerTestId = getTriggerTestId(trigger, attrName)\n\n const ctx: PanelContext = {\n container,\n trigger,\n triggerTestId,\n attrName,\n }\n strategy(ctx)\n })\n }\n }\n }\n\n // Upload 组件:需要将 testId 注入到内部 <input type=\"file\">\n // ant-design-vue Upload 可能 inheritAttrs: false,data-testid 不会到达 DOM,\n // 但 id 属性会被转发。编译期双注入保证 id 与 data-testid 值相同。\n const uploadByTestId = node.matches(`.ant-upload[${attrName}]`)\n ? [node]\n : Array.from(node.querySelectorAll(`.ant-upload[${attrName}]`))\n const uploadById = uploadByTestId.length === 0\n ? (node.matches(`.ant-upload[id]`)\n ? [node]\n : Array.from(node.querySelectorAll(`.ant-upload[id]`)))\n : []\n\n const uploads = [...uploadByTestId, ...uploadById]\n\n for (const upload of uploads) {\n if (!(upload instanceof HTMLElement)) continue\n const testId = upload.getAttribute(attrName) || upload.getAttribute('id') || ''\n if (!testId) continue\n const input = upload.querySelector('input[type=\"file\"]') as HTMLElement | null\n if (input && !input.hasAttribute(attrName)) {\n input.setAttribute(attrName, `${testId}-input`)\n // 给外层容器也补上 testId(如果还没有)\n if (!upload.hasAttribute(attrName)) {\n upload.setAttribute(attrName, testId)\n }\n }\n }\n }\n }\n })\n\n observer.observe(document.body, { childList: true, subtree: true })\n\n // 返回清理函数\n return () => observer.disconnect()\n}\n\n/**\n * 手动为当前已存在或即将出现的弹出面板注入 testId(一次性补丁)。\n * 适用于 SSR/hydration 或不想用 MutationObserver 的场景。\n */\nexport function injectCurrentDropdowns(opts: RuntimeOptions = {}): void {\n const attrName = opts.attributeName ?? 'data-testid'\n const panels = { ...DEFAULT_PANELS, ...opts.panels }\n\n for (const [selector, strategy] of Object.entries(panels)) {\n const containers = document.querySelectorAll(selector)\n containers.forEach((container) => {\n if (!(container instanceof HTMLElement)) return\n if (container.hasAttribute(attrName)) return\n\n if (!strategy) return\n\n const trigger = findActiveTrigger(attrName)\n const triggerTestId = getTriggerTestId(trigger, attrName)\n\n strategy({ container, trigger, triggerTestId, attrName })\n })\n }\n\n // Upload 组件:需要将 testId 注入到内部 <input type=\"file\">\n // ant-design-vue Upload 可能 inheritAttrs: false,data-testid 不会到达 DOM,\n // 但 id 属性会被转发。编译期双注入保证 id 与 data-testid 值相同。\n const uploads = [\n ...Array.from(document.querySelectorAll(`.ant-upload[${attrName}]`)),\n ...Array.from(document.querySelectorAll(`.ant-upload[id]`)),\n ]\n // 去重\n const seen = new Set<Element>()\n uploads.forEach((upload) => {\n if (!(upload instanceof HTMLElement)) return\n if (seen.has(upload)) return\n seen.add(upload)\n\n const testId = upload.getAttribute(attrName) || upload.getAttribute('id') || ''\n if (!testId) return\n const input = upload.querySelector('input[type=\"file\"]') as HTMLElement | null\n if (input && !input.hasAttribute(attrName)) {\n input.setAttribute(attrName, `${testId}-input`)\n // 给外层容器也补上 testId(如果还没有)\n if (!upload.hasAttribute(attrName)) {\n upload.setAttribute(attrName, testId)\n }\n }\n })\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * 运行时辅助模块:为 ant-design-vue teleport 弹出层(日期选择器、级联选择器等)\n * 中的 DOM 元素注入 data-testid,解决编译期无法处理的定位问题。\n *\n * 使用方式(在 main.ts 或入口文件中调用一次即可):\n * ```ts\n * import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'\n *\n * setupAntDropdownTestIds()\n * ```\n */\n\nexport interface RuntimeOptions {\n /**\n * testId 属性名,需与编译期插件保持一致\n * @default 'data-testid'\n */\n attributeName?: string\n\n /**\n * 需要监听的 teleport 面板 CSS 选择器映射\n * key: 面板容器选择器\n * value: 注入策略(自定义函数 或 undefined 使用内置策略)\n */\n panels?: Record<string, PanelInjectStrategy | undefined>\n}\n\nexport interface PanelContext {\n /** 面板容器 DOM 元素 */\n container: HTMLElement\n /** 当前激活的触发器元素 */\n trigger: HTMLElement | null\n /** 触发器的 testId 值 */\n triggerTestId: string\n /** testId 属性名 */\n attrName: string\n}\n\nexport type PanelInjectStrategy = (ctx: PanelContext) => void\n\n// ──────────────────────── 内置注入策略 ────────────────────────\n\n/**\n * 判断当前 PickerPanel 处于哪种展示模式。\n * ant-design 通过 CSS 类区分:date / month / year / decade\n */\nfunction getPanelMode(panel: Element): 'date' | 'month' | 'year' | 'decade' {\n if (panel.classList.contains('ant-picker-month-panel')) return 'month'\n if (panel.classList.contains('ant-picker-year-panel')) return 'year'\n if (panel.classList.contains('ant-picker-decade-panel')) return 'decade'\n return 'date'\n}\n\n/** 月份英文缩写 */\nconst MONTH_NAMES = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',\n 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']\n\n/**\n * 为 header-view 内部的独立按钮注入 testId\n * ant-design 中 header-view 在 date 模式下包含两个按钮:\n * - 月份按钮(如 \"Jun\" / \"6月\")→ header-month-btn\n * - 年份按钮(如 \"2026\") → header-year-btn\n */\nfunction injectHeaderViewButtons(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: string,\n): void {\n const headerView = panel.querySelector('.ant-picker-header-view')\n if (!headerView) return\n\n const buttons = headerView.querySelectorAll('button')\n buttons.forEach((btn) => {\n const el = btn as HTMLElement\n if (el.hasAttribute(attrName)) return\n\n const text = el.textContent?.trim().toLowerCase() || ''\n\n let name: string\n if (MONTH_NAMES.some((m) => text.startsWith(m))) {\n // 匹配 Jan/Feb/... 英文月份缩写\n name = 'header-month-btn'\n } else if (/^\\d{1,2}月?$/.test(text)) {\n // 匹配 \"1月\" \"2月\" ... \"12月\" 或纯数字 \"01\" \"1\" ~ \"12\"\n name = 'header-month-btn'\n } else if (/^\\d{4}$/.test(text)) {\n // 4 位数字年份如 \"2026\"\n name = 'header-year-btn'\n } else if (/^\\d{4}-\\d{4}$/.test(text)) {\n // 年代范围如 \"2020-2029\"\n name = 'header-decade-btn'\n } else {\n // 兜底:直接用文本内容\n name = `header-btn-${text}`\n }\n\n el.setAttribute(attrName, `${prefix}-${name}${suffix}`)\n })\n}\n\n/** 已绑定 MutationObserver 的容器,避免重复绑定 */\nconst _observedContainers = new WeakSet<Element>()\n\n/**\n * 为单个 PickerPanel 注入全部 testId:\n * - header 按钮(super-prev/prev/next/super-next)\n * - header-view(带模式后缀:header-date / header-month / header-year)\n * - header-view 内部按钮(header-month-btn / header-year-btn)\n * - 单元格(模式区分:date-xxx / month-xx / year-xxxx 等)\n */\nfunction injectSinglePanel(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: '' | '-left' | '-right',\n): void {\n const mode = getPanelMode(panel)\n\n const setAttr = (selector: string, name: string) => {\n const el = panel.querySelector(selector) as HTMLElement | null\n if (el && !el.hasAttribute(attrName)) {\n el.setAttribute(attrName, `${prefix}-${name}${suffix}`)\n }\n }\n\n setAttr('.ant-picker-header-super-prev-btn', 'super-prev')\n setAttr('.ant-picker-header-prev-btn', 'prev')\n setAttr('.ant-picker-header-next-btn', 'next')\n setAttr('.ant-picker-header-super-next-btn', 'super-next')\n setAttr('.ant-picker-header-view', `header-${mode}`)\n\n injectHeaderViewButtons(panel, prefix, attrName, suffix)\n injectPanelCells(panel, prefix, attrName, suffix)\n}\n\n/**\n * 根据面板当前模式注入单元格 testId\n *\n * date → data-testid=\"{prefix}-date-2026-06-15{suffix}\"\n * month → data-testid=\"{prefix}-month-01{suffix}\"\n * year → data-testid=\"{prefix}-year-2026{suffix}\"\n * decade → data-testid=\"{prefix}-decade-2020-2029{suffix}\"\n */\nfunction injectPanelCells(\n panel: Element,\n prefix: string,\n attrName: string,\n suffix: string,\n): void {\n const mode = getPanelMode(panel)\n const cells = panel.querySelectorAll('.ant-picker-cell-inner')\n\n cells.forEach((cell) => {\n const el = cell as HTMLElement\n if (el.hasAttribute(attrName)) return\n\n const raw = (el.getAttribute('title') || el.textContent?.trim() || '').replace(/\\s+/g, '-')\n let label: string\n\n switch (mode) {\n case 'month':\n // antd 月份单元格 title 一般是 \"2026-01\",取后两位即可\n label = raw.length >= 2 ? raw.slice(-2) : raw\n el.setAttribute(attrName, `${prefix}-month-${label}${suffix}`)\n break\n case 'year':\n label = raw\n el.setAttribute(attrName, `${prefix}-year-${label}${suffix}`)\n break\n case 'decade':\n label = raw\n el.setAttribute(attrName, `${prefix}-decade-${label}${suffix}`)\n break\n default:\n // date 模式\n label = raw\n el.setAttribute(attrName, `${prefix}-date-${label}${suffix}`)\n break\n }\n })\n}\n\n/**\n * ant-design-vue DatePicker / RangePicker / TimePicker 面板策略\n *\n * DatePicker:单面板,直接注入\n * RangePicker:左右双面板,分别标记 left / right\n * TimePicker:时间列,逐项注入\n */\nfunction injectPickerPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n // 面板容器本身\n container.setAttribute(attrName, prefix)\n\n const isRange = container.classList.contains('ant-picker-dropdown-range')\n\n /**\n * 注入容器内所有子面板(header+单元格)\n * 同时注入顶部/底部固定元素(preset、footer buttons 等)\n */\n const injectAllPanels = () => {\n const panels = container.querySelectorAll('.ant-picker-panel')\n if (isRange && panels.length >= 2) {\n injectSinglePanel(panels[0], prefix, attrName, '-left')\n injectSinglePanel(panels[1], prefix, attrName, '-right')\n } else {\n const panel = panels[0] || container\n injectSinglePanel(panel, prefix, attrName, '')\n }\n\n // preset 快捷范围标签\n const presets = container.querySelectorAll('.ant-picker-presets .ant-tag')\n presets.forEach((tag) => {\n const el = tag as HTMLElement\n if (el.hasAttribute(attrName)) return\n const text = el.textContent?.trim() || ''\n el.setAttribute(attrName, `${prefix}-preset-${text}`)\n })\n\n // 时间选择器的时间列\n const timeColumns = container.querySelectorAll('.ant-picker-time-panel-column')\n timeColumns.forEach((col, i) => {\n const el = col as HTMLElement\n if (el.hasAttribute(attrName)) return\n el.setAttribute(attrName, `${prefix}-time-column-${i}`)\n const items = col.querySelectorAll('li')\n items.forEach((item) => {\n const li = item as HTMLElement\n if (li.hasAttribute(attrName)) return\n const text = li.textContent?.trim() || ''\n li.setAttribute(attrName, `${prefix}-time-${i}-${text}`)\n })\n })\n\n // 底部按钮\n const setFooterBtn = (selector: string, name: string) => {\n const btn = container.querySelector(selector) as HTMLElement | null\n if (btn && !btn.hasAttribute(attrName)) {\n btn.setAttribute(attrName, `${prefix}-${name}`)\n }\n }\n setFooterBtn('.ant-picker-ok button', 'ok')\n setFooterBtn('.ant-picker-now button', 'now')\n setFooterBtn('.ant-picker-now-btn', 'now')\n }\n\n // 首次注入\n injectAllPanels()\n\n // ── 容器级 MutationObserver ──\n // 绑定到 dropdown 容器而非单个 panel,这样即便 ant-design\n // 在 date↔month↔year 切换时替换了 panel 元素,也能检测到变化并重注入\n if (!_observedContainers.has(container)) {\n _observedContainers.add(container)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const observer = new MutationObserver(() => {\n if (timer !== null) clearTimeout(timer)\n timer = setTimeout(() => {\n injectAllPanels()\n }, 50) // 等 Vue nextTick + DOM 全部更新\n })\n\n observer.observe(container, { childList: true, subtree: true })\n }\n}\n\n/**\n * ant-design-vue Cascader 面板策略\n */\nfunction injectCascaderPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n container.setAttribute(attrName, prefix)\n\n const menus = container.querySelectorAll('.ant-cascader-menu')\n menus.forEach((menu, i) => {\n const el = menu as HTMLElement\n el.setAttribute(attrName, `${prefix}-menu-${i}`)\n const items = menu.querySelectorAll('.ant-cascader-menu-item')\n items.forEach((item) => {\n const li = item as HTMLElement\n const text = li.textContent?.trim() || ''\n li.setAttribute(attrName, `${prefix}-item-${text}`)\n })\n })\n}\n\n/**\n * ant-design-vue Select / TreeSelect 下拉面板策略\n */\nfunction injectSelectPanel(ctx: PanelContext): void {\n const { container, triggerTestId, attrName } = ctx\n const prefix = `${triggerTestId}-dropdown`\n\n container.setAttribute(attrName, prefix)\n\n /** 注入容器内所有选项/树节点的 testId */\n const injectAllItems = () => {\n const options = container.querySelectorAll('.ant-select-item-option')\n options.forEach((opt) => {\n const el = opt as HTMLElement\n if (el.hasAttribute(attrName)) return\n const title = el.getAttribute('title') || el.textContent?.trim() || ''\n el.setAttribute(attrName, `${prefix}-option-${title}`)\n })\n\n const treeNodes = container.querySelectorAll('.ant-select-tree-treenode')\n treeNodes.forEach((node) => {\n const el = node as HTMLElement\n // 节点内容\n const content = el.querySelector('.ant-select-tree-node-content-wrapper') as HTMLElement | null\n const title = content?.getAttribute('title') || content?.textContent?.trim() || ''\n if (content && !content.hasAttribute(attrName)) {\n content.setAttribute(attrName, `${prefix}-tree-${title}`)\n }\n\n // 左侧展开/折叠图标\n const switcher = el.querySelector('.ant-select-tree-switcher') as HTMLElement | null\n if (switcher && !switcher.hasAttribute(attrName)) {\n const isOpen = switcher.classList.contains('ant-select-tree-switcher_open')\n const state = isOpen ? 'collapse' : 'expand'\n switcher.setAttribute(attrName, `${prefix}-tree-switcher-${state}-${title}`)\n }\n })\n }\n\n // 首次注入\n injectAllItems()\n\n // TreeSelect 展开/折叠子节点时会动态增删 DOM,需要 Observer 持续注入\n if (!_observedContainers.has(container)) {\n _observedContainers.add(container)\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const observer = new MutationObserver(() => {\n if (timer !== null) clearTimeout(timer)\n timer = setTimeout(() => {\n injectAllItems()\n }, 50)\n })\n\n observer.observe(container, { childList: true, subtree: true })\n }\n}\n\n// ──────────────────────── 默认面板配置 ────────────────────────\n\nconst DEFAULT_PANELS: Record<string, PanelInjectStrategy> = {\n '.ant-picker-dropdown': injectPickerPanel,\n '.ant-cascader-dropdown': injectCascaderPanel,\n '.ant-select-dropdown': injectSelectPanel,\n}\n\n// ──────────────────────── Observer ────────────────────────\n\n/**\n * 获取触发器上的 testId 值。\n * ant-design-vue 4.x DatePicker 由于 inheritAttrs: false,data-testid 被丢弃,\n * 只有 id 被转发到了内部 input。因为编译期插件同时注入 id + data-testid(值相同),\n * 优先读 testid,兜底读 id。\n */\nfunction getTriggerTestId(trigger: HTMLElement | null, attrName: string): string {\n if (!trigger) return 'unknown-picker'\n return trigger.getAttribute(attrName) || trigger.getAttribute('id') || 'unknown-picker'\n}\n\n/**\n * 查找当前激活的触发器元素\n */\nfunction findActiveTrigger(attrName: string): HTMLElement | null {\n // 日期选择器:有 .ant-picker-focused 类\n const focused = document.querySelector('.ant-picker-focused') as HTMLElement | null\n if (focused) {\n // 1) focused 根元素自身有 testid\n if (focused.hasAttribute(attrName)) return focused\n // 2) 内部子元素有 testid(ant-design-vue 4.x 通过 getDataOrAriaProps 转发到 input)\n const inner = focused.querySelector(`[${attrName}]`) as HTMLElement | null\n if (inner) return inner\n // 3) ant-design-vue 4.x DatePicker: data-testid 被丢弃(不在 Picker 的 declared props 中),\n // 但 id 属性被显式转发到了内部 <input>。由于插件同时注入 id + data-testid 且值相同,\n // 可以通过 input[id] 找到触发器\n const input = focused.querySelector('input[id]') as HTMLElement | null\n if (input) return input\n }\n\n // Select / TreeSelect:展开时根 div 有 .ant-select-open 类,且 data-testid 正常到达 DOM\n const selectOpen = document.querySelector('.ant-select-open') as HTMLElement | null\n if (selectOpen?.hasAttribute(attrName)) return selectOpen\n\n // 级联选择器\n const cascader = document.querySelector('.ant-cascader-picker-focused') as HTMLElement | null\n if (cascader?.hasAttribute(attrName)) return cascader\n\n // 通用:查找有 testId 的最近打开的面板触发器\n // ant-design 通常在展开面板的元素上添加 aria-expanded=\"true\"\n const expanded = document.querySelector(\n `[${attrName}][aria-expanded=\"true\"]`,\n ) as HTMLElement | null\n if (expanded) return expanded\n\n return null\n}\n\n/**\n * 为 teleport 弹出层面板注入 testId。\n * 使用 MutationObserver 监听 DOM 新增节点,自动识别 ant-design 弹出面板。\n *\n * @example\n * ```ts\n * // main.ts\n * import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'\n *\n * createApp(App).mount('#app')\n * setupAntDropdownTestIds()\n * ```\n *\n * testId 注入结果示例:\n * ```\n * // 日期面板(初始)\n * a-date-picker-0-dropdown // 面板容器\n * a-date-picker-0-dropdown-prev // 上月/上一级\n * a-date-picker-0-dropdown-next // 下月/下一级\n * a-date-picker-0-dropdown-header-date // header 容器\n * a-date-picker-0-dropdown-header-month-btn // header 内的月份按钮(\"Jun\"/\"6月\")\n * a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮(\"2026\")\n * a-date-picker-0-dropdown-date-2026-06-15 // 日期单元格\n * a-date-picker-0-dropdown-ok // 确定按钮\n *\n * // 点击月份按钮 → 月面板(MutationObserver 自动重注入)\n * a-date-picker-0-dropdown-header-month // header 容器\n * a-date-picker-0-dropdown-header-year-btn // header 内的年份按钮\n * a-date-picker-0-dropdown-month-06 // 6月按钮\n *\n * // 点击年份按钮 → 年面板\n * a-date-picker-0-dropdown-header-year // header 容器\n * a-date-picker-0-dropdown-year-2026 // 2026年按钮\n * ```\n */\nexport function setupAntDropdownTestIds(opts: RuntimeOptions = {}): () => void {\n const attrName = opts.attributeName ?? 'data-testid'\n const panels = { ...DEFAULT_PANELS, ...opts.panels }\n\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof HTMLElement)) continue\n\n // 检查新增节点匹配哪种面板选择器\n for (const [selector, strategy] of Object.entries(panels)) {\n const containers = node.matches(selector)\n ? [node]\n : Array.from(node.querySelectorAll(selector))\n\n for (const container of containers) {\n if (!(container instanceof HTMLElement)) continue\n if (container.hasAttribute(attrName)) continue // 已注入过\n\n // 等一个微任务让 CSS 类生效,再找触发器\n if (strategy) {\n queueMicrotask(() => {\n const trigger = findActiveTrigger(attrName)\n const triggerTestId = getTriggerTestId(trigger, attrName)\n\n const ctx: PanelContext = {\n container,\n trigger,\n triggerTestId,\n attrName,\n }\n strategy(ctx)\n })\n }\n }\n }\n\n // Upload 组件:需要将 testId 注入到内部 <input type=\"file\">\n // ant-design-vue Upload 可能 inheritAttrs: false,data-testid 不会到达 DOM,\n // 但 id 属性会被转发。编译期双注入保证 id 与 data-testid 值相同。\n const uploadByTestId = node.matches(`.ant-upload[${attrName}]`)\n ? [node]\n : Array.from(node.querySelectorAll(`.ant-upload[${attrName}]`))\n const uploadById = uploadByTestId.length === 0\n ? (node.matches(`.ant-upload[id]`)\n ? [node]\n : Array.from(node.querySelectorAll(`.ant-upload[id]`)))\n : []\n\n const uploads = [...uploadByTestId, ...uploadById]\n\n for (const upload of uploads) {\n if (!(upload instanceof HTMLElement)) continue\n const testId = upload.getAttribute(attrName) || upload.getAttribute('id') || ''\n if (!testId) continue\n const input = upload.querySelector('input[type=\"file\"]') as HTMLElement | null\n if (input && !input.hasAttribute(attrName)) {\n input.setAttribute(attrName, `${testId}-input`)\n // 给外层容器也补上 testId(如果还没有)\n if (!upload.hasAttribute(attrName)) {\n upload.setAttribute(attrName, testId)\n }\n }\n }\n }\n }\n })\n\n observer.observe(document.body, { childList: true, subtree: true })\n\n // 返回清理函数\n return () => observer.disconnect()\n}\n\n/**\n * 手动为当前已存在或即将出现的弹出面板注入 testId(一次性补丁)。\n * 适用于 SSR/hydration 或不想用 MutationObserver 的场景。\n */\nexport function injectCurrentDropdowns(opts: RuntimeOptions = {}): void {\n const attrName = opts.attributeName ?? 'data-testid'\n const panels = { ...DEFAULT_PANELS, ...opts.panels }\n\n for (const [selector, strategy] of Object.entries(panels)) {\n const containers = document.querySelectorAll(selector)\n containers.forEach((container) => {\n if (!(container instanceof HTMLElement)) return\n if (container.hasAttribute(attrName)) return\n\n if (!strategy) return\n\n const trigger = findActiveTrigger(attrName)\n const triggerTestId = getTriggerTestId(trigger, attrName)\n\n strategy({ container, trigger, triggerTestId, attrName })\n })\n }\n\n // Upload 组件:需要将 testId 注入到内部 <input type=\"file\">\n // ant-design-vue Upload 可能 inheritAttrs: false,data-testid 不会到达 DOM,\n // 但 id 属性会被转发。编译期双注入保证 id 与 data-testid 值相同。\n const uploads = [\n ...Array.from(document.querySelectorAll(`.ant-upload[${attrName}]`)),\n ...Array.from(document.querySelectorAll(`.ant-upload[id]`)),\n ]\n // 去重\n const seen = new Set<Element>()\n uploads.forEach((upload) => {\n if (!(upload instanceof HTMLElement)) return\n if (seen.has(upload)) return\n seen.add(upload)\n\n const testId = upload.getAttribute(attrName) || upload.getAttribute('id') || ''\n if (!testId) return\n const input = upload.querySelector('input[type=\"file\"]') as HTMLElement | null\n if (input && !input.hasAttribute(attrName)) {\n input.setAttribute(attrName, `${testId}-input`)\n // 给外层容器也补上 testId(如果还没有)\n if (!upload.hasAttribute(attrName)) {\n upload.setAttribute(attrName, testId)\n }\n }\n })\n}\n"],"mappings":"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","names":[]}
|