首先简单讲一下覆盖率涉及到的一些基础知识 我使用的 istanbul.js
来做的代码覆盖率,要生成一个覆盖率结果,需要客户端和服务端配合 客户端主要要做两个工作:
服务端主要做:
nyc
(也就是istanbul) 生成对应的结果不过站在前端的角度上,只需要考虑客户端的工作,然后跟测试同学打交道,测试同学会调用我提供的服务来生成覆盖率结果
为了尽量减少侵入性,以及方便业务方接入,我把覆盖率集成到了vite插件中,这种方式是最简单的 同时,这种方式也把上文说的客户端的 「两个工作」 都做了
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { istanbulWidget } from 'vite-plugin-istanbul-widget'
export default defineConfig((env) => ({
plugins: [
react(),
// 引入插件
istanbulWidget({
enabled: !(env.mode === 'production'),
// istanbul-widget 控件配置
istanbulWidgetConfig: {
//
theme: 'dark',
defaultPosition: {
x: 20,
y: 100,
},
plugin: {
report: {
async onReport(coverage: any, ...args: any[]) {
// 调用上报方法
// 这个需要测试方提供接口上报
await window.__report(coverage, ...args)
},
},
setting: {
autoReport: false,
onLeavePage: true,
requireReporter: true,
text: '!设置文案!',
},
buttonGroup: [
{
text: '自定义按钮',
onClick(...args: any[]) {
window.__customClick(...args)
},
},
],
},
},
// 全量上报
fullReport: true,
}),
],
}))
const toFormData = (obj) => {
const formData = new FormData()
Object.keys(obj).forEach((key) => {
formData.append(key, obj[key])
})
return formData
}
async function report(coverage, params) {
// 这个域名是测试方的
const res = await fetch('测试方提供的接口uri', {
body: toFormData({
pl_id: 'whatever', // 产品线id,测试同学告知
version_name: __GIT_COMMIT_ID__, // 全局变量,会通过vite插件注入
owner: params.setting.reporter, // 上报人
src_roots: '/app', // docker容器中的workdir
cov_data: JSON.stringify(coverage), // 覆盖率数据
}),
method: 'POST',
})
if (!res.ok) {
throw new Error('上报失败')
}
}
window.__report = report
由于插件注入了一些全局变量,所以为了更好的typescript提示,我们还需要引入一下类型
{
"compilerOptions": {
"types": ["vite-plugin-istanbul-widget/client"]
}
}
因为业务方需要我们提供项目的 git commit id,所以我们需要在docker中新增一个 git 的镜像。 很简单:
# 举个例而已
FROM node:18.17-alpine3.17
# 在基础镜像后执行
RUN apk add git
这个git的镜像可以接入到我们的基础镜像中,提升构建速度
因为测试方需要全量的代码覆盖率,所以全局的样式污染需要更改:
body {
padding-top: 16px;
width: 100%;
}
更改为
import { useGlobalStyle } from '@minko-fe/react-hook'
useGlobalStyle('body', {
paddingTop: '16px',
width: '100%',
})
基础使用的话,掌握插件式接入即可,如果有更加个性化的需求,可以使用扩展式接入
import { Button } from 'istanbul-widget/components'
import { IstanbulWidget } from 'istanbul-widget'
// 自定义插件
function MyPlugin() {
return <Button size={'sm'}>this is my Plugin</Button>
}
const myPlugin = new IstanbulWidget.IstanbulWidgetReactPlugin('my_plugin', 'My Plugin', MyPlugin)
// 给插件添加事件监听
myPlugin.event.on('init', () => {
console.log('my plugin inited')
})
const istanbulWidget = new IstanbulWidget({
defaultPosition: {
x: -100,
y: 100,
},
plugin: {
report: {
onReport(coverage) {
console.log('上报', coverage)
throw new Error('上报失败')
},
},
setting: {
requireReporter: true,
},
buttonGroup: [
{
text: '额外按钮 - 1',
onClick() {
console.log('1')
},
},
{
text: '额外按钮 - 2',
onClick() {
console.log('2')
},
},
],
},
})
// 把自定义的插件添加到istanbulWidget控件中
istanbulWidget.addPlugin(myPlugin)