vite-plugin-vant-testid
v1.0.2
Published
Vite plugin to auto-inject data-testid into Vue component templates for E2E testing with Vant UI
Maintainers
Readme
vite-plugin-vant-testid
一个 Vite 插件,自动为 Vant UI 组件的 DOM 元素注入 data-testid 属性,让 Playwright、Cypress、Testing Library 等 E2E 测试工具的选择器稳定可靠、零维护。
动机
在大型 Vue 应用中为每个 Vant 组件手动添加 data-testid 既繁琐又容易出错。随着 UI 迭代,选择器经常失效,测试变得不稳定。本插件通过两层自动注入解决此问题:
- 编译期:Vue 模板编译器 transform 生成稳定的、模板作用域内的 testid(如
van-button-0、van-field-1)。 - 运行时:基于 MutationObserver 的注入器为弹出层面板、Teleport 渲染的组件以及子元素补注入 testid,覆盖编译期无法触及的场景。
安装
pnpm add -D vite-plugin-vant-testid
# 或
npm install -D vite-plugin-vant-testid
# 或
yarn add -D vite-plugin-vant-testidPeer dependencies:vite >=5.0.0,vue >=3.3.0,vant >=4.0.0
快速上手
推荐方案:编译器 + Vue 插件 + 运行时
此方案能生成稳定的 testid(重渲染不变),同时拥有完整的运行时覆盖。
// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { vueTestIdPlugin, testIdTransforms } from 'vite-plugin-vant-testid'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
nodeTransforms: testIdTransforms(),
},
},
}),
vueTestIdPlugin(),
],
})// main.ts
import { createApp } from 'vue'
import { createTestIdBridge } from 'vite-plugin-vant-testid/plugin'
import { setupVantTestIds } from 'vite-plugin-vant-testid/runtime'
import App from './App.vue'
const app = createApp(App)
app.use(createTestIdBridge()) // 编译器 testid → DOM 写入
setupVantTestIds() // 面板、子元素、兜底注入
app.mount('#app')纯运行时
最简单的方式,零配置,但 testid 由运行时全局计数器生成。
// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { vueTestIdPlugin } from 'vite-plugin-vant-testid'
export default defineConfig({
plugins: [vue(), vueTestIdPlugin()],
})// main.ts
import { setupVantTestIds } from 'vite-plugin-vant-testid/runtime'
createApp(App).mount('#app')
setupVantTestIds()仅编译期
无运行时开销,但弹出层/对话框面板及子元素不会被注入 testid。
// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { vueTestIdPlugin, testIdTransforms } from 'vite-plugin-vant-testid'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
nodeTransforms: testIdTransforms(),
},
},
}),
vueTestIdPlugin(),
],
})// main.ts
import { createApp } from 'vue'
import { createTestIdBridge } from 'vite-plugin-vant-testid/plugin'
import App from './App.vue'
const app = createApp(App)
app.use(createTestIdBridge())
app.mount('#app')API 参考
testIdTransforms(options?)
返回一组 Vue 编译器 NodeTransform,需注册到 vue() 的 compilerOptions 中。
testIdTransforms({
attributeName?: string // 默认:'data-testid'
vantPrefix?: string // 默认:'van-'
customPrefixes?: string[] // 额外的组件前缀
})vueTestIdPlugin(options?)
Vite 插件占位符。目前为空操作,实际的 testid 注入由 testIdTransforms() 和 setupVantTestIds() 完成。
vueTestIdPlugin({
attributeName?: string // 默认:'data-testid'
})createTestIdBridge(options?)
Vue 插件,从 VNode props 中读取编译期 testid 并写入组件的根 DOM 元素。使用编译期 transform 时必须引入,因为部分 Vant 组件使用了 inheritAttrs: false 或通过 Teleport 渲染。
createTestIdBridge({
attributeName?: string // 默认:'data-testid'
})setupVantTestIds(options?)
启动基于 MutationObserver 的运行时注入器。返回清理函数。
const stop = setupVantTestIds({
attributeName?: string // 默认:'data-testid'
prefixCls?: string // 默认:'van'
components?: Record<string, string> // 自定义 CSS 选择器 → 前缀映射
panels?: Record<string, PanelInjectStrategy | undefined>
})
// 停止监听
stop()injectCurrentPanels(options?)
一次性注入,适用于已存在或即将出现的面板。适合 SSR/hydration 或不想使用 MutationObserver 的场景。
import { injectCurrentPanels } from 'vite-plugin-vant-testid/runtime'
// 在弹窗/弹出层打开后调用
injectCurrentPanels()工作原理
编译期 transform
在 Vue SFC 编译期间,testIdTransforms() 遍历模板 AST,为每个 Vant 组件标签注入 data-testid="{tagName}-{index}"。每个模板独立计数,按组件类型递增。
<!-- 源码 -->
<van-button type="primary">提交</van-button>
<van-field v-model="name" placeholder="请输入姓名" />
<!-- 编译后 -->
<van-button type="primary" data-testid="van-button-0">提交</van-button>
<van-field v-model="name" data-testid="van-field-0" placeholder="请输入姓名" />Vue 插件桥接
createTestIdBridge() 使用全局 mounted mixin,从 VNode props 中读取 data-testid 并显式调用 el.setAttribute()。这绕过了 van-popup、van-dialog、van-picker 等组件中 inheritAttrs: false 和 Teleport 的限制。
运行时注入
setupVantTestIds() 通过 MutationObserver 监听 DOM 变化。当检测到新的 Vant 组件根元素时,使用计数器注入 testid。同时处理:
- 子元素注入:field 输入框、stepper 加减按钮、slider 滑块、rate 星星、tabs 标签/面板等。
- 弹出层面板注入:Picker/DatetimePicker/Area 工具栏、ActionSheet 选项、ShareSheet 分享项、Dialog 按钮。
- 兜底注入:编译期 transform 遗漏的组件。
运行时 Testid 覆盖范围
组件根元素
每个 Vant 组件的根元素都会获得 van-{组件名}-{n} 格式的 testid:
van-button、van-field、van-search、van-stepper、van-switch、van-slider、van-rate、van-uploader、van-checkbox、van-radio、van-picker、van-datetime-picker、van-area、van-popup、van-dialog、van-action-sheet、van-share-sheet、van-notify、van-overlay、van-dropdown-menu、van-tabs、van-nav-bar、van-tabbar、van-sidebar、van-index-bar、van-cell、van-cell-group、van-card、van-tag、van-collapse、van-image、van-image-preview、van-progress、van-circle、van-steps、van-divider、van-loading、van-skeleton、van-empty、van-result、van-list、van-grid、van-swipe、van-swipe-cell、van-password-input、van-coupon、van-coupon-list、van-submit-bar、van-goods-action、van-contact-card、van-contact-list、van-address-list、van-address-edit、van-action-bar、van-tree-select、van-pull-refresh、van-back-top
子元素
| 组件 | 子元素 | testid 格式 |
|-----------|-------------|----------------|
| Field | 输入框/文本域 | {testId}-control |
| Field | 清除按钮 | {testId}-clear |
| Field | 左侧图标 | {testId}-left-icon |
| Field | 右侧图标 | {testId}-right-icon |
| Field | 错误信息 | {testId}-error |
| Field | 字数统计 | {testId}-word-limit |
| Search | 搜索图标 | {testId}-search-icon |
| Search | 输入框 | {testId}-search-input |
| Search | 清除按钮 | {testId}-clear |
| Search | 操作按钮 | {testId}-action-{文本} |
| Stepper | 减号按钮 | {testId}-minus |
| Stepper | 加号按钮 | {testId}-plus |
| Stepper | 输入框 | {testId}-input |
| Slider | 滑块按钮 | {testId}-button-{i} |
| Rate | 星星图标 | {testId}-star-{i} |
| Tabs | 标签项 | {testId}-tab-{i}-{文本} |
| Tabs | 内容面板 | {testId}-pane-{i} |
| Uploader | 文件输入 | {testId}-input |
| Uploader | 预览项 | {testId}-preview-{i} |
| Uploader | 删除按钮 | {testId}-preview-delete-{i} |
| Uploader | 上传按钮 | {testId}-upload-btn |
| Steps | 步骤项 | {testId}-step-{i}-{标题} |
| PasswordInput | 密码圆点 | {testId}-item-{i} |
| PasswordInput | 光标 | {testId}-cursor |
| Tabbar | 标签项 | {testId}-item-{i}-{文本} |
| Sidebar | 侧边项 | {testId}-item-{i}-{文本} |
| IndexBar | 锚点 | {testId}-anchor-{索引} |
| IndexBar | 侧边索引 | {testId}-index-{文本} |
| DropdownMenu | 下拉项 | {testId}-item-{i}-{标题} |
| NavBar | 左侧区域 | {testId}-left |
| NavBar | 右侧区域 | {testId}-right |
| NavBar | 标题 | {testId}-title |
| Card | 底部区域 | {testId}-footer |
| Card | 缩略图 | {testId}-thumb |
| Collapse | 折叠项 | {testId}-item-{i}-{标题} |
| Swipe | 滑动项 | {testId}-item-{i} |
| Swipe | 指示器 | {testId}-indicator-{i} |
| SwipeCell | 右侧操作 | {testId}-right-{i} |
| ActionBar | 图标按钮 | {testId}-icon-{i}-{文本} |
| ActionBar | 按钮 | {testId}-button-{i}-{文本} |
| Grid | 宫格项 | {testId}-item-{i}-{文本} |
弹出层面板
渲染在 popup/portal 中的 Picker、ActionSheet、ShareSheet、DropdownMenu、Dialog 等组件会自动注入 testid,包括其工具栏、列、选项和操作按钮。
调试
在浏览器控制台中设置 window.__VANT_TESTID_DEBUG = true 即可看到 testid 注入日志:
window.__VANT_TESTID_DEBUG = trueLicense
MIT
