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-vue3-testid

v2.0.0

Published

Vite plugin to auto-inject data-testid into ant-design-vue (Vue 3) component DOM for E2E testing

Readme

vite-plugin-vue3-testid

English

为 Vue 3 所有 DOM 元素自动注入 data-testid 属性 — UI 库无关。适用于任何 Vue 3 项目,无论使用 Ant Design Vue、Element Plus、Vant、Naive UI 还是纯 HTML。

特性

  • UI 库无关 — 对所有可见 DOM 元素注入 testid,无需组件前缀匹配
  • 编译期三层计数器架构 — 优先级计数器确保 testid 稳定唯一:
    • v-for 动态注入(最高优先级)— 利用循环 index 变量生成每轮迭代唯一 testid,无需运行时去重
    • 条件块子计数器(第二优先级)— v-if/v-else/v-show 拥有独立子计数器,子元素以父 testid 为前缀,增删不影响外部稳定性
    • 全局计数器(第三优先级)— 跨模板共享计数器 + usedId Set,保证全局唯一
  • 三层注入系统 — 编译期 transform + Vue 插件桥接 + 运行时 MutationObserver 兜底
  • 丰富的元素分类v-for、事件监听、条件块、普通元素各有专用格式
  • 全局唯一 — 编译期全局计数器 + 已用 ID Set + 运行时去重,确保每个 DOM 元素都有唯一的 testid
  • 可配置前缀testIdPrefix 选项为所有编译期 testid 拼接公共前缀
  • 运行时去重 — 检测 v-for、UI 库 slot 重复渲染等导致的重复 testid,自动追加 :N 后缀
  • 幂等注入 — 所有注入层在注入前检查元素是否已有 testid,防止重复
  • Teleport 支持 — 运行时层覆盖通过 <Teleport> 或 UI 库弹窗/下拉框渲染到 <body> 的元素
  • 零运行时依赖 — 运行时无额外依赖;@vue/compiler-core 仅作为 devDependency 用于类型定义

注入格式

编译期(三层注入)

| 优先级 | 层级 | 格式 | 示例 | |--------|------|------|------| | 1st(最高) | v-for 含 index | {tag}_in_for-{i0}[_{i1}] | li_in_for-0_1 | | 2nd | 条件块子元素 | {parentTestId}__{tag}-{n} | div-1__span-0 | | 3rd | 全局计数器 | {tag}-{n} | span-2, h1-3 |

v-for 动态注入:

<!-- 源码 -->
<li v-for="(item, i) in list" :key="i">...</li>

<!-- 编译后(动态表达式,每轮迭代 index 自动解析) -->
<li v-for="(item, i) in list" :key="i" :data-testid="`li_in_for-${i}`">...</li>

<!-- 无 index 变量 → 静态后缀 -->
<li v-for="item in list" :key="item.id" data-testid="li-in_for-0">...</li>

条件块子计数器:

<!-- 各条件分支独立子计数器 -->
<div v-if="visible" data-testid="div-0">
  <span data-testid="div-0__span-0" />
  <button data-testid="div-0__button-event-click-0">提交</button>
</div>
<div v-else data-testid="div-1">
  <span data-testid="div-1__span-0" />
</div>

v-if/v-else/v-show 内部子元素使用 {parentTestId}__{tag}-{n} 格式,确保:

  • testid 稳定 — 父元素数量变化不影响子元素编号
  • 可预测 — 测试人员可通过 parent→child 链式定位嵌套元素

事件监听标记:

{tag}-event-{names}-{n}
例如:button-event-click-0, input-event-input_focus-1

运行时去重: 编译期每个模板 AST 节点只能生成一个 testid。当同一节点在运行时被多次实例化(v-for 无 index、UI 库 slot 如 a-tablebodyCell 等),运行时 MutationObserver 检测到重复 testid 后自动追加 :2:3、... 后缀。

安装

pnpm add -D vite-plugin-vue3-testid
# 或
npm install --save-dev vite-plugin-vue3-testid

使用方式

方式 B:编译器 + Vue 插件 + 运行时(推荐)

全量覆盖:编译期用三层架构注入稳定 testid,运行时处理动态/teleport 元素。

// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { vueTestIdPlugin, testIdTransforms } from 'vite-plugin-vue3-testid'

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          nodeTransforms: testIdTransforms({
            testIdPrefix: '',     // 可选:公共前缀
            debug: false,         // 可选:调试日志
          })
        }
      }
    }),
    vueTestIdPlugin({ debug: false }),
  ]
})
// main.ts
import { createTestIdBridge } from 'vite-plugin-vue3-testid/plugin'
import { setupTestIds } from 'vite-plugin-vue3-testid/runtime'

const app = createApp(App)
app.use(createTestIdBridge())  // 将编译期 testid 桥接到 DOM
app.mount('#app')
setupTestIds()                 // 运行时兜底:动态元素 / teleport / 去重

方式 C:仅编译期(轻量)

无运行时开销。动态元素(teleport 弹窗、下拉框等)不会注入 testid。

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

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          nodeTransforms: testIdTransforms()
        }
      }
    })
  ]
})
// main.ts
import { createTestIdBridge } from 'vite-plugin-vue3-testid/plugin'

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

方式 A:纯运行时(无需修改 vite 配置)

无需修改 vite.config.ts,所有 testid 通过 MutationObserver 在运行时生成。

// main.ts
import { setupTestIds } from 'vite-plugin-vue3-testid/runtime'

createApp(App).mount('#app')
setupTestIds()

架构:三层注入

┌──────────────────────────────────────────────────────┐
│ 1. 编译期 Transform(最高优先级)                     │
│    Vue 模板 AST → 三层计数器注入 testid               │
│    - v-for+index:动态 JS 表达式                      │
│    - 条件块内部:{父testid}__{tag}-{n}               │
│    - 全局元素:{tag}-{n}                              │
│    跨模板全局计数器 + 已用 ID Set                     │
├──────────────────────────────────────────────────────┤
│ 2. Vue 插件桥接                                      │
│    app.mixin({ mounted() })                          │
│    读取 vnode.props['data-testid'] → 写入 $el        │
│    绕过 inheritAttrs: false                          │
├──────────────────────────────────────────────────────┤
│ 3. 运行时 MutationObserver(兜底)                    │
│    监听 document.body 的 DOM 变化                     │
│    处理动态/teleport/第三方 DOM                       │
│    检测重复 testid → 追加 :N 后缀                     │
└──────────────────────────────────────────────────────┘

三层均为幂等注入 — 在注入前检查元素是否已有 data-testid 属性。

API 参考

testIdTransforms(options?)

返回编译期注入所需的 Vue 3 NodeTransform 函数数组。

import { testIdTransforms } from 'vite-plugin-vue3-testid'

// 默认配置
testIdTransforms()
testIdTransforms({
  attributeName: 'data-testid',
  testIdPrefix: '',
  injectForLoops: true,
  injectEventElements: true,
  debug: false,
})

参数:

| 参数 | 类型 | 默认值 | 说明 | |---|---|---|---| | attributeName | string | 'data-testid' | 自定义 testid 属性名 | | testIdPrefix | string | '' | 所有编译期 testid 的公共前缀,如 'static-'static-div-0 | | injectForLoops | boolean | true | 启用 v-for 动态注入(需要 index 变量);无 index 时回退为 -in_for- 后缀 | | injectEventElements | boolean | true | 带 @click/v-on 元素使用特殊格式 {tag}-event-{names}-{n} | | debug | boolean | false | 开启编译期调试日志 |

createTestIdBridge(options?)

创建 Vue 3 插件,将编译期 testid 从 vnode.props 桥接到 DOM 元素。

import { createTestIdBridge } from 'vite-plugin-vue3-testid/plugin'

const app = createApp(App)
app.use(createTestIdBridge({ attributeName: 'data-testid' }))

参数:

| 参数 | 类型 | 默认值 | 说明 | |---|---|---|---| | attributeName | string | 'data-testid' | 必须与编译期属性名一致 |

setupTestIds(options?)

启动基于 MutationObserver 的运行时 testid 注入与去重。返回清理函数。

注入:对编译期未覆盖的元素(动态内容、teleport、第三方 DOM)注入 {tag}-{n} 格式的 testid。

去重:扫描 DOM 中所有已有的 testid。当同一 testid 出现在多个元素上时(由 v-for 无 index、UI 库 slot 克隆等导致),自动追加 :2:3、... 后缀使每个实例唯一。第一个出现的实例保持原值不变。

import { setupTestIds } from 'vite-plugin-vue3-testid/runtime'

const cleanup = setupTestIds()
// ... 需要停止监听时:
cleanup()

参数:

| 参数 | 类型 | 默认值 | 说明 | |---|---|---|---| | attributeName | string | 'data-testid' | 自定义 testid 属性名 | | debug | boolean | false | 开启控制台调试日志 | | dedupOnly | boolean | false | 仅去重模式:不注入新 testid,仅处理已有 testid 的重复(跨环境确定性) |

vueTestIdPlugin(options?)

Vite 插件。作为配置占位符存在;实际注入通过 testIdTransforms()(编译期)和 setupTestIds()(运行时)完成。

import { vueTestIdPlugin } from 'vite-plugin-vue3-testid'

vueTestIdPlugin({
  attributeName: 'data-testid',
  testIdPrefix: '',
  debug: false,
})

为什么需要 Vue 插件桥接?

Vue 的 inheritAttrs: false(UI 库组件常见)会阻止非 props 属性传递到 DOM。UI 组件也可能通过 useAttrs() 消费 testid,将其从 $attrs 中移除。桥接插件在 mounted 生命周期直接从 vnode.props 读取 testid,调用 el.setAttribute() 写入 DOM,确保 testid 始终到达目标元素。

调试模式

开启调试日志,查看 testid 注入过程:

// 运行时调试
setupTestIds({ debug: true })

// 编译期调试
testIdTransforms({ debug: true })

// 桥接层调试 — 在 mount 前全局设置
;(window as any).__TESTID_DEBUG = true

跳过的元素

  • templateslot 元素(纯容器,不渲染为独立 DOM)
  • 已有 data-testid 属性的元素(用户手动设置或已被注入)
  • 运行时模式下的非可视化元素:stylescriptmetalinktitleheadhtmlbodynoscriptbrhr

与 Monorepo 中其他包的对比

| 包 | 范围 | Vue 版本 | |---|---|---| | vite-plugin-vue3-testid | 所有元素,UI 库无关 | Vue 3 | | vite-plugin-vue-testid | 仅 Ant Design Vue | Vue 3 | | vite-plugin-vant-testid | 仅 Vant 4 | Vue 3 | | vite-plugin-element-testid | 仅 Element Plus | Vue 3 | | vue-testid-core | Vant 2(通用引擎) | Vue 2 | | vant-testid-webpack-plugin | Vant 2(Webpack 4) | Vue 2 |

License

MIT