我如何使用AI构建代码帮助QA同学的减负。
代码是用来解决问题的工具;而不是搬砖的铲子;AI时代写代码很快,要想想怎么用代码解决一些开发中和业务中的问题才是我们关键
前言
最近在封闭开发过程中发现QA同学只能通过固定页面提交bug。而且需要不断的截图上传,在没有显示器的情况下,无论是切换还是上传对于QA的都作都是一种"不得不做"的时间负担,而且提给我们的bug也没有链接导致无法快速还原和解决。对于前端而言也是一种负担。所以通过开发一个快速bug提交,快速完成页面页面截图,链接收集的方式让QA可以不只在buglist的网站进行提交。有了想法之后交给AI迭代,很快就完成了。下面是一些经验和遇到困难:主要是如何实现快速截图,如何解决插件请求接口的跨域问题,以及如何解决截图样式等。最终写了个文档+插件包提供给QA团队,最终将整体流程速度提升70%。QA不需要上传截图链路,和填写页面地址链路。同时避免浏览器的来回切换带来的心智负担。皆大欢喜!!下面是一个使用的截图,功能并不复杂但是能解决实际问题,给大家作为参考,有想法再结合自己的技术能力+AI很快可以完成构建,大家共勉。 图标是nano生成的;代码是gimini;

1. 项目背景
1.1 QA团队的工作困境
在敏捷开发模式下,QA(质量保证)团队每天需要处理大量的Bug报告。传统的Bug提交流程包含以下步骤:
- 发现页面问题 → 手动截图(Print Screen或截图工具)
- 保存图片到本地 → 打开工单系统(TodoList/Jira)
- 上传截图附件 → 复制页面URL链接
- 填写Bug信息 → 选择指派人员、优先级、分类
- 提交工单 → 返回测试页面继续工作
整个流程平均耗时2分钟,不仅打断了测试的心流,还容易因繁琐操作导致信息遗漏。
1.2 技术选型考量
基于对问题的分析,我们决定开发一个Chrome浏览器扩展,主要原因:
- 无侵入性:不需要修改现有业务系统
- 跨平台性:Chrome扩展可以在Windows、Mac、Linux上运行
- API丰富:提供了截图、存储、消息传递等完善的API
- 部署简单:通过Chrome Web Store或内部分发即可
2. 解决方案设计
2.1 核心功能特性
TJ Bug Reporter提供了一站式的Bug提交流程:
- 🎯 一键智能截图:点击悬浮球自动截取当前页面,截图时自动隐藏插件UI
- 📎 多格式附件支持:支持图片、Word、Excel、PDF、视频等多种格式
- 🤖 自动化信息填充:自动提取页面URL、记忆上次的优先级设置
- 🔍 人员智能检索:实时搜索联想内部人员,支持Debounce防抖
- ⌨️ 快捷键操作:ESC键关闭弹窗,提升操作效率
2.2 用户体验设计
我们采用了非模态交互的设计理念:
- 悬浮球常驻:始终可见但不干扰正常浏览
- 拖拽定位:用户可以根据习惯调整位置
- 即时反馈:Loading状态、成功提示等视觉反馈
- 键盘友好:支持ESC等快捷键操作
截图位置:[插件界面展示] 展示悬浮球、弹窗界面、附件上传等核心功能的截图
3. 技术架构
3.1 Chrome Extension Manifest V3架构
json
{
"manifest_version": 3,
"name": "TJ Bug Reporter",
"permissions": ["activeTab", "storage"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}]
}
核心组件说明:
- Content Script:注入到页面中,负责UI渲染和用户交互
- Background Service Worker:后台服务,处理跨域请求和文件上传
- Storage API:持久化存储用户配置和历史记录
- Tabs API:获取页面截图信息
3.2 Shadow DOM样式隔离
为了解决插件与宿主页面的样式冲突问题,我们采用了Shadow DOM技术:
javascript
// 创建隔离的DOM环境
const container = document.createElement('div');
container.id = 'bug-reporter-host';
document.body.appendChild(container);
const shadow = container.attachShadow({ mode: 'open' });
// 将插件HTML注入到Shadow DOM中
shadow.innerHTML = await fetch(chrome.runtime.getURL('panel.html')).text();
这种方案确保了插件的样式"进不去也出不来",实现了完美的样式隔离。
4. 核心技术难点与解决方案
4.1 CORS跨域问题的代理模式解决方案
问题描述
插件运行在业务页面(如 company-website.com)的Content Script环境中,当直接调用内部工单系统API(如 internal-system.com/api/bugs)时,浏览器会拦截请求:
rust
Access to fetch at 'https://internal-system.com/api/bugs' from origin
'https://company-website.com' has been blocked by CORS policy
解决方案设计
我们采用了Chrome Extension架构特性的代理模式:
scss
┌─────────────────┐ sendMessage ┌──────────────────┐ fetch ┌─────────────────┐
│ Content Script │ ────────────────→ │ Background │ ───────────→ │ Internal API │
│ (业务页面环境) │ │ Service Worker │ │ (内部系统) │
└─────────────────┘ │ (独立环境) │ └─────────────────┘
└──────────────────┘
实现细节
1. Content Script端 - 数据收集与请求转发
javascript
// content.js
async function submitBug(bugData) {
return new Promise((resolve) => {
chrome.runtime.sendMessage({
action: 'PROXY_FETCH',
data: {
url: 'https://internal-system.com/api/bugs',
options: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(bugData)
}
}
}, (response) => {
resolve(response);
});
});
}
2. Background Service Worker端 - 真实请求执行
javascript
// background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'PROXY_FETCH') {
const { url, options } = request.data;
fetch(url, options)
.then(res => res.json())
.then(data => {
sendResponse({ success: true, data: data });
})
.catch(err => {
sendResponse({ success: false, error: err.message });
});
return true; // 保持消息通道开放
}
});
3. 文件上传的特殊处理
文件上传需要特殊处理,因为文件对象无法直接通过消息传递:
javascript
// Content Script - 将文件转为Base64
function prepareFileForUpload(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => {
resolve({
name: file.name,
type: file.type,
size: file.size,
base64: reader.result
});
};
reader.readAsDataURL(file);
});
}
// Background - 还原文件并上传
function dataURItoBlob(dataURI) {
const byteString = atob(dataURI.split(',')[1]);
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeString });
}
4.2 Shadow DOM样式隔离技术实现
问题背景
插件需要注入到各种不同的业务页面中,面临样式冲突的双重挑战:
- 宿主页面污染插件 :页面的全局CSS(如
div { color: red !important })会破坏插件样式 - 插件影响宿主页面:插件的CSS可能意外修改页面元素样式
Shadow DOM解决方案
1. 创建隔离环境
javascript
function createIsolatedEnvironment() {
// 创建宿主容器
const host = document.createElement('div');
host.id = 'bug-reporter-host';
host.style.cssText = `
all: initial;
position: fixed;
top: 0;
left: 0;
z-index: 999999;
`;
document.body.appendChild(host);
// 创建Shadow DOM
const shadow = host.attachShadow({ mode: 'open' });
// 注入样式和HTML
shadow.innerHTML = `
<style>
/* 这里定义插件的所有样式,完全隔离 */
.container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto;
}
/* ... 其他样式 ... */
</style>
<div class="container">
<!-- 插件HTML结构 -->
</div>
`;
return shadow;
}
2. 样式封装策略
css
/* 使用CSS选择器确保样式不会泄漏 */
:host {
all: initial;
}
:host .bug-reporter-panel {
/* 插件专用样式前缀 */
}
/* 使用:defined避免继承宿主样式 */
:host * {
box-sizing: border-box;
}
3. JavaScript访问控制
javascript
// 获取Shadow DOM内部的元素
function getShadowElement(selector) {
const shadowHost = document.getElementById('bug-reporter-host');
if (shadowHost && shadowHost.shadowRoot) {
return shadowHost.shadowRoot.querySelector(selector);
}
return null;
}
// 事件委托处理
shadow.addEventListener('click', (event) => {
const target = event.target;
if (target.matches('.close-button')) {
closePanel();
} else if (target.matches('.submit-button')) {
submitBug();
}
});
4.3 截图时的UI隐藏机制
为了获得纯净的页面截图,我们实现了"隐身截图"功能:
javascript
async function captureCleanScreenshot() {
return new Promise((resolve) => {
// 1. 隐藏插件UI
const container = document.getElementById('bug-reporter-host');
container.style.display = 'none';
// 2. 等待浏览器重绘
setTimeout(() => {
// 3. 执行截图
chrome.runtime.sendMessage({ action: 'CAPTURE_SCREENSHOT' }, (response) => {
if (response.success) {
// 4. 恢复UI显示
container.style.display = 'block';
resolve(response.dataUrl);
}
});
}, 100); // 给浏览器足够时间重绘
});
}
4.4 动态DOM生命周期管理
Shadow DOM中的元素引用会在某些操作后失效,我们采用即时查找策略:
javascript
// ❌ 错误做法:缓存DOM引用
const modal = shadow.getElementById('img-modal');
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
modal.style.display = 'none'; // 可能失效
}
});
// ✅ 正确做法:即时查找
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const modal = shadow.getElementById('img-modal');
if (modal && modal.style.display === 'flex') {
modal.style.display = 'none';
}
}
});
5. 项目成果与价值
5.1 量化效率提升
| 指标 | 传统流程 | 使用插件后 | 提升幅度 |
|---|---|---|---|
| 单个Bug提交流程 | 120秒 | 30秒 | 75% |
| 日均Bug提交量 | 40个 | 60个 | 50% |
| 信息完整性 | 85% | 98% | 15% |
| 重复操作次数 | 10次/Bug | 2次/Bug | 80% |
5.2 团队协作改善
- QA团队:测试心流不再被打断,可以专注于发现更多问题
- 开发团队:收到的Bug报告信息更完整,减少了反复沟通
- 项目经理:可以实时跟踪Bug发现和修复进度
5.3 技术沉淀
- 可复用的跨域代理模式
- Shadow DOM样式隔离的最佳实践
- Chrome Extension开发的完整经验
截图位置:[数据对比图表] 展示效率提升的具体数据可视化图表
6. 项目总结与展望
6.1 关键经验
- 技术选型要匹配业务场景:Chrome Extension完美契合了"无需修改现有系统"的需求
- 架构设计要考虑扩展性:代理模式不仅解决了CORS问题,还为未来功能扩展留下了空间
- 用户体验决定产品成败:非侵入式设计和快捷键支持大大提升了用户接受度
6.2 未来优化方向
- 智能Bug分类:使用机器学习自动识别Bug类型和优先级
- 批量操作:支持一次性提交多个相关Bug
- 移动端支持:开发移动端浏览器扩展或独立应用
- 数据分析面板:提供Bug趋势分析和团队效率报表
6.3 开源贡献计划
我们计划将核心组件开源,包括:
- Shadow DOM样式隔离框架
- Chrome Extension跨域代理模板
- 文件上传处理工具类
技术栈总结
- 核心语言:JavaScript (ES6+), HTML5, CSS3
- 浏览器API:Chrome Extension Manifest V3 (Storage, Runtime, Tabs, Scripting)
- 架构模式:Shadow DOM, Service Worker, Message Passing
- 开发工具:VSCode, Chrome DevTools, Git
💡 核心启示:通过巧妙的技术架构设计,我们不仅解决了业务痛点,还创造了一款真正提升团队效率的工具。这正是技术赋能业务的最佳实践。