Skip to content

Commit c302f00

Browse files
committed
ci: GitHub Actions自动测试 + 插件蓝本自动扫描+MCP闭环提示
- CI/CD: .github/workflows/test.yml 每次push自动跑pytest - 插件: 蓝本路径自动扫描(workspace下testpilot.json自动发现) - 插件: 扫描按钮 + 浏览按钮 + 下拉选择 - 插件: 测试发现Bug后日志区提示MCP闭环修复方式
1 parent 930698e commit c302f00

2 files changed

Lines changed: 161 additions & 1 deletion

File tree

.github/workflows/test.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: TestPilot AI Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.11"]
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install Poetry
26+
run: pip install poetry
27+
28+
- name: Install dependencies
29+
run: |
30+
poetry config virtualenvs.in-project true
31+
poetry install --with dev
32+
33+
- name: Run tests
34+
run: poetry run pytest -x -q --tb=short
35+
env:
36+
OPENAI_API_KEY: "sk-test-placeholder"
37+
TESTPILOT_ENV: "ci"

extension/src/sidebarProvider.ts

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
6363
case "launchEngine":
6464
vscode.commands.executeCommand("testpilot-ai.launchEngine");
6565
break;
66+
case "scanBlueprints":
67+
await this._handleScanBlueprints();
68+
break;
69+
case "browseBlueprint":
70+
await this._handleBrowseBlueprint();
71+
break;
6672
}
6773
});
6874

@@ -216,6 +222,34 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
216222
vscode.window.showInformationMessage("Bug摘要已复制到剪贴板,粘贴到聊天窗口让AI修复");
217223
}
218224

225+
private async _handleScanBlueprints(): Promise<void> {
226+
const blueprints: string[] = [];
227+
const folders = vscode.workspace.workspaceFolders;
228+
if (folders) {
229+
for (const folder of folders) {
230+
const pattern = new vscode.RelativePattern(folder, "**/testpilot.json");
231+
const files = await vscode.workspace.findFiles(pattern, "**/node_modules/**", 20);
232+
for (const f of files) {
233+
blueprints.push(f.fsPath);
234+
}
235+
}
236+
}
237+
this._postMessage({ command: "blueprintList", data: blueprints });
238+
}
239+
240+
private async _handleBrowseBlueprint(): Promise<void> {
241+
const result = await vscode.window.showOpenDialog({
242+
canSelectFiles: true,
243+
canSelectFolders: false,
244+
canSelectMany: false,
245+
filters: { "TestPilot 蓝本": ["json"] },
246+
title: "选择蓝本文件 (testpilot.json)",
247+
});
248+
if (result && result.length > 0) {
249+
this._postMessage({ command: "blueprintSelected", data: result[0].fsPath });
250+
}
251+
}
252+
219253
private _getHtml(webview: vscode.Webview): string {
220254
const nonce = getNonce();
221255

@@ -256,6 +290,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
256290
background: var(--input-bg); color: var(--input-fg);
257291
border: 1px solid var(--input-border); border-radius: 3px;
258292
}
293+
.input-row { display: flex; gap: 4px; align-items: center; }
294+
.input-row select { flex: 1; }
295+
.input-row button { width: auto; min-width: 32px; margin-top: 0; font-size: 14px; }
259296
.checkbox-row { display: flex; align-items: center; gap: 6px; margin: 6px 0; }
260297
.checkbox-row input[type="checkbox"] { width: auto; }
261298
@@ -368,7 +405,12 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
368405
<!-- 蓝本模式 -->
369406
<div class="tab-content active" id="panelBlueprint">
370407
<label>蓝本文件路径 *</label>
371-
<input type="text" id="inputBlueprintPath" placeholder="D:\\projects\\my-app\\testpilot.json" />
408+
<div class="input-row">
409+
<select id="selectBlueprint" style="flex:1"><option value="">扫描中...</option></select>
410+
<button id="btnScanBp" title="扫描工作区" style="min-width:32px;padding:4px">🔍</button>
411+
<button id="btnBrowseBp" title="手动浏览" style="min-width:32px;padding:4px">📂</button>
412+
</div>
413+
<input type="text" id="inputBlueprintPath" placeholder="或手动输入路径..." style="font-size:11px;margin-top:4px" />
372414
373415
<label>覆盖 base_url(可选)</label>
374416
<input type="text" id="inputBpBaseUrl" placeholder="http://localhost:3000" />
@@ -495,6 +537,35 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
495537
panelExplore.classList.add("active"); panelBlueprint.classList.remove("active");
496538
});
497539
540+
// 蓝本自动扫描
541+
const selectBlueprint = document.getElementById("selectBlueprint");
542+
const inputBlueprintPath = document.getElementById("inputBlueprintPath");
543+
544+
// 下拉选择 → 同步到手动输入框
545+
selectBlueprint.addEventListener("change", () => {
546+
if (selectBlueprint.value) {
547+
inputBlueprintPath.value = selectBlueprint.value;
548+
}
549+
});
550+
551+
// 手动输入 → 清除下拉选中
552+
inputBlueprintPath.addEventListener("input", () => {
553+
selectBlueprint.value = "";
554+
});
555+
556+
// 扫描按钮
557+
document.getElementById("btnScanBp").addEventListener("click", () => {
558+
vscode.postMessage({ command: "scanBlueprints" });
559+
});
560+
561+
// 浏览按钮
562+
document.getElementById("btnBrowseBp").addEventListener("click", () => {
563+
vscode.postMessage({ command: "browseBlueprint" });
564+
});
565+
566+
// 页面加载时自动扫描
567+
vscode.postMessage({ command: "scanBlueprints" });
568+
498569
// 元素引用
499570
const statusDot = document.getElementById("statusDot");
500571
const engineStatus = document.getElementById("engineStatus");
@@ -630,9 +701,56 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
630701
case "progress": onProgress(msg.data); break;
631702
case "controlResult": addLog("控制: " + msg.data.action + " → " + msg.data.state, "info"); break;
632703
case "controlError": addLog("控制失败: " + msg.data.error, "error"); break;
704+
case "blueprintList": onBlueprintList(msg.data); break;
705+
case "blueprintSelected": onBlueprintSelected(msg.data); break;
633706
}
634707
});
635708
709+
function onBlueprintList(paths) {
710+
selectBlueprint.innerHTML = "";
711+
if (paths.length === 0) {
712+
const opt = document.createElement("option");
713+
opt.value = "";
714+
opt.textContent = "未找到 testpilot.json";
715+
selectBlueprint.appendChild(opt);
716+
return;
717+
}
718+
paths.forEach((p, i) => {
719+
const opt = document.createElement("option");
720+
opt.value = p;
721+
// 显示简短路径
722+
const parts = p.replace(/\\\\/g, "/").split("/");
723+
const short = parts.slice(-3).join("/");
724+
opt.textContent = short;
725+
selectBlueprint.appendChild(opt);
726+
});
727+
// 自动选中第一个并填入输入框
728+
selectBlueprint.selectedIndex = 0;
729+
inputBlueprintPath.value = paths[0];
730+
addLog("找到 " + paths.length + " 个蓝本文件", "info");
731+
}
732+
733+
function onBlueprintSelected(path) {
734+
inputBlueprintPath.value = path;
735+
// 在下拉里也加上(如果不在列表中)
736+
let found = false;
737+
for (let i = 0; i < selectBlueprint.options.length; i++) {
738+
if (selectBlueprint.options[i].value === path) {
739+
selectBlueprint.selectedIndex = i;
740+
found = true;
741+
break;
742+
}
743+
}
744+
if (!found) {
745+
const opt = document.createElement("option");
746+
opt.value = path;
747+
const parts = path.replace(/\\\\/g, "/").split("/");
748+
opt.textContent = parts.slice(-3).join("/");
749+
selectBlueprint.appendChild(opt);
750+
selectBlueprint.value = path;
751+
}
752+
}
753+
636754
function updateEngineStatus(data) {
637755
if (data.connected) {
638756
statusDot.className = "status-dot connected";
@@ -686,6 +804,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
686804
687805
// 渲染步骤详情
688806
renderSteps(report.steps || []);
807+
808+
// MCP闭环提示
809+
if (report.bug_count > 0) {
810+
addLog("💡 在Cascade聊天窗口说「帮我修复这些Bug」可自动闭环修复", "info");
811+
}
689812
}
690813
691814
function renderBugs(bugs) {

0 commit comments

Comments
 (0)