@@ -205,9 +205,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
205205 try {
206206 this . _postMessage ( { command : "testStarted" } ) ;
207207
208- // 依次执行每个蓝本,汇总结果
208+ // 依次执行每个蓝本,汇总结果(用户停止时中断后续蓝本)
209209 const results : TestReportResponse [ ] = [ ] ;
210+ let userStopped = false ;
210211 for ( const bp of msg . blueprint_paths ) {
212+ if ( userStopped ) { break ; }
211213 try {
212214 const platform = ( msg . platform || "web" ) . toLowerCase ( ) ;
213215 let report : TestReportResponse ;
@@ -237,6 +239,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
237239 }
238240
239241 results . push ( report ) ;
242+
243+ // 检查是否被用户停止,停止则中断后续蓝本
244+ if ( report . stopped ) {
245+ userStopped = true ;
246+ }
240247 } catch ( err : unknown ) {
241248 const errMsg = err instanceof Error ? err . message : String ( err ) ;
242249 results . push ( {
@@ -262,7 +269,10 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
262269 const passRate = totalSteps > 0 ? ( passedSteps / totalSteps * 100 ) : 0 ;
263270
264271 let md = `# 批量蓝本测试汇总\n\n` ;
265- md += `- 蓝本数: ${ results . length } \n` ;
272+ if ( userStopped ) {
273+ md += `> ⚠️ 用户手动停止,已执行 ${ results . length } /${ msg . blueprint_paths . length } 个蓝本\n\n` ;
274+ }
275+ md += `- 蓝本数: ${ results . length } ${ userStopped ? `/${ msg . blueprint_paths . length } ` : "" } \n` ;
266276 md += `- 总步骤: ${ totalSteps } (通过 ${ passedSteps } / 失败 ${ failedSteps } )\n` ;
267277 md += `- 总Bug数: ${ totalBugs } \n` ;
268278 md += `- 总通过率: ${ passRate . toFixed ( 0 ) } %\n` ;
@@ -724,6 +734,7 @@ ${commonRules}`;
724734 const platform = ( msg . platform || "web" ) . toLowerCase ( ) ;
725735 try {
726736 if ( platform === "android" || platform === "ios" ) {
737+ // 只检查设备是否连接,Session 由引擎在蓝本测试时自动创建
727738 const devices = await this . _client . listMobileDevices ( ) ;
728739 if ( ( devices . count || 0 ) === 0 ) {
729740 this . _postMessage ( {
@@ -737,31 +748,14 @@ ${commonRules}`;
737748 return ;
738749 }
739750
740- const sessions = await this . _client . listMobileSessions ( ) ;
741- if ( ( sessions . count || 0 ) === 0 ) {
742- const first = devices . devices ?. [ 0 ] || { } ;
743- const deviceName = String ( ( first as Record < string , unknown > ) . model || ( first as Record < string , unknown > ) . serial || "" ) ;
744- const created = await this . _client . createMobileSession ( { device_name : deviceName } ) ;
745- this . _postMessage ( {
746- command : "platformPrecheckResult" ,
747- data : {
748- ok : true ,
749- platform,
750- message : `设备已连接,已创建会话 ${ created . session_id } ,可以开始测试。` ,
751- mobile_session_id : created . session_id ,
752- } ,
753- } ) ;
754- return ;
755- }
756-
757- const sid = String ( ( sessions . sessions ?. [ 0 ] as Record < string , unknown > ) ?. session_id || "" ) ;
751+ const first = devices . devices ?. [ 0 ] || { } ;
752+ const model = String ( ( first as Record < string , unknown > ) . model || ( first as Record < string , unknown > ) . serial || "设备" ) ;
758753 this . _postMessage ( {
759754 command : "platformPrecheckResult" ,
760755 data : {
761756 ok : true ,
762757 platform,
763- message : `检测到已连接设备和活跃会话 ${ sid } ,可以开始测试。` ,
764- mobile_session_id : sid ,
758+ message : `设备 ${ model } 已连接,Appium Session 将自动创建。` ,
765759 } ,
766760 } ) ;
767761 return ;
@@ -814,7 +808,7 @@ ${commonRules}`;
814808 }
815809
816810 private async _handleConnectDevice ( _msg : { platform ?: string } ) : Promise < void > {
817- const { exec } = require ( "child_process" ) as typeof import ( "child_process" ) ;
811+ const { exec, spawn } = require ( "child_process" ) as typeof import ( "child_process" ) ;
818812 const run = ( cmd : string ) : Promise < string > =>
819813 new Promise ( ( resolve ) => {
820814 exec ( cmd , { timeout : 10000 } , ( _err , stdout ) => resolve ( stdout || "" ) ) ;
@@ -852,11 +846,29 @@ ${commonRules}`;
852846 fail ( `设备 ${ model } 未安装 Appium 自动化组件(uiautomator2),首次测试时会自动安装。\n也可手动运行:appium driver install uiautomator2` ) ;
853847 return ;
854848 }
855- // 4. 检查 Appium server 是否运行
856- const appiumOk = await httpCheck ( "http://127.0.0.1:4723/status" ) ;
849+ // 4. 检查 Appium server 是否运行,未运行则自动启动
850+ let appiumOk = await httpCheck ( "http://127.0.0.1:4723/status" ) ;
857851 if ( ! appiumOk ) {
858- fail ( `设备 ${ model } 就绪,但 Appium 未运行。\n请先在终端运行:appium\n或点击"启动引擎"按钮` ) ;
859- return ;
852+ // 自动启动 Appium
853+ this . _postMessage ( {
854+ command : "connectDeviceResult" ,
855+ data : { ok : false , message : `设备 ${ model } 就绪,Appium 未运行,正在自动启动...` } ,
856+ } ) ;
857+ try {
858+ spawn ( "appium" , [ "--port" , "4723" ] , { detached : true , stdio : "ignore" , shell : true , windowsHide : true } ) . unref ( ) ;
859+ } catch ( spawnErr : unknown ) {
860+ fail ( `Appium 启动失败: ${ spawnErr instanceof Error ? spawnErr . message : String ( spawnErr ) } \n请手动运行:appium --port 4723` ) ;
861+ return ;
862+ }
863+ // 等待 Appium 启动(最多12秒)
864+ for ( let i = 0 ; i < 12 ; i ++ ) {
865+ await new Promise < void > ( ( r ) => setTimeout ( r , 1000 ) ) ;
866+ if ( await httpCheck ( "http://127.0.0.1:4723/status" ) ) { appiumOk = true ; break ; }
867+ }
868+ if ( ! appiumOk ) {
869+ fail ( `设备 ${ model } 就绪,但 Appium 启动超时(12秒)。\n请手动运行:appium --port 4723` ) ;
870+ return ;
871+ }
860872 }
861873 // 5. 检查引擎
862874 const engineOk = await httpCheck ( "http://127.0.0.1:8900/health" ) ;
@@ -1062,11 +1074,6 @@ ${commonRules}`;
10621074 </div>
10631075 <div style="display:flex;gap:4px;margin-top:2px">
10641076 <button id="btnDetectDevice" class="btn-secondary" style="font-size:10px;padding:3px 6px;flex:1">检测设备</button>
1065- <button id="btnHandshake" class="btn-secondary hidden" style="font-size:10px;padding:3px 6px;flex:1">🤝 握手</button>
1066- </div>
1067- <div id="handshakeStatusRow" class="hidden" style="display:flex;align-items:center;gap:4px;margin-top:4px;font-size:10px">
1068- <span id="handshakeIcon">⏳</span>
1069- <span id="handshakeText" style="color:var(--muted);word-break:break-word">未握手</span>
10701077 </div>
10711078 </div>
10721079 <button id="btnLaunchEngine" class="hidden" style="background:#22c55e;margin-top:6px">🚀 一键启动引擎</button>
@@ -1273,7 +1280,6 @@ ${commonRules}`;
12731280 } else if (platform === "android") {
12741281 deviceRow.classList.remove("hidden");
12751282 document.getElementById("btnDetectDevice").style.display = "";
1276- document.getElementById("btnHandshake").classList.remove("hidden");
12771283 checkDeviceStatus();
12781284 } else {
12791285 deviceRow.classList.add("hidden");
@@ -1295,21 +1301,6 @@ ${commonRules}`;
12951301 checkDeviceStatus();
12961302 });
12971303
1298- // 握手按钮
1299- document.getElementById("btnHandshake").addEventListener("click", () => {
1300- const btn = document.getElementById("btnHandshake");
1301- const row = document.getElementById("handshakeStatusRow");
1302- const icon = document.getElementById("handshakeIcon");
1303- const text = document.getElementById("handshakeText");
1304- btn.disabled = true;
1305- btn.textContent = "⏳ 握手中...";
1306- row.classList.remove("hidden");
1307- icon.textContent = "⏳";
1308- text.textContent = "正在检测 Appium 环境...";
1309- text.style.color = "var(--muted)";
1310- vscode.postMessage({ command: "connectDevice", platform: getCurrentPlatform() });
1311- });
1312-
13131304 // 刷新项目按钮
13141305 document.getElementById("btnRefreshProjects").addEventListener("click", () => {
13151306 vscode.postMessage({ command: "scanBlueprints" });
@@ -1440,7 +1431,6 @@ ${commonRules}`;
14401431 addLog("正在断开引擎...", "info");
14411432 });
14421433
1443- let currentMobileSessionId = "";
14441434 let pendingBlueprintRun = null;
14451435
14461436 // 蓝本测试(支持多选批量 + 平台路由 + 前置检查)
@@ -1508,45 +1498,21 @@ ${commonRules}`;
15081498 case "blueprintSelected": onBlueprintSelected(msg.data); break;
15091499 case "platformPrecheckResult": onPlatformPrecheckResult(msg.data); break;
15101500 case "deviceStatusResult": onDeviceStatusResult(msg.data); break;
1511- case "connectDeviceResult": onConnectDeviceResult(msg.data); break;
15121501 }
15131502 });
15141503
15151504 function onDeviceStatusResult(data) {
15161505 const statusText = document.getElementById("deviceStatusText");
15171506 const statusIcon = document.getElementById("deviceStatusIcon");
1518- const btnHandshake = document.getElementById("btnHandshake");
15191507 if (!data) return;
15201508 if (data.connected) {
1521- statusText.textContent = data.message || "设备已连接";
1509+ statusText.textContent = ( data.message || "设备已连接") + "(运行蓝本时自动连接) ";
15221510 statusText.style.color = "var(--success,#22c55e)";
15231511 statusIcon.textContent = "✅";
1524- btnHandshake.classList.remove("hidden");
15251512 } else {
15261513 statusText.textContent = data.message || "未检测到设备";
15271514 statusText.style.color = "var(--error,#ef4444)";
15281515 statusIcon.textContent = "❌";
1529- btnHandshake.classList.add("hidden");
1530- document.getElementById("handshakeStatusRow").classList.add("hidden");
1531- }
1532- }
1533-
1534- function onConnectDeviceResult(data) {
1535- const row = document.getElementById("handshakeStatusRow");
1536- const icon = document.getElementById("handshakeIcon");
1537- const text = document.getElementById("handshakeText");
1538- const btn = document.getElementById("btnHandshake");
1539- row.classList.remove("hidden");
1540- btn.disabled = false;
1541- btn.textContent = "🤝 握手";
1542- if (data.ok) {
1543- icon.textContent = "✅";
1544- text.textContent = data.message || "握手成功";
1545- text.style.color = "var(--success,#22c55e)";
1546- } else {
1547- icon.textContent = "❌";
1548- text.textContent = data.message || "握手失败";
1549- text.style.color = "var(--error,#ef4444)";
15501516 }
15511517 }
15521518
@@ -1560,10 +1526,6 @@ ${commonRules}`;
15601526 return;
15611527 }
15621528
1563- if (data.mobile_session_id) {
1564- currentMobileSessionId = data.mobile_session_id;
1565- }
1566-
15671529 addLog(data.message || "平台检查通过", "success");
15681530
15691531 const run = pendingBlueprintRun;
@@ -1577,15 +1539,13 @@ ${commonRules}`;
15771539 blueprint_paths: run.paths,
15781540 base_url: run.baseUrl,
15791541 platform: run.platform,
1580- mobile_session_id: currentMobileSessionId || undefined,
15811542 });
15821543 } else {
15831544 vscode.postMessage({
15841545 command: "blueprintTest",
15851546 blueprint_path: run.paths[0],
15861547 base_url: run.baseUrl,
15871548 platform: run.platform,
1588- mobile_session_id: currentMobileSessionId || undefined,
15891549 });
15901550 }
15911551 }
@@ -1826,18 +1786,9 @@ ${commonRules}`;
18261786 stepList.innerHTML = "";
18271787 stepSection.classList.add("hidden");
18281788 logArea.innerHTML = "";
1829- addLog("测试任务已启动...", "info");
1830- // 持续跳动提示,让用户知道系统在运行
1831- let dots = 0;
1832- const phases = ["正在连接模拟器...", "正在执行测试步骤...", "仍在测试中,请耐心等待..."];
1833- let phase = 0;
1789+ addLog("测试任务已启动,步骤进度将实时显示...", "info");
18341790 if (testingTimer) clearInterval(testingTimer);
1835- testingTimer = setInterval(() => {
1836- dots = (dots + 1) % 4;
1837- const dotStr = ".".repeat(dots + 1);
1838- addLog("⏳ " + phases[phase] + dotStr, "info");
1839- if (dots === 3) phase = Math.min(phase + 1, phases.length - 1);
1840- }, 5000);
1791+ testingTimer = null;
18411792 }
18421793
18431794 function onTestResult(report) {
0 commit comments