babel-plugin-react-native-testid
Version:
babel plugin for react native testid attributes
210 lines (163 loc) • 5.84 kB
Markdown
一个智能的Babel插件,为React Native组件自动生成testID属性,支持多种优先级策略,特别适用于自动化测试(如Detox)。
- 🎯 **智能优先级策略**: 按照预定义的优先级自动生成testID
- 🌐 **多语言支持**: 智能提取i18n翻译键作为testID
- 📱 **React Native优化**: 专为React Native组件设计
- ⚙️ **高度可配置**: 支持自定义属性、分隔符和忽略元素
- 🔄 **向后兼容**: 保持与原有testID的兼容性
**原则**: 保留人工干预的最高权力。如果组件已有testID或id,插件不会覆盖。
```jsx
// 输入
<Button testID="custom-login-button">登录</Button>
<Button id="custom-id">登录</Button>
// 输出 (保持不变)
<Button testID="custom-login-button">登录</Button>
<Button id="custom-id">登录</Button>
```
**原则**: 组件的功能通常由其props定义。
```jsx
// 输入
<TextInput placeholder="请输入用户名" />
<Button title="登录">Click</Button>
<Icon name="settings" />
// 输出
<TextInput placeholder="请输入用户名" testID="请输入用户名" id="请输入用户名" />
<Button title="登录" testID="登录" id="登录">Click</Button>
<Icon name="settings" testID="icon-settings" id="icon-settings" />
```
**支持的有意义属性**:
- `title` - 标题
- `placeholder` - 占位符
- `label` - 标签
- `alt` - 替代文本
- `name` - 名称(会添加组件类型前缀)
- `id` - 标识符
- `key` - 键值
- `accessibilityLabel` - 无障碍标签
**原则**: 组件的内容(children)是其最直观的语义。
```jsx
// 输入
<Button>登录</Button>
// 输出
<Button testID="登录" id="登录">登录</Button>
```
**关键优化**: 插件不执行t函数,而是直接提取翻译Key,这个Key是稳定且极具语义的。
```jsx
// 输入
<Text>{t('user.profile.nickname')}</Text>
<Button>{i18n.t('common.save')}</Button>
// 输出
<Text testID="user.profile.nickname" id="user.profile.nickname">{t('user.profile.nickname')}</Text>
<Button testID="common.save" id="common.save">{i18n.t('common.save')}</Button>
```
**原则**: 作为保底方案,确保所有元素至少有一个可用的ID。
```jsx
// 输入
function ProfileScreen() {
return (
<View>
<Button />
</View>
);
}
// 输出
function ProfileScreen() {
return (
<View>
<Button testID="ProfileScreen-Button" id="ProfileScreen-Button" />
</View>
);
}
```
```bash
npm install babel-plugin-react-native-testid --save-dev
yarn add babel-plugin-react-native-testid --dev
```
在你的`.babelrc`或`babel.config.js`中添加插件:
```json
{
"plugins": ["babel-plugin-react-native-testid"]
}
```
**默认情况下,插件会同时添加`testID`和`id`属性。**
```javascript
// babel.config.js
module.exports = {
plugins: [
[
'babel-plugin-react-native-testid',
{
// 要添加的属性名称
attributes: ['testID'],
// 层级分隔符
delimiter: '-',
// 忽略的元素(不会生成testID)
ignoreElements: ['View', 'Text', 'Image'],
// 有意义的属性
meaningfulAttributes: ['title', 'placeholder', 'label', 'customLabel']
}
]
]
};
```
| 选项 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `attributes` | `string[]` | `['testID', 'id']` | 要添加的属性名称 |
| `delimiter` | `string` | `'-'` | 层级分隔符 |
| `ignoreElements` | `string[]` | `['View', 'Text', 'Image', ...]` | 忽略的元素列表 |
| `meaningfulAttributes` | `string[]` | `['title', 'placeholder', ...]` | 有意义的属性列表 |
```javascript
// 测试代码
await element(by.id('请输入用户名')).typeText('testuser');
await element(by.id('user.profile.nickname')).tap();
await element(by.id('登录')).tap();
```
```jsx
// 组件代码
function LoginScreen() {
return (
<View>
<TextInput placeholder={t('login.username.placeholder')} />
<TextInput placeholder={t('login.password.placeholder')} />
<Button>{t('login.submit')}</Button>
</View>
);
}
// 生成的testID和id
// testID="login.username.placeholder" id="login.username.placeholder"
// testID="login.password.placeholder" id="login.password.placeholder"
// testID="login.submit" id="login.submit"
```
根据您提供的XML视图层次结构,这个插件可以帮助生成更有意义的testID:
```xml
<!-- 当前: 缺少testID -->
<RCTView alpha="1.0" class="RCTView" focused="false" height="30" id="EnhancedTouchable-Component" label="登录" visibility="visible" width="62" x="198" y="0">
<RCTTextView alpha="1.0" class="RCTTextView" focused="false" height="21" id="ReText-RNText" label="登录" visibility="visible" width="30" x="16" y="4" />
</RCTView>
<!-- 使用插件后: 自动生成有意义的testID -->
<RCTView testID="登录" alpha="1.0" class="RCTView" focused="false" height="30" id="EnhancedTouchable-Component" label="登录" visibility="visible" width="62" x="198" y="0">
<RCTTextView testID="登录" alpha="1.0" class="RCTTextView" focused="false" height="21" id="ReText-RNText" label="登录" visibility="visible" width="30" x="16" y="4" />
</RCTView>
```
MIT
欢迎提交Issue和Pull Request!