uirecorder 模块化
uirecorder原生代码问题
原生js文件十分臃肿,所有依赖都在一个js中,一个case一个js文件,后期维护十分困难。
模块化
对原生js进行模块化提取出2部分,一部分是以it快为主的主要case流程代码,另一部分是执行it快的依赖及主要方法。
- 将原文件中的module.exports提取出来
module.exports = function(){
let driver, testVars;
before(function(){
let self = this;
driver = self.driver;
testVars = self.testVars;
});
it('url: http://www.baidu.com', async function(){
await driver.url(_(`http://www.baidu.com`));
});
it('waitBody: ', async function(){
await driver.sleep(500).wait('body', 30000).html().then(function(code){
isPageError(code).should.be.false;
});
});
it('click: wd ( #kw, 163, 12, 0 )', async function(){
await driver.sleep(300).wait('#kw', 30000)
.sleep(300).mouseMove(163, 12).click(0);
});
it('click: 学术 ( //a[text()="学术"], 6, 9, 0 )', async function(){
await driver.sleep(300).wait('//a[text()="学术"]', 30000)
.sleep(300).mouseMove(6, 9).click(0);
});
it('waitBody: ', async function(){
await driver.sleep(500).wait('body', 30000).html().then(function(code){
isPageError(code).should.be.false;
});
});
it('click: 贴吧 ( //a[text()="贴吧"], 3, 8, 0 )', async function(){
await driver.sleep(300).wait('//a[text()="贴吧"]', 30000)
.sleep(300).mouseMove(3, 8).click(0);
});
it('switchWindow: 1', async function(){
await driver.sleep(500).switchWindow(1);
});
it('waitBody: ', async function(){
await driver.sleep(500).wait('body', 30000).html().then(function(code){
isPageError(code).should.be.false;
});
});
it('click: kw1 ( #wd1, 69, 18, 0 )', async function(){
await driver.sleep(300).wait('#wd1', 30000)
.sleep(300).mouseMove(69, 18).click(0);
});
it('sendKeys: mu{BACK_SPACE}{BACK_SPACE}MU{BACK_SPACE}{BACK_SPACE}mushenji', async function(){
await driver.sendKeys('mu{BACK_SPACE}{BACK_SPACE}MU{BACK_SPACE}{BACK_SPACE}mushenji');
});
it('click: 进入贴吧 ( //a[text()="进入贴吧"], 55, 15, 0 )', async function(){
await driver.sleep(300).wait('//a[text()="进入贴吧"]', 30000)
.sleep(300).mouseMove(55, 15).click(0);
});
it('waitBody: ', async function(){
await driver.sleep(500).wait('body', 30000).html().then(function(code){
isPageError(code).should.be.false;
});
});
it('click: mushenji ( a > em, 22, 11, 0 )', async function(){
await driver.sleep(300).wait('a > em', 30000)
.sleep(300).mouseMove(22, 11).click(0);
});
it('switchWindow: 2', async function(){
await driver.sleep(500).switchWindow(2);
});
it('waitBody: ', async function(){
await driver.sleep(500).wait('body', 30000).html().then(function(code){
isPageError(code).should.be.false;
});
});
it('expect: text, a.card_title_fname, equal, 牧神记吧', async function(){
await driver.sleep(300).wait('a.card_title_fname', 30000)
.text()
.should.not.be.a('error')
.should.equal(_(`牧神记吧`));
});
function _(str){
if(typeof str === 'string'){
return str.replace(/\{\{(.+?)\}\}/g, function(all, key){
return testVars[key] || '';
});
}
else{
return str;
}
}
};
function isPageError(code){
return code == '' || / jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(code);
}
- 将剩余的执行module.exports的方法封装到类中
const fs = require('fs');
const path = require('path');
const chai = require("chai");
const should = chai.should();
const JWebDriver = require('jwebdriver');
chai.use(JWebDriver.chaiSupportChainPromise);
const resemble = require('resemblejs-node');
resemble.outputSettings({
errorType: 'flatDifferenceIntensity'
});
class baseRun {
constructor() { }
runThisSpec(func,filename) {
let rootPath = getRootPath();
// read config
let webdriver = process.env['webdriver'] || '';
let proxy = process.env['wdproxy'] || '';
let config = require(rootPath + '/config.json');
let webdriverConfig = Object.assign({}, config.webdriver);
let host = webdriverConfig.host;
let port = webdriverConfig.port || 4444;
let match = webdriver.match(/([^\:]+)(?:\:(\d+))?/);
if (match) {
host = match[1] || host;
port = match[2] || port;
}
let testVars = config.vars;
let browsers = webdriverConfig.browsers;
browsers = browsers.replace(/^\s+|\s+$/g, '');
delete webdriverConfig.host;
delete webdriverConfig.port;
delete webdriverConfig.browsers;
// read hosts
let hostsPath = rootPath + '/hosts';
let hosts = '';
if (fs.existsSync(hostsPath)) {
hosts = fs.readFileSync(hostsPath).toString();
}
let specName = path.relative(rootPath, filename).replace(/\\/g, '/').replace(/\.js$/, '');
browsers.split(/\s*,\s*/).forEach(function (browserName) {
let caseName = specName + ' : ' + browserName;
let browserInfo = browserName.split(' ');
browserName = browserInfo[0];
let browserVersion = browserInfo[1];
describe(caseName, function () {
this.timeout(600000);
this.slow(1000);
let driver;
before(function () {
let self = this;
let driver = new JWebDriver({
'host': host,
'port': port
});
let sessionConfig = Object.assign({}, webdriverConfig, {
'browserName': browserName,
'version': browserVersion,
'ie.ensureCleanSession': true,
});
if (proxy) {
sessionConfig.proxy = {
'proxyType': 'manual',
'httpProxy': proxy,
'sslProxy': proxy
}
}
else if (hosts) {
sessionConfig.hosts = hosts;
}
try {
self.driver = driver.session(sessionConfig).windowSize(1024, 768).config({
pageloadTimeout: 30000, // page onload timeout
scriptTimeout: 5000, // sync script timeout
asyncScriptTimeout: 10000 // async script timeout
});
} catch (e) {
console.log(e);
}
let strs = filename.split('\\');
let thisName = strs[strs.length -1];
self.testVars = testVars;
let casePath = path.dirname(caseName)+'/'+thisName;
self.screenshotPath = rootPath + '/screenshots/' + casePath;
self.diffbasePath = rootPath + '/diffbase/' + casePath;
self.caseName = caseName.replace(/.*\//g, '').replace(/\s*[:\.\:\-\s]\s*/g, '_');
mkdirs(self.screenshotPath);
mkdirs(self.diffbasePath);
self.stepId = 0;
return self.driver;
});
func();
beforeEach(function () {
let self = this;
self.stepId++;
if (self.skipAll) {
self.skip();
}
});
afterEach(async function () {
let self = this;
let currentTest = self.currentTest;
let title = currentTest.title;
if (currentTest.state === 'failed' && /^(url|waitBody|switchWindow|switchFrame):/.test(title)) {
self.skipAll = true;
}
if (!/^(closeWindow):/.test(title)) {
let filepath = self.screenshotPath + '/' + self.caseName + '_' + self.stepId;
let driver = self.driver;
try {
// catch error when get alert msg
await driver.getScreenshot(filepath + '.png');
let url = await driver.url();
let html = await driver.source();
html = '<!--url: ' + url + ' -->\n' + html;
fs.writeFileSync(filepath + '.html', html);
let cookies = await driver.cookies();
fs.writeFileSync(filepath + '.cookie', JSON.stringify(cookies));
appendToContext(self, filepath + '.png');
}
catch (e) { }
}
});
after(function () {
return this.driver.close();
});
});
});
}
callSpec(name) {
try {
require(rootPath + '/' + name)();
}
catch (e) {
console.log(e)
process.exit(1);
}
}
}
function getRootPath() {
let rootPath = path.resolve(__dirname);
while (rootPath) {
if (fs.existsSync(rootPath + '/config.json')) {
break;
}
rootPath = rootPath.substring(0, rootPath.lastIndexOf(path.sep));
}
return rootPath;
}
function mkdirs(dirname) {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirs(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
}
function isPageError(code) {
return code == '' || / jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(code);
}
function appendToContext(mocha, content) {
try {
const test = mocha.currentTest || mocha.test;
if (!test.context) {
test.context = content;
} else if (Array.isArray(test.context)) {
test.context.push(content);
} else {
test.context = [test.context];
test.context.push(content);
}
} catch (e) {
console.log('error', e);
}
};
function catchError(error) {
}
baseRun.rootPath = getRootPath();
module.exports = baseRun;
- 通过给引入class,调用方法传参的方式编写case
const baseRun = require('../commons/base/baseRun.js');
const thisSpec = function() {
require('./dome1/demo1')();
}
if (module.parent && /mocha\.js/.test(module.parent.id)) {
const run = new baseRun();
run.runThisSpec(thisSpec,__filename);
}
思考
1、上面的相对路径可以换成绝对路径
2、class的封装太简单
3、可以自设置class来完成自己想要达到的结果
4、这里还有好多东西可以配置成默认设置
有关资料
mocha官方网站 --这个uirecorder依赖的测试框架
一些前辈关羽mocha官方的中文翻译博客
macaca --这个是产生报告的,暂时没有更多了解
uirecorder官方文档,里面有介绍安装、使用等很多介绍。
jwebdriver --类似于webdriver