背景:

公司使用 TAPD 作为项目、测试流程、测试用例、缺陷管理平台,而日常公司内部的交流工具为钉钉;TAPD 与钉钉无法在系统上进行很好的交互,在提交 bug 或者其他任务状态发生变化时,需要及时通知到相关的同事进行及时处理。

解决方案:

编写 Chrome 插件,对需要关注的任务、bug 进行状态监听;当状态发生流转时,能够通过插件调用钉钉机器人,及时的在工作群中通知到相关同事。

目前效果:


严重级别及以上、优先级为高及以上的 bug,状态流转至已解决的耗时由平均 4.8个小时提升至平均 2.9个小时。极大程度上提高了 bug 的解决效率和测试效率。

原理:

Chrome 插件编写 Java Script 对按钮进行监听,判断字段发生变化并且满足条件时,调用 XMLHttpRequest 对已设置好的钉钉群机器人发送消息,即可实现像上图一样的效果。是不是很 easy!

代码:

// var dingdingUrl = "https://oapi.dingtalk.com/robot/send?access_token=842de0b90ff76bfce0830ab71097dee74fad0a17ba0963a56a3fffa7a940449b";

$(function () {
    isSend();

    //提交缺陷
    $('#_view').click(function () {
        localStorage.setItem('preUrl', window.location.href);
    });

    //bug状态流转
    $('#update_status_btn').click(function (){
        localStorage.setItem('preUrl', window.location.href);
    });

    //从缓存拿上个页面地址进行对比,并进行发送
    async function isSend() { 
        var _preUrl = localStorage.getItem('preUrl');
        var _referrer = document.referrer;
        if (_preUrl == _referrer) {
            var _bugPriority = $('#ContentPriority').attr('data-editable-value');
            var _bugSeverity = $('#ContentSeverity').attr('data-editable-value');
            var _bugStatus = $('a[workflow-status-change=entityViewStatusChange]').attr('title');
            //根据bug等级、状态、优先级决定是否进行发送钉钉
            if(_bugSeverity == "致命" || _bugSeverity == "严重" || _bugPriority == "紧急" || _bugPriority == ""){
                if(_bugStatus != "已关闭" && _bugStatus != "接受/处理" ){
                    sendingMsg(await setBugParams())
                }
            }

            localStorage.removeItem('preUrl');
        }
    }

    async function setBugParams(){
        var _bugUrl = getBugUrl(location.href);
        var _bugTitle = $('#bug_title_view .editable-value').attr('title');
        var _bugPriority = $('#ContentPriority').attr('data-editable-value');
        var _bugSeverity = $('#ContentSeverity').attr('data-editable-value');
        var _bugModule = $('#Content所属模块').attr('data-editable-value');
        var _bugStatus = $('a[workflow-status-change=entityViewStatusChange]').attr('title');
        var _bugOwners = $('#ContentCurrentOwner').text();
        _bugOwners = _bugOwners.replace(/\(.*?\)/g,'');
        var _bugPriority = $('#ContentPriority').attr('data-editable-value');
        var _bugSeverity = $('#ContentSeverity').attr('data-editable-value');
        var _mobileList =await getMobiles(_bugOwners);

        var params = {
            "msgtype": "text",
            "text": {
            "content": "BUG:" + _bugTitle + 
            "\n状态:" + _bugStatus + 
            "\n优先级:" + _bugPriority + 
            "\n级别:" + _bugSeverity + 
            "\n所属模块:" + _bugModule + 
            "\n链接:" + _bugUrl
            },
            "at":{
                "atMobiles": _mobileList,
                "isAtAll": false
            }

        };
        return params;
    }

    //发送消息XMLHTTP
    function sendingMsg(params) {
        if (params == 0){
            return;
        }
        var xhr = new XMLHttpRequest();
        xhr.open("POST", dingdingUrl, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (window.Notification) {
                    chrome.extension.sendRequest({ type: 1, name: "发送钉钉通知", msg: xhr.responseText }, function (response) {
                        console.log(response);
                    });
                } else { alert('消息已发送,当前浏览器不支持推送消息'); }
            }
        }
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.send(JSON.stringify(params));
    }
    //拼接bug URL
    function getBugUrl(url){
        var tmpList1 = splitStr(url,"?");
        var tmpList2 = splitStr(tmpList1[1],"&");
        var bugUrl = tmpList1[0] + "?";
        for(var i=0;i<tmpList2.length;i++){
            console.log(tmpList2[i]);
            if(tmpList2[i].match("bug_id=")){
                bugUrl = bugUrl + tmpList2[i];
                break;
            }
        }
        return bugUrl;
    }
    //获取处理人联系电话
    async function getMobiles(bugOwners){
        var mobileList = splitStr(bugOwners,";").filter((v) => v)
        for(let i=0;i<mobileList.length;i++){
            //后端封装了个简单的接口,用于获取通知人的电话
            url='http://easytest.yzw.cn/user/getPhone?tapdUserAccount='+mobileList[i];
            mobileList[i] = await getMobile(url);
        }
        return mobileList;
    }
    //单个获取电话号码
    async function getMobile(url){
        return await fetch(url).then(response=>response.text())
    }

    //字符串解析分割
    function splitStr(str,regx){
        var result = str.split(regx);
        return result;
    }

});

说明:本插件脚本中,封装了获取 TAPD 用户电话号码的接口,因为要实现在钉钉中 @ 到某一个人。TAPD 的用户名跟钉钉的电话号码是维护在一个表里头,用接口进行返回。如果不需要接口的方式,也可以简单的用 Map 的方式在脚本中维护即可;如果不需要实现 @ 到某个人,那么也可以不用获取电话号码。

脚本写好后,需要以插件的形式安装到 Chrome 中。以下介绍下整体插件文件目录以及安装过程:

插件文件目录:

写好之后压缩了发给同事们吧~

## 安装步骤:


↙↙↙阅读原文可查看相关链接,并与作者交流