# Conflicts:
#	src/views/charts2/charts/networkOverview/NetworkOverviewLine.vue
This commit is contained in:
@changcode
2023-01-10 12:27:15 +08:00
8 changed files with 339 additions and 216 deletions

View File

@@ -2,6 +2,8 @@ module.exports = {
roots: [ roots: [
'<rootDir>/test' '<rootDir>/test'
], ],
verbose: true,
setupFilesAfterEnv: ['<rootDir>/test/init.js'],
testMatch: [ testMatch: [
'<rootDir>/test/**/__tests__/**/*.{vue,js,jsx,ts,tsx}', '<rootDir>/test/**/__tests__/**/*.{vue,js,jsx,ts,tsx}',
'<rootDir>/test/**/*.{spec,test}.{vue,js,jsx,ts,tsx}' '<rootDir>/test/**/*.{spec,test}.{vue,js,jsx,ts,tsx}'

View File

@@ -1,21 +1,29 @@
<template> <template>
<span data-test="count">{{count}}</span> <span test-id="count">{{count}}</span>
<button data-test="button" @click="click">click</button> <span test-id="title">{{getData.title}}</span>
<button test-id="button" @click="click">click</button>
</template> </template>
<script> <script>
/* vue-jest的测试示例 */ /* vue-jest的测试示例 */
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import axios from 'axios'
export default { export default {
name: 'Test', name: 'Test',
data () { data () {
return { return {
count: 0 count: 0,
getData: { id: 1, title: 'title' }
} }
}, },
methods: { methods: {
click () { click () {
this.count++ this.count++
},
async getPosts () {
axios.get('/api/posts').then(response => {
this.getData = response.data
})
} }
}, },
created () { created () {

View File

@@ -1,6 +1,5 @@
import { hasButton } from '@/permission' import { hasButton } from '@/permission'
import { dateFormatByAppearance } from '@/utils/date-util' import { dateFormatByAppearance } from '@/utils/date-util'
import { storageKey } from '@/utils/constants'
export default { export default {
data () { data () {
return { return {
@@ -21,7 +20,9 @@ export default {
query: false query: false
}, },
timeout: null, timeout: null,
debounceFunc: null debounceFunc: null,
// 是否正在单元测试
isUnitTesting: false
} }
}, },
methods: { methods: {

View File

@@ -1103,10 +1103,5 @@ export function getNameByEventType (type) {
case 'high dns response time': { case 'high dns response time': {
return 'dns response time' return 'dns response time'
} }
// 目前ui并未找到此类型添加以防不测
case 'high http resopnse time':
default: {
return 'http response time'
}
} }
} }

View File

@@ -17,10 +17,11 @@
<div :class="item.class"></div> <div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div> <div class="mpackets-name">{{$t(item.name)}}</div>
</div> </div>
<div class="line-value-unit"> <div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span> <span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2"> <span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span> <span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span> </span>
</div> </div>
</div> </div>
@@ -46,7 +47,7 @@
</div> </div>
<div style="height: calc(100% - 74px); position: relative"> <div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData && !showError"></chart-no-data> <chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" id="overviewLineChart"></div> <div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" ref="overviewLineChart"></div>
<!-- todo 后续改动此处为框选返回--> <!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">--> <!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>--> <!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
@@ -63,7 +64,7 @@ import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import _ from 'lodash' import _ from 'lodash'
import { get } from '@/utils/http' import axios from 'axios'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
@@ -205,7 +206,8 @@ export default {
} }
this.toggleLoading(true) this.toggleLoading(true)
get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => { axios.get(api.netWorkOverview.totalTrafficAnalysis, { params: params }).then(response => {
const res = response.data
this.errorMsg = res.message this.errorMsg = res.message
if (res.code === 200) { if (res.code === 200) {
@@ -260,6 +262,8 @@ export default {
} }
}, },
echartsInit (echartsData, show) { echartsInit (echartsData, show) {
// echarts内容在单元测试时不执行
if (!this.isUnitTesting) {
if (this.lineTab) { if (this.lineTab) {
this.handleActiveBar() this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false) echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
@@ -269,9 +273,6 @@ export default {
const _this = this const _this = this
// !this.myChart && (this.myChart = echarts.init(dom)) // !this.myChart && (this.myChart = echarts.init(dom))
// 此处为验证是否因dom未销毁导致图表出错后续可能会改 // 此处为验证是否因dom未销毁导致图表出错后续可能会改
let dom = null
dom = document.getElementById('overviewLineChart')
this.chartOption = stackedLineChartOption this.chartOption = stackedLineChartOption
const chartOption = this.chartOption.series[0] const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => { this.chartOption.series = echartsData.map((t, i) => {
@@ -369,7 +370,7 @@ export default {
} }
this.showMarkLine = true this.showMarkLine = true
this.$nextTick(() => { this.$nextTick(() => {
this.myChart = echarts.init(dom) this.myChart = echarts.init(this.$refs.overviewLineChart)
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
// 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush // 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush
this.myChart.dispatchAction({ this.myChart.dispatchAction({
@@ -417,6 +418,7 @@ export default {
} }
}) })
}) })
}
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return if (this.isNoData) return
@@ -489,12 +491,13 @@ export default {
referenceSelectChange (val) { referenceSelectChange (val) {
this.lineRefer = val this.lineRefer = val
let echartsData let echartsData
const chartOption = this.myChart.getOption()
if (this.lineTab) { if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false) echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
} else { } else {
echartsData = this.mpackets.filter(t => t.show === true) echartsData = this.mpackets.filter(t => t.show === true)
} }
if (!this.isUnitTesting) {
const chartOption = this.myChart.getOption()
if (this.lineRefer === 'Average' && this.showMarkLine) { if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => { chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) { if (t.name === echartsData[0].name) {
@@ -515,6 +518,7 @@ export default {
}) })
} }
this.myChart.setOption(chartOption) this.myChart.setOption(chartOption)
}
}, },
symbolSizeSortChange (index, time) { symbolSizeSortChange (index, time) {
const dataIntegrationArray = [] const dataIntegrationArray = []

View File

@@ -1,19 +1,18 @@
import Test from '../src/Test' import Test from '../src/Test'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { getNameByEventType } from '@/utils/tools'
import axios from 'axios'
// 模拟vue-router库否则组件中引用vue-router的代码报错 // 模拟的axios返回数据
jest.mock('vue-router', () => { const mockPostList = { data: { id: 2, title: 'title2' } }
return {
useRouter: function () { return { currentRoute: '/' } } describe('单元测试demo', () => {
} test('Vue组件--点击按钮后count的值+1', async () => {
})
test('点击按钮后count的值+1', async () => {
// 加载vue组件获得实例 // 加载vue组件获得实例
const wrapper = mount(Test) const wrapper = mount(Test)
// 取到文本和按钮的dom // 取到文本和按钮的dom
const textNode = await wrapper.get('[data-test="count"]') const textNode = await wrapper.get('[test-id="count"]')
const button = await wrapper.get('[data-test="button"]') const button = await wrapper.get('[test-id="button"]')
// 断言文本dom的内容是否是'0' // 断言文本dom的内容是否是'0'
expect(textNode.text()).toContain('0') expect(textNode.text()).toContain('0')
// 模拟按钮dom点击执行click()方法 // 模拟按钮dom点击执行click()方法
@@ -23,12 +22,30 @@ test('点击按钮后count的值+1', async () => {
// 再次点击 // 再次点击
await button.trigger('click') await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'2' // 断言点击按钮后文本dom的内容是否是'2'
expect(textNode.text()).toContain('2') expect(textNode.text()).toBe('2')
/* 更多断言类型https://jestjs.io/zh-Hans/docs/expect */
})
test('Vue组件--直接获取vue实例中的data和method', async () => {
// 模拟axios返回数据
axios.get.mockResolvedValue(mockPostList)
// 加载vue组件获得实例
const wrapper = mount(Test)
const textNode2 = await wrapper.get('[test-id="title"]')
// 通过wrapper.vm.xxx直接执行click方法、获取data的数据 // 通过wrapper.vm.xxx直接执行click方法、获取data的数据
expect(wrapper.vm.count).toEqual(2) expect(wrapper.vm.count).toEqual(0)
await wrapper.vm.click() await wrapper.vm.click()
expect(wrapper.vm.count).toEqual(3) expect(wrapper.vm.count).toBe(1)
// 更多断言类型https://jestjs.io/zh-Hans/docs/expect expect(textNode2.text()).toEqual('title')
await wrapper.vm.getPosts()
await wrapper.vm.$nextTick(() => {
expect(textNode2.text()).toBe('title2')
})
})
test('js方法--getNameByEventType', async () => {
expect(getNameByEventType('http error')).toBe('http error ratio')
})
}) })

36
test/init.js Normal file
View File

@@ -0,0 +1,36 @@
import 'jest-canvas-mock'
import axios from 'axios'
import { config } from '@vue/test-utils'
/* 开启测试 */
config.global.mocks.isUnitTesting = true
/* 初始化dayjs */
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
const advancedFormat = require('dayjs/plugin/advancedFormat')
const weekday = require('dayjs/plugin/weekday')
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(advancedFormat)
dayjs.extend(weekday)
window.$dayJs = dayjs
/* 模拟vue-router库否则组件中引用vue-router的代码报错 */
jest.mock('vue-router', () => {
return {
useRouter: function () { return { currentRoute: '/' } },
useRoute: function () { return { query: {} } },
createWebHashHistory: jest.fn(),
createRouter: jest.fn().mockReturnValue({
beforeEach: jest.fn()
})
}
})
/* 模拟axios */
jest.mock('axios')
/* 模拟$t */
config.global.mocks.$t = key => key
/* 消除warn */
jest.spyOn(console, 'warn').mockImplementation(() => {})

View File

@@ -0,0 +1,60 @@
import NetworkOverviewLine from '@/views/charts2/charts/networkOverview/NetworkOverviewLine'
import { mount } from '@vue/test-utils'
import axios from 'axios'
const mockGet = {
data: {"status":200,"code":200,"success":true,"message":null,"formatType":"json","data":{"resultType":"object","result":[{"type":"bytes","totalBitsRate":{"values":[[1673247564,"96801019.52"]],"analysis":{"avg":"112042808.24","max":"301842105.76","min":"52096324","p95":"168089003.35199997"}},"inboundBitsRate":{"values":[[1673247564,"11814508.48"]],"analysis":{"avg":"18587597.36","max":"137528138.88","min":"3181142.88","p95":"49561521.447999954"}},"outboundBitsRate":{"values":[[1673247564,"84282965.52"]],"analysis":{"avg":"87557861.44","max":"290402258","min":"45337684.48","p95":"121041718.81199999"}},"internalBitsRate":{"values":[[1673247564,"9125.12"]],"analysis":{"avg":"278114.32","max":"2215460.48","min":"0","p95":"923494.5719999998"}},"throughBitsRate":{"values":[[1673247564,"694420.48"]],"analysis":{"avg":"5619235.12","max":"42455480.24","min":"262607.76","p95":"13559588.195999999"}},"other":{"values":[[1673247564,"0.00"]],"analysis":{"avg":"0.01","max":"0.08","min":"0.00","p95":"0.08"}}},{"type":"packets","totalPacketsRate":{"values":[[1673247564,"12077.53"]],"analysis":{"avg":"14062.37","max":"32840.42","min":"6564.17","p95":"20923.167999999987"}},"inboundPacketsRate":{"values":[[1673247564,"3865.58"]],"analysis":{"avg":"4241.61","max":"15460.03","min":"1918.22","p95":"8549.799999999997"}},"outboundPacketsRate":{"values":[[1673247564,"8118.89"]],"analysis":{"avg":"9170.98","max":"27134.58","min":"4510.25","p95":"13690.540999999996"}},"internalPacketsRate":{"values":[[1673247564,"15.89"]],"analysis":{"avg":"35.95","max":"276.47","min":"0.00","p95":"122.49749999999999"}},"throughPacketsRate":{"values":[[1673247564,"77.17"]],"analysis":{"avg":"613.82","max":"3768.56","min":"42.92","p95":"1279.757499999999"}},"other":{"values":[[1673247564,"0.00"]],"analysis":{"avg":"0","max":"0.01","min":"0.00","p95":"0.01"}}},{"type":"sessions","totalSessionsRate":{"values":[[1673247564,"29.92"]],"analysis":{"avg":"29.89","max":"29.92","min":"29.67","p95":"29.92"}}}]},"msg":"OK"}
}
describe('NetworkOverviewLine.vue测试', () => {
test('Metric=Bits/s', async () => {
// 模拟axios返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter: {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
},
chart: {"id":1,"name":"network overview line","i18n":"","panelId":1,"pid":0,"type":102,"x":0,"y":0,"w":19,"h":6,"params":{},"cby":1,"ctime":"2022-07-06 16:59:22","uby":1,"utime":"2022-07-06 16:59:22","remark":"","state":1,"system":0,"buildIn":0,"uuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"cuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"children":[],"parent":null,"panel":{"id":1,"name":"Network Overview","i18n":null,"type":null,"params":null,"cby":null,"ctime":null,"uby":null,"utime":null,"remark":null,"state":null,"buildIn":null,"uuser":null,"cuser":null},"i":1,"category":"echarts","firstShow":false,"moved":false}
}
})
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
// 延迟1秒等待渲染。使用wrapper.vm.$nextTick有时不管用
await new Promise(resolve => setTimeout(() => {
expect(textNode0.text()).toEqual('112.04Mbps')
expect(textNode1.text()).toEqual('18.59Mbps')
expect(textNode2.text()).toEqual('87.56Mbps')
resolve()
}, 1000))
})
test('Metric=Packets/s', async () => {
// 模拟axios返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter: {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
},
metric: 'Packets/s',
chart: {"id":1,"name":"network overview line","i18n":"","panelId":1,"pid":0,"type":102,"x":0,"y":0,"w":19,"h":6,"params":{},"cby":1,"ctime":"2022-07-06 16:59:22","uby":1,"utime":"2022-07-06 16:59:22","remark":"","state":1,"system":0,"buildIn":0,"uuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"cuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"children":[],"parent":null,"panel":{"id":1,"name":"Network Overview","i18n":null,"type":null,"params":null,"cby":null,"ctime":null,"uby":null,"utime":null,"remark":null,"state":null,"buildIn":null,"uuser":null,"cuser":null},"i":1,"category":"echarts","firstShow":false,"moved":false}
}
})
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
await new Promise(res => setTimeout(() => {
expect(textNode0.text()).toEqual('14.06Kpackets/s')
expect(textNode1.text()).toEqual('4.24Kpackets/s')
expect(textNode2.text()).toEqual('9.17Kpackets/s')
res()
}, 1000))
})
})