前言
最近接到一个测试同学的需求,为了方便UI自动化定位,他们希望给每个html元素添加一个唯一标示。目前我们使用的是react,所以下面方法主要针对react用户。
方案
对于SPA应用,在页面加载的时候会执行对应的打包js代码,里面一般是这样的:
那其实答案已经有了,只要改变一下React.createElement,增加额外的props就能解决这个需求了;但需要明白一点,每次页面刷新同一个地方的元素为唯一属性一定是不变的,否则就失去意义了。
代码
import React, {
ReactHTML,
ClassAttributes,
ReactNode,
HTMLAttributes,
ReactSVG,
SVGAttributes,
DOMAttributes,
} from 'react';
import md5 from 'md5';
/**
* 封装React.createElement方法
* 使页面创建元素的时候都添加一个data-key的唯一属性,方便做自动化测试
* data-key的生成过程会先拼接元素的所有string类型属性,并且会 自动继承上级的data-key
* 最终由md5创建一个不可逆的加密
* 其他:
* 1、需要注意的是,如果元素存在动态的属性,比如className,那他的data-key也会不同;
* 2、为了保证尽可能的唯一,在自定义attribute的基础上增加了{attribute}-url的属性,为了尽可能存在一个唯一属性;
*/
export interface ReactUniqueKeyProps {
attribute?: string; // default data-key(注意这里的格式应是 data-*)
}
const reactUniqueKey = (props?: ReactUniqueKeyProps) => {
const { attribute = 'data-key' } = props || {};
const createEle = React.createElement;
const urlKeys = {};
React.createElement = function createElement<
P extends HTMLAttributes<T> | SVGAttributes<T> | DOMAttributes<T>,
T extends HTMLElement | SVGElement | Element
>(
type: keyof ReactHTML | keyof ReactSVG | string,
props?: (ClassAttributes<T> & P) | null,
...children: ReactNode[]
) {
const keyStrings = [];
if (typeof type === 'string') {
keyStrings.push(`[label=${type}]`);
}
// 这一步会自动添加上级的attribute生成新的
if (props) {
Object.keys(props).forEach(key => {
const value = props[key];
if (typeof value === 'string') {
keyStrings.push(`[${key}=${value}]`);
}
});
}
// 尽可能保证有个唯一属性
const url = window.location.href;
const attributesWidthUrl = [`[url=${url}]`, ...keyStrings];
let attributeKey = md5(keyStrings.join(';'));
let attributeUrl = md5(attributesWidthUrl.join(';'));
// 同页面下记录并防重(li元素等情况)
if (!urlKeys[url]) {
urlKeys[url] = {
index: 0,
keys: [],
};
urlKeys[url].keys.push(attributeKey);
} else {
if (urlKeys[url].keys.includes(attributeKey)) {
urlKeys[url].index += 1;
attributeKey += urlKeys[url].index;
attributeUrl += urlKeys[url].index;
} else {
urlKeys[url].keys.push(attributeKey);
}
}
const newProps = {
...props,
[attribute]: attributeKey,
[`${attribute}-url`]: attributeUrl,
};
return createEle(type, newProps, ...children);
} as any;
};
export default reactUniqueKey;
使用
在配置文件中引入并执行即可(如app.ts)
import reactUniqueKey from './utils/react-unique-key';
reactUniqueKey();
效果
data-key-url与data-key的区别:
data-key-url: 保证每个页面的都不同
data-key: 每个页面相同的部分还是不变的
这样可以提供更多的UI定位方法
其他方案
还可以通过SSR的方式,ReactDOM.hydrate 会自动给每个节点添加data-reactid,有兴趣可以自行研究一下。