公司正在使用的项目管理工具是禅道 17.8,还是开源版。项目的测试管理、用例编写、缺陷录入都在系统上进行,但是开源版本并不支持测试报告的导出,这就会碰到整理测试文档时,缺少测试报告或者需要手工处理的情况。
为了解决这个问题我们编写了一段简单的油猴脚本,在禅道对应的报告页添加了一个导出的按钮,点击后整理页面相关内容形成 Markdown 格式文档后导出到本地,
这边文章主要是分享下油猴脚本和我的导出脚本,希望能帮助遇到同样问题的小伙伴。
安装插件并且编写脚本后,它可以做到:
1.在匹配的网页上自动运行;
2.改善用户界面;
3.绕过限制;
4.一系列自动化的操作。
脚本编写使用的语言是 JavaScript,熟悉自动化的小伙伴可以轻松入手
禅道的测试报告记录了整个测试过程,包括总结、测试范围、用例,内容详尽。但是开源版本的禅道,是没有导出按钮的,
通过油猴脚本我可以在上边添加一个导出按钮
后续的导出点击按钮就可以直接下载页面的内容,生成一份 Markdown 文件
附上脚本内容
// ==UserScript==
// @name 调试脚本
// @namespace http://tampermonkey.net/
// @version 2024-05-16
// @description try to take over the world!
// @author You
// @icon https://www.google.com/s2/favicons?sz=64&domain=cdf-hn.com
// @grant GM_xmlhttpRequest
// @require https://unpkg.com/turndown@7.1.3/dist/turndown.js
// ==/UserScript==
(function() {
'use strict';
const turndownService = new TurndownService();
// 对纵向列表的简单处理
function htmlToMarkdownTable(html) {
// 使用DOMParser将HTML字符串解析为DOM对象
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// 报告只需要table的第一个tbody
const tbody = doc.querySelector('table');
if (!tbody) return '';
let markdown = '| ';
// 遍历表头以获取列标题
const headers = [];
tbody.querySelectorAll('tr:first-child th').forEach(th => {
markdown += th.textContent.trim() + ' | ';
headers.push(th.textContent.trim());
});
markdown += '\n| --- | ';
// 添加分隔线,每个标题后都加一个
for (let i = 1; i < headers.length; i++) {
markdown += '--- | ';
}
markdown += '\n';
// 遍历表格的其余行
tbody.querySelectorAll('tr:not(:first-child)').forEach(tr => {
markdown += '| ';
let colspanCount = 0;
tr.querySelectorAll('td, th').forEach(cell => {
if (cell.getAttribute('colspan')) {
colspanCount = parseInt(cell.getAttribute('colspan'), 10) - 1; // 减去1,因为第一个单元格已经处理了
}
// 如果不是colspan的单元格,或者colspan的结束单元格,添加内容
if (colspanCount <= 0) {
markdown += cell.textContent.trim() + ' | ';
colspanCount = cell.getAttribute('colspan') ? parseInt(cell.getAttribute('colspan'), 10) - 1 : 0;
}
// 跳过colspan的额外单元格
else {
colspanCount--;
markdown += ' | ';
}
});
markdown += '\n';
});
return markdown;
}
// 绑定按钮点击事件
function createButtonAndBindEvent() {
// 创建按钮并添加到页面
const button = document.createElement('button');
button.textContent = '导出为 Markdown';
button.onclick = exportToMarkdown;
document.querySelector("#mainMenu").appendChild(button)
}
// 导出为 Markdown 的函数
function exportToMarkdown() {
// 有多个部分的 HTML 内容需要导出,单独记录selector
const titleSelector = 'title'; // 标题
const basicSelector = '#basic'; // 基本信息
const storyAndBugSelector = '#storyAndBug'; // 测试范围
const tabBuildSelector = '#tabBuild'; // 测试轮次
const tabCaseSelector = '#tabCase'; // 关联的用例
const tabLegacyBugsSelector = '#tabLegacyBugs'; // 遗留BUG
const tabCommentSelector = '#tabComment'; // 总结
const tabHistorySelector = '#tabHistory'; // 历史记录
// 获取 HTML 内容
const titlePart = document.querySelector(titleSelector).innerHTML;
const basicPart = document.querySelector(basicSelector).innerHTML;
const storyAndBugPart = document.querySelector(storyAndBugSelector).innerHTML;
const tabBuildPart = document.querySelector(tabBuildSelector).innerHTML;
const tabCasePart = document.querySelector(tabCaseSelector).innerHTML;
const tabLegacyBugPart = document.querySelector(tabLegacyBugsSelector).innerHTML;
const tabCommentPart = document.querySelector(tabCommentSelector).innerHTML;
const tabHistoryPart = document.querySelector(tabHistorySelector).innerHTML;
// 将 HTML 转换为 Markdown
let markdownPart = turndownService.turndown(titlePart);
markdownPart += `\n\n`;
markdownPart += `## 基本信息`;
markdownPart += `\n\n`;
markdownPart += turndownService.turndown(basicPart);
markdownPart += `\n\n`;
markdownPart += `## 测试范围`;
markdownPart += `\n\n`;
markdownPart += htmlToMarkdownTable(storyAndBugPart);
markdownPart += `\n\n`;
markdownPart += `## 测试轮次`;
markdownPart += `\n\n`;
markdownPart += htmlToMarkdownTable(tabBuildPart);
markdownPart += `\n\n`;
markdownPart += `## 关联的用例`;
markdownPart += `\n\n`;
markdownPart += htmlToMarkdownTable(tabCasePart);
markdownPart += `\n\n`;
markdownPart += `## 遗留的BUG`;
markdownPart += `\n\n`;
markdownPart += htmlToMarkdownTable(tabLegacyBugPart);
markdownPart += `\n\n`;
markdownPart += `## 总结`;
markdownPart += `\n\n`;
markdownPart += turndownService.turndown(tabCommentPart);
markdownPart += `\n\n`;
markdownPart += `## 历史记录`;
markdownPart += `\n\n`;
markdownPart += turndownService.turndown(tabHistoryPart);
// 组合 Markdown 内容(这里只是简单地拼接,可以根据需要进行更复杂的处理)
const combinedMarkdown = `${markdownPart}`;
// 显示或处理 Markdown 内容
// 调试使用,将其显示在一个新的 textarea 元素中
const textarea = document.createElement('textarea');
textarea.value = combinedMarkdown;
textarea.style.width = '100%';
textarea.style.height = '200px';
document.body.appendChild(textarea);
// 创建一个 Blob 对象包含 Markdown 内容
const blob = new Blob([combinedMarkdown], { type: 'text/plain;charset=utf-8' });
// 创建一个指向 Blob 对象的 URL
const url = window.URL.createObjectURL(blob);
// 创建一个隐藏的 <a> 标签用于下载
const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.download = 'exported_content.md'; // 设置下载的文件名
document.body.appendChild(downloadLink);
// 触发点击事件开始下载
downloadLink.click();
// 清理
document.body.removeChild(downloadLink);
window.URL.revokeObjectURL(url);
}
// 执行
createButtonAndBindEvent();
})();