前言

需求:测试编写测试自动化脚本,需要定位到页面元素。需要每个操作元素添加唯一标识ID,并且每次前端构建不能导致相同页面相同元素的ID变化。方便测试快速写自动化测试脚本。

解决方案1:
webpack-loader打包的时候处理渲染前的HTML根据正则去匹配操作元素,添加唯一id。最后渲染出来的页面操作元素有唯一id。

webpack-loader chainWebpack代码:

 chainWebpack: (config) => { 
        config.resolveLoader.alias.set('my-loader', path.resolve(__dirname, 'build/preLoader-makeId.js'))

        config.module
            .rule('vue').test(/\.vue$/)
            .use('my-loader')
            .loader('my-loader').end()
            ;
    },

preLoader-makeId.js文件:

module.exports = function (contentStr, val) {

var first = contentStr.indexOf('<template>')
var end = contentStr.indexOf('<script>')

content = contentStr.slice(first, end)


let returnContent = content.replace(/\r\n/g, ' ')
returnContent = returnContent.replace(/\r/g, ' ')
returnContent = returnContent.replace(/\n/g, ' ')
returnContent = returnContent.replace(/<\/el-/g, '\n</el-')
returnContent = returnContent.replace(/<el-/g, '\n<el-')
var regModule = /(model=\")([^\"]*)\"/g
var regId = /(id=\")([^\"]*)\"/g
var regBtn = /<(el-button)([^<>]*)>/g
var regInput = /<(el-input)([^<>]*)>/g
var regSele = /<(el-select)([^<>]*)>/g
var regDate = /<(el-date-picker)([^<>]*)>/g
var regInputNumber = /<(el-input-number)([^<>]*)>/g
function makeId_input(index, item) {
    let id
    let res = regModule.exec(item)
    if (res && res.length > 0) {
        id = res[0]
            .replace('model="', '')
            .replace(/\"/g, '')
            .replace(/\./g, '_')
    }

    if (id) {
        return id
    } else {
        return index
    }

    // return id || index
}
function makeId_btn(index, item) {
    let id
    let clickAllArr = item.split(/click=/g)
    let subClick = ''
    let subClickArr = []
    if (clickAllArr.length > 1) {
        subClick = clickAllArr[1].substring(1, clickAllArr[1].length)
        subClickArr = subClick.split(/"/g)
        if (subClickArr.length > 1) {
            id = subClickArr[0]
                .split(/\=/g)[0]
                .split(/\(\"/g)[0]
                .split(/\(\'/g)[0]
                .split(/\(/g)[0]
        } else {
            id = index
        }
    } else {
        id = index
    }

    if (id) {
        return id
    } else {
        return index
    }
}
let contentArr = returnContent.split('\n')
let resultArr = contentArr.map((item, index) => {
    let id
    if (!regId.test(item)) {
        // 输入框 - 数字
        if (regInputNumber.test(item)) {
            item =
                '<el-input-number' +
                ' id="zgwww-input_number_' +
                makeId_input(index, item) +
                '"' +
                item.substring('<el-input-number'.length, item.length) +
                ' '
        } else if (regInput.test(item)) {
            // 输入框
            item =
                '<el-input' +
                ' id="zgwww-input_' +
                makeId_input(index, item) +
                '"' +
                item.substring('<el-input'.length, item.length) +
                ' '
        }
        if (regSele.test(item)) {
            // 选择框
            item =
                '<el-select' +
                ' id="zgwww-select_' +
                makeId_input(index, item) +
                '"' +
                item.substring('<el-select'.length, item.length) +
                ' '
        }
        if (regDate.test(item)) {
            // 日期选择
            item =
                '<el-date-picker' +
                ' id="zgwww-date_' +
                makeId_input(index, item) +
                '"' +
                item.substring('<el-date-picker'.length, item.length) +
                ' '
        }
        if (regBtn.test(item)) {
            // 按钮
            item =
                '<el-button' +
                ' id="zgwww-btn_' +
                makeId_btn(index, item) +
                '"' +
                item.substring('<el-button'.length, item.length) +
                ' '
        }
    }
    // alert(1)
    return item
})

return resultArr.join('\n') + '\n' + contentStr.slice(end)
// return contentStr

}

结果:但是如果使用webpack-loader去处理vue文件里面的html-template。
会导致同一个页面使用多个相同的前端组件-同一个vue文件。生成出来的ID一样。
不符合测试要求的一个页面每个操作DOM元素生成不同的ID。

解决方案2:
监听window浏览器地址栏事件,包括window.history.pushState,window.history.replaceState,window.onpopstate这三个window事件。
给页面框架最外层盒子添加id-appmain,这里要用setTimeout宏任务进行递归childNodes。因为document.getElementById('appmain')取得到的html-object对象是个异步的对象。

window.history.pushState = patchRouter(
      window.history.pushState,
      'micro_push'
    );
window.addEventListener('micro_push', turnApp);
 function turnApp() {
      var that = this;
      setTimeout(function () {
        var appMain = document.getElementById('appmain');
        var a = 0;
        function forDom(root1) {
          if (root1) {
            if (root1.setAttribute) {
              if (root1.id != 'appmain' && !root1.id) {
                if (root1._prevClass) {
                  root1.setAttribute('id', root1._prevClass + a);
                } else if (root1.nodeName) {
                  root1.setAttribute('id', root1.nodeName + a);
                } else if (root1.localName) {
                  root1.setAttribute('id', root1.localName + a);
                } else {
                  root1.setAttribute('id', 'Id' + a);
                }
              }
              // console.log('root1', root1);
            }
          }

          a = a + 1;
          if (root1) {
            if (root1.childNodes) {
              if (root1.childNodes.length != 0) {
                for (var i = 0; i < root1.childNodes.length; i++) {
                  forDom(root1.childNodes[i]);
                }
              }
            }
          }
        }
        forDom(appMain);
      }, 3000);
    }

对于一些点击操作使用v-if重新改变页面dom结构的。
我们监听window的click事件去重新触发turnApp重新给页面dom结构添加id,对于一些页面加载的时候已经加载过id的,我们做判断不重新赋值id。

   window.addEventListener('click', turnApp);
if (root1.id != 'appmain' && !root1.id) {
}

总结:
本文对测试自动化测试-添加前端页面DOM元素唯一ID进行了解决方案与代码实现。也对里面代码实现的细节点进行了详细说明。讲解部分到此结束,谢谢阅读!


zgwww
21 声望3 粉丝