1、jest.config.js
/*
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
module.exports = {
"reporters": [
"default",
["jest-html-reporters", {
"publicPath": "./html-report",
"filename": `${new Date().getTime()}.html`,
"openReport": false
}]
]
};
2、src/case/page
login_page.js
/**
* @description steps表示每个用例的执行步骤,selector字段使用的是playwright的selector选择器
* @description assertElement表示需要断言的元素,selector可以直接使用浏览器copy的selector
*/
module.exports = {
pageName: 'login-page',
url: 'https://192.168.3.80:8080/user/login',
steps: [
{
type: 'input',
selector: 'text=账号',
value: 'user'
},
{
type: 'input',
selector: 'text=密码',
value: 'Wlh@1234567'
},
{
type: 'click',
selector: 'button',
}
],
assertElement: [
{
selector: '#root > div > div > div:nth-child(1) > div > h1 > span',
value: 'test信息平台'
},
{
selector: '#root > div > div > div:nth-child(1) > div > form > div:nth-child(3) > button > span',
value: '登录'
}
]
}
home_page.js
module.exports = {
pageName: 'home-page',
url: 'https://192.168.3.80:8080/home',
steps: [],
assertElement: [
{
selector: '#logo > a > h1',
value: 'test信息平台'
},
{
selector: '#content > div > div > div > div > div.react-grid-layout.layout > div:nth-child(4) > div > div > div > div._2Wj8-Ajffvav9sQz3y4esS > span',
value: '近30天趋势'
},
{
selector: '#content > div > div > div > div > div.react-grid-layout.layout > div:nth-child(5) > div > div > div > div._2Wj8-Ajffvav9sQz3y4esS > span',
value: '年度趋势'
},
{
selector: '#content > div > div > div > div > div.react-grid-layout.layout > div:nth-child(6) > div > div > div > div._2Wj8-Ajffvav9sQz3y4esS > span',
value: '结果占比'
},
{
selector: '#content > div > div > div > div > div.react-grid-layout.layout > div:nth-child(7) > div > div > div > div._2Wj8-Ajffvav9sQz3y4esS > span',
value: '任务占比'
},
{
selector: '#root > div > div > div._1wGopWCKE7xbN00hj3CcxY.ant-layout > div._1Z92MrfcbggFRHWL0Eku7O.ant-layout-header > div > span._1r_Ku5UxCqCBFcxYwa7YcI.ant-dropdown-trigger > span.TPdRrLa458rcF0shFyj7D > span._1DLXYUJQFelazd_39MSHRs > span',
value: '已登录'
}
]
}
keyword_page.js
module.exports = {
pageName: 'keyword-page',
url: 'https://192.168.3.80:8080/keyword/list',
steps: [],
assertElement: [
{
selector: '#content > div > div > div > h3',
value: 'xxx管理'
},
{
selector: '#content > div > div > div > div > div > div._2z6IDqY1I9YYJ78A_TdGrW > div > div > div._3ICFjh4oC0gh_vBuZ4fsMO > button.ant-btn.my-button.ant-btn-primary.ant-btn-background-ghost > span',
value: '重置'
},
{
selector: '#content > div > div > div > div > div > div:nth-child(2) > div > div.ant-spin-nested-loading > div > div > div:nth-child(1) > div > div > div > div.qZHpEC_JchvC5swxxm4uE > button > span',
value: '新建xxx策略'
}
]
}
3、src/config/setting.js
/**
* @description projectName表示用例集的名称
* @description tempLogPath表示请求失败日志存放的地方
* @type {{tempLogPath: string, projectName: string}}
*/
module.exports = {
projectName: 'test信息平台',
tempLogPath:'/log/request.log'
}
/**
* @description orderList表示任务运行时的执行顺序
*/
module.exports.orderList =[
'login_page',
'home_page',
'keyword_page'
]
4、src/log/request.log
5、src/test/test.js
const setting = require('../config/setting')
const {chromium} = require('playwright');
const file_load = require('../utils/file_load')
const assertElement = require('../utils/assert_element')
const path = require('path')
const temp_log = require('../utils/file_temp_log')
jest.setTimeout(10000)
describe(`${setting.projectName}`, function () {
let browser
let page
let case_path = 'src/case/page'
let files = file_load(case_path)
beforeAll(async function () {
browser = await chromium.launch({
headless: false, slowMo: 300, args: ['--start-maximized']
});
page = await browser.newPage({viewport: null, ignoreHTTPSErrors: true});
page.on('requestfailed', function (request) {
let obj = {
time: new Date().toLocaleString(),
url: request.url(),
method: request.method(),
// headers: request.allHeaders(),//这个方法用来取代下面的request.headers(),但是实际上没有生效
headers: request.headers(),
data: request.postData(),
}
temp_log(setting.tempLogPath, JSON.stringify(obj))
})
})
beforeEach(async () => {
})
for (let i = 0; i < files.length; i++) {
let module_name = files[i].replace('.js', '')
let module_path = path.join('../case/page', module_name)
let module_list = {}
module_list[module_name] = require(module_path)
it(`${module_list[module_name].pageName}`, async () => {
if (module_list[module_name].url !== '') {
await page.goto(module_list[module_name].url);
}
await assertElement(page, module_list[module_name].assertElement)
if (module_list[module_name].steps.length !== 0) {
for (let j of module_list[module_name].steps) {
if (j.hasOwnProperty('type')) {
if (j.type === 'input') {
await page.fill(j.selector, j.value)
} else {
await page.click(j.selector)
await page.waitForLoadState()
}
}
}
}
})
}
afterEach(async function () {
})
afterAll(async () => {
page.close()
browser.close()
})
})
6、src/util/
add_style.js
/**
* @description 给断言的元素添加样式
* @param page
* @param ele
* @param num 传0代表通过标记为绿色,1代表value没匹配,标记为红色
* @return {Promise<void>}
*/
async function addStyle(page, ele, num) {
const elementHandle = await page.$(ele)
if (num === 0) {
await elementHandle.evaluateHandle((ele) => ele.style = "border-style:solid;border-width:2px;border-color:green")
} else if (num === 1) {
await elementHandle.evaluateHandle((ele) => ele.style = "border-style:solid;border-width:2px;border-color:red")
}
await elementHandle.dispose();
}
module.exports = addStyle
assert_element.js
/**
*@description 断言页面上的元素的value,并且根据结果添加样式进行标记
*/
const assert = require('assert')
const addStyle = require('../utils/add_style')
async function assertElements(page, elements) {
for (let i of elements) {
if (i.hasOwnProperty('value') && i.hasOwnProperty('selector')) {
let context = await page.$eval(i.selector, e => e.innerText)
if ((context.replace(' ', '')) === i.value) {
await addStyle(page, i.selector, 0)
assert.ok(`${i.value} matched`)
} else {
await addStyle(page, i.selector, 1)
assert.fail(`${i.value} not matched`)
}
}
}
}
module.exports = assertElements
file_load.js
/**
* @description 动态加载case中的文件,并且依据config/setting中的orderList进行排序
*/
const fs = require('fs')
const setting = require('../config/setting')
module.exports = (path) => {
let file = fs.readdirSync(path)
for (let i in setting.orderList) {
for (let j in file) {
if (new RegExp(setting.orderList[i]).test(file[j])) {
let temp = file[j]
file[j] = file[i]
file[i] = temp
}
}
}
return file
}
file_temp_log.js
/**
* @description 生成日志
*/
const fs = require('fs')
const path = require('path')
const os = require('os')
function tempLog(log_path, data) {
let config = path.join(__dirname.replace('utils', ''), log_path)
if (!fs.existsSync(config)) {
fs.writeFileSync(config, data, 'utf8')
} else {
fs.appendFileSync(config, `${os.EOL}${data}`, 'utf8')
}
}
module.exports = tempLog
最终效果