npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-vue-testid

v1.0.3

Published

Vite plugin to auto-inject data-testid into Vue component templates for E2E testing

Readme

vite-plugin-vue-testid

编译期 + 运行时自动为 Vue 组件模板注入 data-testid 的 Vite 插件,专为 ant-design-vue 4.x 适配,大幅降低 E2E 测试中的元素定位成本。

特性

  • 编译期注入 — 通过 Vue 编译器 nodeTransform 自动为组件模板添加 data-testid
  • 运行时注入 — 为 ant-design-vue teleport 弹出层(DatePicker、Cascader、Select、TreeSelect 等)动态注入 testId
  • 适配 — bodyCell 插槽内组件自动生成「列名 + 行号 + 序号」的唯一 testId
  • 自动补全 index 解构 — 即使 #bodyCell="{ column, record }" 未写 index,插件也会自动补上
  • 不会覆盖已有 testId — 手动标注了 data-testid 的元素会被跳过
  • teleport 组件双注入 — DatePicker / RangePicker / Modal 同时注入 id + data-testid
  • 日期面板模式感知 — date / month / year / decade 面板自动区分
  • MutationObserver 重注入 — 面板切换、树节点展开/折叠时自动检测并注入新 DOM
  • 可扩展 — 支持自定义组件列表、testId 生成规则、弹出面板注入策略

安装

pnpm add -D vite-plugin-vue-testid

要求:vite >= 5.0.0vue >= 3.3.0@vue/compiler-core >= 3.5.0


快速开始

1. 编译期注入(vite.config.ts)

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createVueTestIdTransform } from 'vite-plugin-vue-testid'

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          nodeTransforms: [createVueTestIdTransform()]
        }
      }
    })
  ]
})

2. 运行时注入(main.ts)

// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'

const app = createApp(App)
app.mount('#app')

// 在 mount 之后调用,监听 teleport 面板并自动注入 testId
setupAntDropdownTestIds()

生成的 testId 示例

普通组件

<!-- 源码 -->
<a-input />
<a-select />
<a-button />

<!-- 编译后 -->
<a-input data-testid="a-input-0" />
<a-select data-testid="a-select-1" />
<a-button data-testid="a-button-2" />

teleport 组件(双注入)

<!-- 源码 -->
<a-date-picker />
<a-modal v-model:visible="show" title="提示" />

<!-- 编译后 -->
<a-date-picker id="a-date-picker-0" data-testid="a-date-picker-0" />
<a-modal id="a-modal-1" data-testid="a-modal-1" />

bodyCell 内组件

格式:${column.dataIndex}-${index}-${组件名}-${序号}

示例:
- label-0-a-input-4    (label 列,第 0 行)
- value-2-a-input-5    (value 列,第 2 行)

运行时 DatePicker 面板

a-date-picker-0-dropdown                    # 面板容器
a-date-picker-0-dropdown-prev               # 上一月
a-date-picker-0-dropdown-next               # 下一月
a-date-picker-0-dropdown-header-date        # header 容器
a-date-picker-0-dropdown-header-month-btn   # header 月份按钮
a-date-picker-0-dropdown-header-year-btn    # header 年份按钮
a-date-picker-0-dropdown-date-2026-06-15    # 日期单元格
a-date-picker-0-dropdown-ok                 # 确定按钮

切换到月面板 / 年面板后(MutationObserver 自动重注入):

a-date-picker-0-dropdown-header-month       # 月面板 header
a-date-picker-0-dropdown-month-06           # 6月
a-date-picker-0-dropdown-header-year        # 年面板 header
a-date-picker-0-dropdown-year-2026          # 2026年
a-date-picker-0-dropdown-decade-2020-2029   # 2020-2029 年代

运行时 RangePicker 面板

a-range-picker-0-dropdown-prev-left         # 左侧面板上一月
a-range-picker-0-dropdown-date-2026-05-15-left   # 左侧面板日期
a-range-picker-0-dropdown-date-2026-06-20-right  # 右侧面板日期

运行时 TimePicker

a-time-picker-0-dropdown-time-column-0      # 时列
a-time-picker-0-dropdown-time-08            # 08时
a-time-picker-0-dropdown-time-column-1      # 分列
a-time-picker-0-dropdown-time-30            # 30分

运行时 Cascader

a-cascader-0-dropdown                       # 面板容器
a-cascader-0-dropdown-menu-0                # 第1级菜单
a-cascader-0-dropdown-item-浙江             # 浙江选项
a-cascader-0-dropdown-menu-1                # 第2级菜单
a-cascader-0-dropdown-item-杭州             # 杭州选项

运行时 Select

a-select-0-dropdown                         # 下拉容器
a-select-0-dropdown-option-选项1            # 选项
a-select-0-dropdown-option-选项2            # 选项

运行时 TreeSelect

a-tree-select-0-dropdown                          # 下拉容器
a-tree-select-0-dropdown-tree-root-1              # 根节点
a-tree-select-0-dropdown-tree-switcher-expand-root-1   # 展开图标(可展开状态)
a-tree-select-0-dropdown-tree-switcher-collapse-root-1 # 展开图标(已展开状态)
a-tree-select-0-dropdown-tree-parent-1            # 父节点(展开后注入)
a-tree-select-0-dropdown-tree-my-leaf             # 叶子节点

配置

编译期配置

import { createVueTestIdTransform } from 'vite-plugin-vue-testid'

createVueTestIdTransform({
  /**
   * 需要注入 testId 的组件标签列表(kebab-case)
   * @default 见下方「默认支持的组件」
   */
  components: ['a-input', 'a-select', 'my-button'],

  /**
   * 自定义 testId 生成函数
   * @param tagName  组件标签名
   * @param count    全局递增序号
   * @returns testId 字符串
   */
  generateId: (tagName, count) => `myapp-${tagName}-${count}`,

  /**
   * testId 属性名(可改为 data-cy 适配 Cypress)
   * @default 'data-testid'
   */
  attributeName: 'data-cy',

  /**
   * 需要同时注入 id + testId 的组件
   * @default ['a-date-picker', 'a-range-picker', 'a-time-picker', 'a-modal']
   */
  idComponents: ['a-date-picker', 'a-modal'],

  /**
   * id 属性名
   * @default 'id'
   */
  idAttributeName: 'id',
})

运行时配置

import { setupAntDropdownTestIds } from 'vite-plugin-vue-testid/runtime'

// 默认配置
const cleanup = setupAntDropdownTestIds()

// 自定义配置
const cleanup = setupAntDropdownTestIds({
  /**
   * testId 属性名,需与编译期配置一致
   * @default 'data-testid'
   */
  attributeName: 'data-testid',

  /**
   * 自定义面板选择器 → 注入策略映射
   * key: CSS 选择器
   * value: 注入函数,设为 undefined 可禁用某个默认面板
   */
  panels: {
    '.ant-picker-dropdown': undefined,                     // 禁用内置 Picker 面板注入
    '.my-custom-dropdown': (ctx) => {
      // ctx.container   — 面板容器 HTMLElement
      // ctx.trigger     — 触发器 HTMLElement | null
      // ctx.triggerTestId — 触发器的 testId 值
      // ctx.attrName    — testId 属性名
      const prefix = `${ctx.triggerTestId}-dropdown`
      ctx.container.setAttribute(ctx.attrName, prefix)
      // ... 自定义注入逻辑
    },
  },
})

// 组件卸载时清理 Observer
onUnmounted(cleanup)

一次性手动注入

import { injectCurrentDropdowns } from 'vite-plugin-vue-testid/runtime'

// SSR / hydration 场景下,不想用 MutationObserver 时直接调用
injectCurrentDropdowns({ attributeName: 'data-testid' })

默认支持的组件

| 组件 | 编译期注入 | 运行时注入 | |------|:---------:|:---------:| | a-input | ✅ | — | | a-textarea | ✅ | — | | a-input-number | ✅ | — | | a-select | ✅ | ✅ 下拉面板 | | a-cascader | ✅ | ✅ 级联面板 | | a-tree-select | ✅ | ✅ 树选择面板 | | a-radio-group | ✅ | — | | a-checkbox-group | ✅ | — | | a-date-picker | ✅ id+testid | ✅ 日期面板 | | a-range-picker | ✅ id+testid | ✅ 双面板 | | a-time-picker | ✅ id+testid | ✅ 时间面板 | | a-switch | ✅ | — | | a-slider | ✅ | — | | a-rate | ✅ | — | | a-upload | ✅ | — | | a-form-item | ✅ | — | | a-button | ✅ | — | | a-modal | ✅ id+testid | — |


自定义面板注入策略

如果需要支持其他 UI 库的弹出面板,可以自定义策略:

import type { PanelInjectStrategy } from 'vite-plugin-vue-testid/runtime'

// 示例:为 Element Plus 的弹出层注入 testId
const injectElDropdown: PanelInjectStrategy = (ctx) => {
  const { container, triggerTestId, attrName } = ctx
  const prefix = `${triggerTestId}-dropdown`

  container.setAttribute(attrName, prefix)

  container.querySelectorAll('.el-select-dropdown__item').forEach((item) => {
    const el = item as HTMLElement
    const text = el.textContent?.trim() || ''
    el.setAttribute(attrName, `${prefix}-option-${text}`)
  })
}

setupAntDropdownTestIds({
  panels: {
    '.el-select-dropdown': injectElDropdown,
    '.el-cascader-dropdown': injectElDropdown,
  },
})

API 参考

编译期

| 导出 | 类型 | 说明 | |------|------|------| | createVueTestIdTransform(opts?) | (node: RootNode \| TemplateChildNode) => void | 创建 Vue 编译器 nodeTransform | | VueTestIdTransformOptions | interface | 编译期配置类型 |

运行时(/runtime 子路径)

| 导出 | 类型 | 说明 | |------|------|------| | setupAntDropdownTestIds(opts?) | () => void | 启动 MutationObserver,自动注入弹出面板 testId,返回清理函数 | | injectCurrentDropdowns(opts?) | void | 一次性手动注入当前 DOM 中已有的弹出面板 | | RuntimeOptions | interface | 运行时配置类型 | | PanelContext | interface | 面板注入上下文类型 | | PanelInjectStrategy | (ctx: PanelContext) => void | 面板注入策略函数类型 |


原理

编译期

通过 Vue 编译器的 nodeTransform 钩子在 AST 阶段遍历模板节点,匹配目标组件标签后直接追加 data-testid 属性节点。由于是编译期注入,对运行时没有性能影响。

运行时

ant-design-vue 4.x 的 DatePicker / Select / Cascader 等组件使用 Teleport 将弹出层渲染到 document.body,编译期无法触及这些 DOM。运行时模块通过 MutationObserver 监听 body 下的 DOM 变化,匹配 ant-design 面板的 CSS class,自动为面板内元素注入与触发器关联的 testId。

对于 DatePicker 这种 inheritAttrs: false 的组件,data-testid 在编译期注入后无法到达 DOM。插件通过同时注入 id 属性(该属性被 ant-design-vue 显式转发到内部 <input>)作为兜底标识。


License

MIT