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

vant-testid-webpack-plugin

v1.0.13

Published

Webpack 4 plugin to auto-inject data-testid into Vant 2 (Vue 2) component DOM for E2E testing

Readme

vant-testid-webpack-plugin

English

一个 Webpack 4 插件,自动为 Vant 2 UI 组件的 DOM 元素注入 data-testid 属性,使 Playwright、Cypress、Testing Library 等 E2E 测试工具的选择器稳定可靠、维护成本低。

兼容: Vue 2.6+ · Vant 2.x · Webpack 4.x

背景动机

在大型 Vue 2 应用中,为每个 Vant 组件手动添加 data-testid 既繁琐又容易出错。随着 UI 迭代变化,选择器容易失效,测试变得不稳定。本插件通过两层注入解决该问题:

  • 编译期: 一个 vue-template-compiler 模块生成稳定的、模板范围内唯一且可复现的 testid(van-button-0van-field-1...)。
  • 运行时: 基于 MutationObserver 的注入器可以覆盖编译期变换无法处理的弹出面板、传送门、Portal 和子元素(ActionSheet 选项、Picker 列、Slider 滑块等)。

安装

pnpm add -D vant-testid-webpack-plugin
# or
npm install -D vant-testid-webpack-plugin
# or
yarn add -D vant-testid-webpack-plugin

Peer 依赖: vue >=2.6.0, webpack >=4.0.0

快速开始

webpack.config.js 用户

推荐用法:编译器 + Vue 插件 + 运行时

这种方式提供稳定的 testid,不随渲染顺序变化,同时拥有完整的运行时覆盖。

// webpack.config.js
const { VantTestIdWebpackPlugin, createCompilerModule } = require('vant-testid-webpack-plugin')

module.exports = {
  module: {
    rules: [{
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        compilerOptions: {
          modules: [createCompilerModule()]
        }
      }
    }]
  },
  plugins: [new VantTestIdWebpackPlugin({ debug: false })]
}
// main.js
import Vue from 'vue'
import { createVue2TestIdBridge } from 'vant-testid-webpack-plugin/vue-plugin'
import { setupVantTestIds } from 'vant-testid-webpack-plugin/runtime'
import App from './App.vue'

Vue.use(createVue2TestIdBridge())   // 编译器 testid → DOM
new Vue({ render: h => h(App) }).$mount('#app')
setupVantTestIds()                   // 面板 / 子元素 / 兜底

仅运行时

最简单的配置 — 零模板配置,但 testid 由运行时全局计数器生成。

// webpack.config.js
const { VantTestIdWebpackPlugin } = require('vant-testid-webpack-plugin')

module.exports = {
  plugins: [new VantTestIdWebpackPlugin()]
}
// main.js
import Vue from 'vue'
import { setupVantTestIds } from 'vant-testid-webpack-plugin/runtime'
import App from './App.vue'

new Vue({ render: h => h(App) }).$mount('#app')
setupVantTestIds()

仅编译时

无运行时性能开销,但弹出面板/对话框和子元素不会被注入 testid。

// webpack.config.js
const { VantTestIdWebpackPlugin, createCompilerModule } = require('vant-testid-webpack-plugin')

module.exports = {
  module: {
    rules: [{
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        compilerOptions: {
          modules: [createCompilerModule()]
        }
      }
    }]
  },
  plugins: [new VantTestIdWebpackPlugin()]
}
// main.js
import Vue from 'vue'
import { createVue2TestIdBridge } from 'vant-testid-webpack-plugin/vue-plugin'
import App from './App.vue'

Vue.use(createVue2TestIdBridge())
new Vue({ render: h => h(App) }).$mount('#app')

Vue CLI 用户(vue.config.js)

Vue CLI 项目使用 vue.config.js 而非 webpack.config.js。通过 configureWebpackchainWebpack 配置插件。

Vue CLI:推荐用法(编译器 + Vue 插件 + 运行时)

// vue.config.js
const { VantTestIdWebpackPlugin, createCompilerModule } = require('vant-testid-webpack-plugin')

module.exports = {
  configureWebpack: {
    plugins: [new VantTestIdWebpackPlugin({ debug: false })]
  },
  chainWebpack(config) {
    // 将编译器模块注入 vue-loader
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => {
        options.compilerOptions = options.compilerOptions || {}
        options.compilerOptions.modules = [
          ...(options.compilerOptions.modules || []),
          createCompilerModule()
        ]
        return options
      })
  }
}
// main.js(与 webpack.config.js 版本相同)
import Vue from 'vue'
import { createVue2TestIdBridge } from 'vant-testid-webpack-plugin/vue-plugin'
import { setupVantTestIds } from 'vant-testid-webpack-plugin/runtime'
import App from './App.vue'

Vue.use(createVue2TestIdBridge())
new Vue({ render: h => h(App) }).$mount('#app')
setupVantTestIds()

Vue CLI:仅运行时

// vue.config.js
const { VantTestIdWebpackPlugin } = require('vant-testid-webpack-plugin')

module.exports = {
  configureWebpack: {
    plugins: [new VantTestIdWebpackPlugin()]
  }
}
// main.js
import Vue from 'vue'
import { setupVantTestIds } from 'vant-testid-webpack-plugin/runtime'
import App from './App.vue'

new Vue({ render: h => h(App) }).$mount('#app')
setupVantTestIds()

Vue CLI:仅编译时

// vue.config.js
const { VantTestIdWebpackPlugin, createCompilerModule } = require('vant-testid-webpack-plugin')

module.exports = {
  configureWebpack: {
    plugins: [new VantTestIdWebpackPlugin()]
  },
  chainWebpack(config) {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options => {
        options.compilerOptions = options.compilerOptions || {}
        options.compilerOptions.modules = [
          ...(options.compilerOptions.modules || []),
          createCompilerModule()
        ]
        return options
      })
  }
}
// main.js
import Vue from 'vue'
import { createVue2TestIdBridge } from 'vant-testid-webpack-plugin/vue-plugin'
import App from './App.vue'

Vue.use(createVue2TestIdBridge())
new Vue({ render: h => h(App) }).$mount('#app')

API 文档

VantTestIdWebpackPlugin(options?)

Webpack 插件。将全局配置注入构建流程,用于存储配置和调试日志。

new VantTestIdWebpackPlugin({
  attributeName?: string        // 默认值: 'data-testid'
  prefixCls?: string            // 默认值: 'van'
  compileTime?: boolean         // 默认值: false
  compilerModuleOptions?: TransformModuleOptions
  components?: Record<string, string>  // 自定义 CSS 选择器 → testid 前缀映射
  debug?: boolean               // 默认值: false
})

createCompilerModule(options?)

返回一个 vue-template-compiler 模块(含 postTransformNode 钩子),用于编译期注入 testid。需添加到 vue-loadercompilerOptions.modules 数组中。

createCompilerModule({
  attributeName?: string   // 默认值: 'data-testid'
  vantPrefix?: string      // 默认值: 'van-'
  customPrefixes?: string[] // 额外的自定义组件标签前缀
})

createVue2TestIdBridge(options?)

Vue 2 插件,在组件 mounted 时读取 vnode.data.attrs 中编译期注入的 testid,并手动写入组件的根 DOM 元素。使用编译期变换时必须同时注册此插件,因为部分 Vant 组件使用了 inheritAttrs: false

Vue.use(createVue2TestIdBridge({
  attributeName?: string  // 默认值: 'data-testid'
}))

setupVantTestIds(options?)

启动基于 MutationObserver 的运行时注入器。返回一个清理函数。

const stop = setupVantTestIds({
  attributeName?: string    // 默认值: 'data-testid'
  prefixCls?: string        // 默认值: 'van'
  components?: Record<string, string>  // 自定义 CSS 选择器 → testid 前缀映射
  panels?: Record<string, PanelInjectStrategy | undefined>
})

// 需要时停止监听
stop()

injectCurrentPanels(options?)

一次性为当前已存在或即将出现的面板注入 testId。适用于 SSR/hydration 场景,或不想使用 MutationObserver 的场景。

import { injectCurrentPanels } from 'vant-testid-webpack-plugin/runtime'

// 在弹窗/面板打开后调用
injectCurrentPanels()

工作原理

编译期变换

在 Vue SFC 编译期间,createCompilerModule() 遍历模板 AST,为找到的每个 Vant 组件标签注入 data-testid="{tagName}-{index}"。所有模板共享同一个全局计数器,确保 testid 全局唯一。

<!-- 源码 -->
<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 插件桥接

createVue2TestIdBridge() 通过全局 mounted mixin,从 vm.$vnode.data.attrs 中读取编译期注入的 testid,并显式调用 el.setAttribute() 写入 DOM。这使得 van-popupvan-dialogvan-pickerinheritAttrs: false 的组件也能正确带上 testid。

运行时注入器

setupVantTestIds() 使用 MutationObserver 监听 DOM 变化。当检测到新的 Vant 组件根元素时,使用计数器注入 testid。此外还会:

  • 注入子元素:输入框控件、步进器按钮、滑块手柄、评分星星、标签页、上传预览等。
  • 注入弹出面板:Picker/DatetimePicker/Area 的工具栏、ActionSheet 选项、ShareSheet 选项、Dialog 按钮等。
  • 为编译期变换遗漏的组件提供兜底注入。

运行时 Testid 覆盖范围

组件根元素(60+ 组件)

每个 Vant 2 组件根元素都会获得 van-{组件名}-{序号} 格式的 testid:

van-fieldvan-searchvan-password-inputvan-number-keyboardvan-steppervan-switchvan-slidervan-ratevan-uploadervan-checkboxvan-checkbox-groupvan-radiovan-radio-groupvan-buttonvan-popupvan-dialogvan-action-sheetvan-share-sheetvan-notifyvan-overlayvan-dropdown-menuvan-pickervan-datetime-pickervan-areavan-tabsvan-nav-barvan-tabbarvan-sidebarvan-index-barvan-cell-groupvan-cellvan-cardvan-tagvan-collapsevan-imagevan-image-previewvan-progressvan-circlevan-stepsvan-dividervan-calendarvan-panelvan-loadingvan-skeletonvan-emptyvan-listvan-gridvan-swipevan-swipe-cellvan-couponvan-coupon-listvan-coupon-cellvan-submit-barvan-goods-actionvan-contact-cardvan-contact-listvan-contact-editvan-address-listvan-address-editvan-tree-selectvan-pull-refreshvan-notice-barvan-lazyload

子元素

| 组件 | 子元素 | 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-input | | Search | 清除按钮 | {testId}-clear | | Search | 操作按钮 | {testId}-action-{按钮文本} | | Search | 标签文本 | {testId}-label | | Stepper | 减号按钮 | {testId}-minus | | Stepper | 加号按钮 | {testId}-plus | | Stepper | 输入框 | {testId}-input | | Switch | 节点元素 | {testId}-node | | Switch | 加载中 | {testId}-loading | | Slider | 滑块按钮 | {testId}-button-{i} | | Slider | 进度条 | {testId}-bar | | Rate | 星星图标 | {testId}-star-{i} | | Tabs | 标签导航项 | {testId}-tab-{i}-{标签文本} | | Tabs | 内容面板 | {testId}-pane-{i} | | Tabs | 导航容器 | {testId}-wrap | | Uploader | 文件输入框 | {testId}-input | | Uploader | 预览项 | {testId}-preview-{i} | | Uploader | 删除按钮 | {testId}-preview-delete-{i} | | Uploader | 预览遮罩 | {testId}-preview-cover-{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 | | NavBar | 文本 | {testId}-text | | Card | 底部区域 | {testId}-footer | | Card | 缩略图 | {testId}-thumb | | Card | 标签 | {testId}-tag | | Card | 描述 | {testId}-desc | | Collapse | 折叠项 | {testId}-item-{i}-{标题} | | Collapse | 折叠包装器 | {testId}-item-{i}-wrapper | | Collapse | 折叠内容 | {testId}-item-{i}-content | | Swipe | 轮播项 | {testId}-item-{i} | | Swipe | 指示器 | {testId}-indicator-{i} | | SwipeCell | 右侧操作 | {testId}-right-{i} | | SwipeCell | 左侧操作 | {testId}-left-{i} | | GoodsAction | 图标按钮 | {testId}-icon-{i}-{文本} | | GoodsAction | 按钮 | {testId}-button-{i}-{文本} | | SubmitBar | 价格 | {testId}-price | | SubmitBar | 按钮 | {testId}-button-{文本} | | Grid | 宫格项 | {testId}-item-{i}-{文本} | | Panel | 头部 | {testId}-header | | Panel | 底部 | {testId}-footer | | NoticeBar | 左图标 | {testId}-left-icon | | NoticeBar | 右图标 | {testId}-right-icon | | Calendar | 头部标题 | {testId}-header-title | | Calendar | 头部副标题 | {testId}-header-subtitle | | Calendar | 月份标题 | {testId}-month-{i}-{文本} | | Calendar | 星期 | {testId}-month-{i}-weekday-{w} | | Calendar | 日期格子 | {testId}-month-{i}-day-{日期} | | Calendar | 确定按钮 | {testId}-confirm | | Picker | 取消按钮 | {testId}-cancel | | Picker | 确定按钮 | {testId}-confirm | | Picker | 标题 | {testId}-title | | Picker | 列 | {testId}-column-{i} | | Picker | 列选项 | {testId}-column-{i}-item-{文本} |

弹出面板

在 popup/portal 中渲染的 Picker、ActionSheet、ShareSheet、DropdownMenu 和 Dialog 都会自动注入 testid,覆盖它们的工具栏、列、选项和操作按钮。

调试

在浏览器控制台中设置 globalThis.VANT_TESTID_DEBUG = true 可查看 testid 注入日志:

globalThis.VANT_TESTID_DEBUG = true

或在插件构造函数中开启调试模式:

new VantTestIdWebpackPlugin({ debug: true })

与 Vite 版对比

| | Vite 插件 | Webpack 插件 | |---|---|---| | Vue 版本 | Vue 3.3+ | Vue 2.6+ | | UI 库 | Vant 4 | Vant 2 | | 编译器 | @vue/compiler-core (nodeTransforms) | vue-template-compiler (modules) | | Vue 插件 | app.mixin + getCurrentInstance | Vue.mixin + $vnode.data.attrs | | 输出格式 | ESM | CJS |

许可证

MIT