4.8 playwright实现页面及接口的自动化

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

最终效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值