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

rn-testid-babel-plugin

v1.0.1

Published

Babel plugin to auto-inject testID prop into React Native JSX elements for E2E testing (Detox, Appium, Maestro)

Readme

rn-testid-babel-plugin

npm version License: MIT

自动为 React Native JSX 元素注入 testID prop 的 Babel 插件,用于 E2E 测试(Detox、Appium、Maestro)。

特性

  • 零配置:默认自动为所有 React Native 内置组件注入 testID
  • 自定义 UI 库:支持配置组件名前缀(如 RNENBPaper
  • FlatList 感知:自动标记 renderItem 内部元素,支持运行时去重
  • 确定性的 ID:同一源码文件始终生成相同的 testID
  • 运行时兜底(可选):createElement monkey-patch 处理动态元素
  • TypeScript:完整类型定义

安装

npm install --save-dev rn-testid-babel-plugin
# 或
pnpm add -D rn-testid-babel-plugin

快速开始

Expo 项目

babel.config.js 中添加插件:

module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],  // Expo 默认 preset
    plugins: [
      'rn-testid-babel-plugin',
    ],
  };
};

⚠️ 重要:修改 babel.config.js 后必须清除缓存:

npx expo start -c

裸 React Native 项目 (CLI)

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],  // RN CLI 默认 preset
  plugins: [
    'rn-testid-babel-plugin',
  ],
};

⚠️ 重要:修改 babel.config.js 后必须清除 Metro 缓存:

npx react-native start --reset-cache

或删除 node_modules/.cache 目录后重新启动。

搞定!所有 React Native 内置组件将自动获得 testID prop:

// 编译前
<View style={styles.container}>
  <Text>Hello World</Text>
  <TouchableOpacity onPress={handlePress}>
    <Text>Click me</Text>
  </TouchableOpacity>
</View>

// 编译后
<View testID="View-0" style={styles.container}>
  <Text testID="Text-1">Hello World</Text>
  <TouchableOpacity testID="TouchableOpacity-2" onPress={handlePress}>
    <Text testID="Text-3">Click me</Text>
  </TouchableOpacity>
</View>

配置自定义 UI 库前缀

module.exports = {
  plugins: [
    ['rn-testid-babel-plugin', {
      customPrefixes: ['RNE', 'NB', 'Paper'],
    }],
  ],
}

运行时兜底(可选)

// App.tsx — 必须在任何 React 组件之前导入!
import { setupRuntimeTestIDs } from 'rn-testid-babel-plugin/runtime'

if (__DEV__) {
  setupRuntimeTestIDs({ debug: true })
}

配置项

| 选项 | 类型 | 默认值 | 说明 | |--------|------|--------|------| | attrName | string | 'testID' | 自定义属性名 | | customPrefixes | string[] | [] | 额外组件名前缀 | | includeRNComponents | boolean | true | 是否包含 RN 内置组件 | | injectFlatListItems | boolean | true | 标记 FlatList renderItem 内部元素 | | exclude | (string \| RegExp)[] | [] | 跳过文件 | | include | (string \| RegExp)[] | [] | 仅处理文件 | | debug | boolean | false | 输出注入日志 | | formatTestID | function | — | 自定义 testID 格式 |

完整配置示例

module.exports = {
  plugins: [
    ['rn-testid-babel-plugin', {
      attrName: 'testID',
      includeRNComponents: true,
      injectFlatListItems: true,
      customPrefixes: ['RNE', 'NB', 'Paper'],
      exclude: [/\.test\.(tsx|jsx)$/, /__tests__/],
      debug: process.env.NODE_ENV !== 'production',
      formatTestID(tag, counter, meta) {
        const suffix = meta.isInFlatList ? '-flatlist-' : ''
        return `e2e-${tag}-${counter}${suffix}`
      },
    }],
  ],
}

testID 格式

| 场景 | 格式 | 示例 | |------|------|------| | RN 内置组件 | {Name}-{counter} | View-0, Text-5 | | 自定义前缀组件 | {PrefixName}-{counter} | RNEButton-3 | | FlatList renderItem | {Name}-{counter}-flatlist- | View-8-flatlist- | | FlatList 运行时去重 | {Name}-{counter}-flatlist-:{N} | View-8-flatlist-:0 | | 运行时注入(无编译期) | {Name}-runtime-{counter} | View-runtime-1 | | 用户手动指定 | 保持不变 | my-custom-id |

E2E 测试集成

Detox

await element(by.id('View-0')).tap()
await element(by.id('TouchableOpacity-2')).tap()

Appium

const el = await driver.elementById('View-0')
await el.click()

Maestro

- tapOn:
    id: "TouchableOpacity-2"

工作原理

详见 DESIGN.md

覆盖的 React Native 内置组件

View, Text, Image, ImageBackground, ScrollView, FlatList, SectionList, VirtualizedList, TextInput, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, TouchableNativeFeedback, Pressable, Button, Modal, ActivityIndicator, Switch, RefreshControl, SafeAreaView, KeyboardAvoidingView, StatusBar, Slider, Picker, DatePickerIOS, ProgressViewIOS, ProgressBarAndroid, DrawerLayoutAndroid

故障排查

看不到 testID?

1. 清除 Metro 缓存(最常见原因)

# Expo
npx expo start -c

# 裸 React Native CLI
npx react-native start --reset-cache

2. 确认插件已加载

babel.config.js 中临时开启 debug 模式:

plugins: [
  ['rn-testid-babel-plugin', { debug: true }],
],

重新构建后,终端会输出每个注入的日志:

[rn-testid] View → testID="View-0" (App.tsx:12)
[rn-testid] Text → testID="Text-1" (App.tsx:14)

3. 验证 JS Bundle 产物

搜索编译后的 bundle 文件确认 testID 字符串存在:

# Expo
npx expo export --platform ios
grep -o 'testID="View-' dist/_expo/static/js/ios/*.js | head

# 裸 React Native CLI (iOS)
npx react-native bundle --platform ios --dev false --entry-file index.js --bundle-output /tmp/bundle.js
grep -o 'testID="View-' /tmp/bundle.js | head

4. 在 React DevTools 中查看

组件树的右侧 Props 面板中应该显示 testID prop。注意 testID 是一个普通的 React prop,不是 HTML attribute,在 RN 的 Native 层中它被映射到 accessibilityIdentifier (iOS) / testID (Android)。

5. 检查文件是否被排除

确保你的组件文件没有匹配 exclude 选项中的模式。

Expo 特殊说明

  • Expo Go vs Development Build:两者都支持 Babel 插件,无需特殊处理
  • Monorepo:如果使用 pnpm/yarn workspaces,确保 rn-testid-babel-plugin 的软链接正确,可使用 npx expo start -c 重建依赖图

License

MIT