Skip to content

Commit 078a29c

Browse files
committed
fix: 恢复双按钮(一键启动引擎+断开引擎) + 小程序测试实时步骤进度推送
1 parent 0435528 commit 078a29c

2 files changed

Lines changed: 88 additions & 66 deletions

File tree

extension/src/sidebarProvider.ts

Lines changed: 47 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,8 @@ ${commonRules}`;
11551155
</div>
11561156
</div>
11571157
<div style="display:flex;gap:4px;margin-top:6px">
1158-
<button id="btnLaunchEngine" style="background:#22c55e;flex:1">� 一键连接</button>
1158+
<button id="btnLaunchEngine" style="background:#22c55e;flex:1">🚀 一键启动引擎</button>
1159+
<button id="btnStopEngine" class="hidden" style="background:#ef4444;flex:1">⏹ 断开引擎</button>
11591160
</div>
11601161
</div>
11611162
@@ -1528,67 +1529,54 @@ ${commonRules}`;
15281529
15291530
// 检查引擎
15301531
const btnLaunchEngine = document.getElementById("btnLaunchEngine");
1532+
const btnStopEngine = document.getElementById("btnStopEngine");
15311533
let isStarting = false; // 是否处于「正在启动中」状态
15321534
let engineFound = false; // 引擎是否已连接成功(防止重复检查)
1533-
let engineConnected = false; // 当前是否已连接
15341535
let launchTimeoutId = null; // 防止多次点击导致旧 timer 覆盖按钮状态
15351536
document.getElementById("btnCheckEngine").addEventListener("click", () => {
15361537
vscode.postMessage({ command: "checkEngine" });
15371538
});
15381539
1539-
// 单按钮:连接时点击=断开,断开时点击=连接
1540+
// 一键启动引擎
15401541
btnLaunchEngine.addEventListener("click", () => {
1541-
if (engineConnected) {
1542-
// 已连接 → 断开
1543-
isStarting = false;
1544-
wasConnected = false;
1545-
engineFound = false;
1546-
engineConnected = false;
1547-
vscode.postMessage({ command: "stopEngine" });
1548-
btnLaunchEngine.textContent = "⏳ 断开中...";
1549-
btnLaunchEngine.style.background = "#6b7280";
1550-
btnLaunchEngine.disabled = true;
1551-
addLog("正在断开引擎...", "info");
1552-
setTimeout(() => {
1553-
btnLaunchEngine.textContent = "🔗 一键连接";
1554-
btnLaunchEngine.style.background = "#22c55e";
1555-
btnLaunchEngine.disabled = false;
1556-
}, 2000);
1557-
} else {
1558-
// 未连接 → 启动
1559-
isStarting = true;
1560-
engineFound = false;
1561-
vscode.postMessage({ command: "launchEngine" });
1562-
btnLaunchEngine.textContent = "⏳ 启动中...";
1563-
btnLaunchEngine.style.background = "#6b7280";
1564-
btnLaunchEngine.disabled = true;
1565-
addLog("正在启动引擎,请稍候...", "info");
1566-
1567-
// 清除旧 timer,防止多次点击互相干扰
1568-
if (launchTimeoutId) { clearTimeout(launchTimeoutId); launchTimeoutId = null; }
1569-
1570-
// 每 3s 检查一次,最多 8 次(24s)
1571-
let checkCount = 0;
1572-
function doCheck() {
1573-
if (engineFound || !isStarting) { return; }
1574-
checkCount++;
1542+
isStarting = true;
1543+
engineFound = false;
1544+
vscode.postMessage({ command: "launchEngine" });
1545+
btnLaunchEngine.classList.add("hidden");
1546+
btnStopEngine.classList.remove("hidden");
1547+
btnStopEngine.textContent = "⏹ 断开引擎";
1548+
btnStopEngine.disabled = false;
1549+
addLog("正在启动引擎,请稍候(最长约25秒)...", "info");
1550+
1551+
// 清除旧 timer,防止多次点击互相干扰
1552+
if (launchTimeoutId) { clearTimeout(launchTimeoutId); launchTimeoutId = null; }
1553+
1554+
// 分别在 8s / 16s / 25s 重试检查连接,连接成功后停止后续检查
1555+
const delays = [8000, 16000, 25000];
1556+
function scheduleCheck(idx) {
1557+
if (idx >= delays.length || engineFound) { return; }
1558+
launchTimeoutId = setTimeout(() => {
1559+
if (engineFound) { return; }
15751560
vscode.postMessage({ command: "checkEngine" });
1576-
if (checkCount < 8) {
1577-
launchTimeoutId = setTimeout(doCheck, 3000);
1578-
} else {
1579-
launchTimeoutId = setTimeout(() => {
1580-
if (!engineFound) {
1581-
isStarting = false;
1582-
btnLaunchEngine.textContent = "🔗 一键连接";
1583-
btnLaunchEngine.style.background = "#22c55e";
1584-
btnLaunchEngine.disabled = false;
1585-
addLog("引擎启动超时,请检查环境后重试", "error");
1586-
}
1587-
}, 1000);
1588-
}
1589-
}
1590-
launchTimeoutId = setTimeout(doCheck, 3000);
1561+
scheduleCheck(idx + 1);
1562+
}, delays[idx] - (idx > 0 ? delays[idx - 1] : 0));
15911563
}
1564+
scheduleCheck(0);
1565+
});
1566+
1567+
// 断开引擎
1568+
btnStopEngine.addEventListener("click", () => {
1569+
isStarting = false;
1570+
wasConnected = false;
1571+
engineFound = false;
1572+
vscode.postMessage({ command: "stopEngine" });
1573+
btnStopEngine.textContent = "⏳ 断开中...";
1574+
btnStopEngine.disabled = true;
1575+
addLog("正在断开引擎...", "info");
1576+
setTimeout(() => {
1577+
btnLaunchEngine.classList.remove("hidden");
1578+
btnStopEngine.classList.add("hidden");
1579+
}, 2000);
15921580
});
15931581
15941582
let pendingBlueprintRun = null;
@@ -1935,28 +1923,26 @@ ${commonRules}`;
19351923
if (data.connected) {
19361924
isStarting = false;
19371925
engineFound = true;
1938-
engineConnected = true;
19391926
statusDot.className = "status-dot connected";
19401927
engineStatus.textContent = "v" + (data.version || "?");
1941-
btnLaunchEngine.textContent = "⏹ 断开";
1942-
btnLaunchEngine.style.background = "#ef4444";
1943-
btnLaunchEngine.disabled = false;
1928+
btnLaunchEngine.classList.add("hidden");
1929+
btnStopEngine.classList.remove("hidden");
1930+
btnStopEngine.textContent = "⏹ 断开引擎";
1931+
btnStopEngine.disabled = false;
19441932
if (!wasConnected) {
19451933
wasConnected = true;
19461934
addLog("引擎连接成功 | v" + data.version, "success");
19471935
vscode.postMessage({ command: "scanBlueprints" });
19481936
}
19491937
} else {
1950-
engineConnected = false;
19511938
statusDot.className = "status-dot disconnected";
19521939
engineStatus.textContent = isStarting ? "启动中..." : "未连接";
1953-
btnLaunchEngine.textContent = isStarting ? "⏳ 启动中..." : "🔗 一键连接";
1954-
btnLaunchEngine.style.background = isStarting ? "#6b7280" : "#22c55e";
1955-
btnLaunchEngine.disabled = isStarting;
19561940
if (!isStarting) {
1941+
btnLaunchEngine.classList.remove("hidden");
1942+
btnStopEngine.classList.add("hidden");
19571943
wasConnected = false;
19581944
}
1959-
addLog(isStarting ? "引擎尚未就绪,继续等待..." : "引擎未连接,点击「一键连接」按钮启动", isStarting ? "warn" : "error");
1945+
addLog(isStarting ? "引擎尚未就绪,继续等待..." : "引擎未连接,点击「🚀 一键启动引擎」按钮启动", isStarting ? "warn" : "error");
19601946
}
19611947
}
19621948

src/api/routes.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -781,15 +781,48 @@ async def run_miniprogram_blueprint_test(req: dict) -> TestReportResponse:
781781
logger.info("启动Node.js执行器: {}", runner_script.name)
782782
start = time.time()
783783

784+
# 用Popen实时读取stderr进度并推送WebSocket
785+
await ws_manager.send_log(f"小程序蓝本测试开始: {blueprint.app_name} | {len(all_steps)}步")
784786
proc = await asyncio.get_event_loop().run_in_executor(
785787
None,
786-
lambda: sp.run(
788+
lambda: sp.Popen(
787789
["node", str(runner_script), str(tmp_file)],
788-
capture_output=True, timeout=600,
790+
stdout=sp.PIPE, stderr=sp.PIPE,
789791
encoding="utf-8", errors="replace",
790792
)
791793
)
792794

795+
# 实时读stderr推送步骤进度(线程读取 + 协程推送)
796+
import threading
797+
_stderr_buf = []
798+
_stderr_done = threading.Event()
799+
loop = asyncio.get_event_loop()
800+
801+
def _stream_stderr():
802+
for line in proc.stderr:
803+
_stderr_buf.append(line.rstrip())
804+
_stderr_done.set()
805+
806+
stderr_thread = threading.Thread(target=_stream_stderr, daemon=True)
807+
stderr_thread.start()
808+
809+
_last_pushed = 0
810+
while not _stderr_done.is_set() or _last_pushed < len(_stderr_buf):
811+
await asyncio.sleep(0.3)
812+
while _last_pushed < len(_stderr_buf):
813+
line = _stderr_buf[_last_pushed]
814+
_last_pushed += 1
815+
if line.startswith("[PROGRESS]"):
816+
parts = line[len("[PROGRESS]"):].strip()
817+
await ws_manager.send_step_start(0, f"🔄 {parts}")
818+
elif line.startswith("[STEP]"):
819+
parts = line[len("[STEP]"):].strip()
820+
await ws_manager.send_log(parts)
821+
822+
# 等Node进程结束,读取stdout
823+
stdout_data = await loop.run_in_executor(None, proc.stdout.read)
824+
await loop.run_in_executor(None, proc.wait)
825+
793826
# 清理临时文件
794827
try:
795828
tmp_file.unlink()
@@ -800,8 +833,10 @@ async def run_miniprogram_blueprint_test(req: dict) -> TestReportResponse:
800833
logger.info("Node.js执行器完成 | 耗时:{:.1f}秒 | rc:{}", duration, proc.returncode)
801834

802835
# 解析结果
803-
stdout = proc.stdout or ""
804-
stderr = proc.stderr or ""
836+
stdout = stdout_data or ""
837+
stderr = "\n".join(_stderr_buf)
838+
839+
await ws_manager.send_log(f"小程序蓝本测试完成 | 耗时:{duration:.1f}秒")
805840

806841
# 从stdout找JSON(最后一行)
807842
result_data = None
@@ -816,8 +851,8 @@ async def run_miniprogram_blueprint_test(req: dict) -> TestReportResponse:
816851

817852
if not result_data:
818853
logger.error("执行器无JSON输出:\nstdout:{}\nstderr:{}", stdout[-300:], stderr[-300:])
819-
# crash时返回友好错误报告,不抛500
820854
hint = ""
855+
rc = proc.returncode
821856
if rc and rc != 0:
822857
hint = f"执行器异常退出(rc={rc})。可能原因:小程序代码修改后未重新编译,或模拟器状态异常。请在微信开发者工具中点击'编译'后重试。"
823858
else:
@@ -878,6 +913,7 @@ def _to_action(s: str) -> ActionType:
878913
)
879914

880915
pass_rate = passed / total * 100 if total > 0 else 0
916+
await ws_manager.send_test_done(pass_rate, len(bugs))
881917

882918
from src.api.models import StepDetail, BugDetail
883919
return TestReportResponse(

0 commit comments

Comments
 (0)