Skip to content

Commit 184d33e

Browse files
committed
插件增强:扫描排除test_fixtures+提示词按平台区分(Web/小程序7铁律/Android/桌面4选择器)+option加data-platform
1 parent a01c744 commit 184d33e

1 file changed

Lines changed: 167 additions & 23 deletions

File tree

extension/src/sidebarProvider.ts

Lines changed: 167 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
7777
await this._handleBrowseBlueprint();
7878
break;
7979
case "copyBlueprintPrompt":
80-
await this._handleCopyBlueprintPrompt();
80+
await this._handleCopyBlueprintPrompt(msg.platform || "web");
8181
break;
8282
case "platformPrecheck":
8383
await this._handlePlatformPrecheck(msg);
@@ -372,7 +372,7 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
372372
];
373373
const seen = new Set<string>();
374374
for (const pattern of patterns) {
375-
const files = await vscode.workspace.findFiles(pattern, "**/node_modules/**", 50);
375+
const files = await vscode.workspace.findFiles(pattern, "{**/node_modules/**,**/test_fixtures/**,**/.venv/**}", 50);
376376
for (const f of files) {
377377
if (seen.has(f.fsPath)) { continue; }
378378
seen.add(f.fsPath);
@@ -429,8 +429,156 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
429429
}
430430
}
431431

432-
private async _handleCopyBlueprintPrompt(): Promise<void> {
433-
const prompt = `请帮我为当前项目生成测试蓝本文件,放在项目的 testpilot/ 文件夹下。
432+
private async _handleCopyBlueprintPrompt(platform: string): Promise<void> {
433+
const commonRules = `要求:
434+
1. 分析源代码中所有可操作 UI 元素(按钮/表单/导航/弹窗)
435+
2. 选择器使用代码中的真实 id(如 #login-btn)或稳定 class,禁止用 div:nth-child(3) 这类脆弱选择器
436+
3. 每个功能页面对应一个场景,覆盖正常流程和异常场景(空表单提交、错误输入)
437+
4. 每个 fill 操作后必须有 assert_text 或 screenshot 验证
438+
5. 每次导航后必须有断言验证页面已正确加载
439+
6. 如果应用需要命令行启动(如 npm start、python app.py),必须填写 start_command 字段;纯HTML静态应用留空
440+
441+
重要提醒:以后修改代码时,主动审视并更新对应模块的蓝本,无需用户提醒!`;
442+
443+
let prompt = "";
444+
if (platform === "miniprogram") {
445+
prompt = `请帮我为当前【微信小程序】项目生成测试蓝本文件 testpilot/testpilot.json。
446+
447+
⚠️ 小程序蓝本与Web蓝本完全不同!必须严格遵守以下铁律:
448+
449+
【铁律1】evaluate 的 value 必须是可被 new Function(code) 包裹的纯JS代码字符串
450+
✅ "value": "(() => { const app = getApp(); return app.globalData.cart.length; })()"
451+
❌ "value": "() => getApp().globalData.cart.length"(箭头函数不能被new Function直接执行)
452+
453+
【铁律2】小程序没有 document 对象!evaluate 里只能用:
454+
✅ getApp() / getCurrentPages() / wx.xxx / Page方法
455+
❌ document.querySelector / window.location(这些在小程序里不存在)
456+
457+
【铁律3】每个场景的第一步必须是 reset_state(清空购物车等全局状态+reLaunch回首页)
458+
{"action": "reset_state", "description": "重置状态回首页"}
459+
460+
【铁律4】跨页面导航必须用 navigate_to,不能用 navigate
461+
{"action": "navigate_to", "value": "/pages/cart/cart", "description": "跳转到购物车页"}
462+
463+
【铁律5】call_method 的参数必须用 JSON 格式
464+
{"action": "call_method", "target": "onCategoryTap", "value": "{\\"detail\\": {\\"dataset\\": {\\"cat\\": \\"水果\\"}}}"}
465+
466+
【铁律6】assert_compare 的 value 格式为 "操作符 期望值"
467+
{"action": "assert_compare", "target": "#cartCount", "value": "> 0"}
468+
{"action": "assert_compare", "target": "#total", "value": "== 100"}
469+
470+
【铁律7】page_query 读取DOM文本/数量时用 value 指定返回类型
471+
{"action": "page_query", "target": ".product", "value": "count"} → 返回元素数量
472+
{"action": "page_query", "target": "#price", "value": "text"} → 返回文本内容
473+
474+
支持的 action(15种):
475+
reset_state / navigate_to / click / fill / call_method / evaluate / read_text / assert_text / assert_compare / page_query / tap_multiple / screenshot / wait / select / scroll
476+
477+
蓝本格式:
478+
{
479+
"app_name": "小程序名称",
480+
"description": "功能说明",
481+
"base_url": "miniprogram://项目绝对路径",
482+
"version": "1.0",
483+
"platform": "miniprogram",
484+
"pages": [
485+
{
486+
"url": "/pages/index/index",
487+
"title": "首页",
488+
"elements": { "元素描述": "CSS选择器(来自wxml)" },
489+
"scenarios": [
490+
{
491+
"name": "场景名",
492+
"steps": [
493+
{"action": "reset_state", "description": "重置状态回首页"},
494+
{"action": "read_text", "target": "#price", "description": "读取价格"},
495+
{"action": "evaluate", "value": "(() => { return getApp().globalData.isVip; })()", "expected": "true", "description": "验证会员状态"}
496+
]
497+
}
498+
]
499+
}
500+
]
501+
}
502+
503+
请先阅读项目中所有 .wxml 和 .js 文件,提取选择器和业务逻辑,再生成蓝本。
504+
505+
${commonRules}`;
506+
} else if (platform === "android") {
507+
prompt = `请帮我为当前【Android】项目生成测试蓝本文件 testpilot/testpilot.json。
508+
509+
蓝本格式:
510+
{
511+
"app_name": "应用名称",
512+
"description": "功能说明",
513+
"base_url": "http://被测网页URL(手机浏览器测试)",
514+
"version": "1.0",
515+
"platform": "android",
516+
"pages": [
517+
{
518+
"url": "/",
519+
"title": "页面标题",
520+
"elements": { "元素描述": "#CSS选择器" },
521+
"scenarios": [
522+
{
523+
"name": "场景名",
524+
"steps": [
525+
{"action": "navigate", "value": "http://被测URL"},
526+
{"action": "click", "target": "#btn"},
527+
{"action": "assert_text", "target": "#result", "expected": "预期文本"}
528+
]
529+
}
530+
]
531+
}
532+
]
533+
}
534+
535+
注意:Android测试通过手机浏览器执行,选择器与Web相同(CSS选择器)。
536+
如果是原生App测试,target改用 resource-id 格式(如 com.app:id/btnLogin)。
537+
538+
支持的 action:navigate / click / fill / select / wait / screenshot / assert_text / assert_visible / hover / scroll
539+
540+
${commonRules}`;
541+
} else if (platform === "desktop") {
542+
prompt = `请帮我为当前【Windows桌面应用】项目生成测试蓝本文件 testpilot/testpilot.json。
543+
544+
蓝本格式:
545+
{
546+
"app_name": "桌面应用名称",
547+
"description": "功能说明",
548+
"base_url": "desktop://应用名称",
549+
"version": "1.0",
550+
"platform": "desktop",
551+
"pages": [
552+
{
553+
"url": "/",
554+
"title": "主窗口",
555+
"elements": { "元素描述": "选择器" },
556+
"scenarios": [
557+
{
558+
"name": "场景名",
559+
"steps": [
560+
{"action": "click", "target": "name:按钮名称"},
561+
{"action": "fill", "target": "automationid:inputField", "value": "测试值"},
562+
{"action": "assert_text", "target": "name:结果标签", "expected": "预期文本"}
563+
]
564+
}
565+
]
566+
}
567+
]
568+
}
569+
570+
桌面选择器格式(4种):
571+
- name:XXX — 按UI元素Name属性查找
572+
- automationid:XXX — 按AutomationId查找(最稳定)
573+
- class:XXX — 按ClassName查找
574+
- point:X,Y — 按屏幕坐标点击(兜底方案)
575+
576+
支持的 action:click / fill / assert_text / screenshot / wait
577+
578+
${commonRules}`;
579+
} else {
580+
// Web(默认)
581+
prompt = `请帮我为当前【Web】项目生成测试蓝本文件,放在项目的 testpilot/ 文件夹下。
434582
435583
重要:按功能模块拆分成多个蓝本文件,不要创建单一的 testpilot.json!
436584
@@ -440,26 +588,15 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
440588
- testpilot/order.testpilot.json(订单管理)
441589
- testpilot/cart.testpilot.json(购物车)
442590
443-
要求:
444-
1. 分析源代码中所有可操作 UI 元素(按钮/表单/导航/弹窗)
445-
2. 选择器使用代码中的真实 id(如 #login-btn)或稳定 class,禁止用 div:nth-child(3) 这类脆弱选择器
446-
3. 每个功能页面对应一个场景,覆盖正常流程和异常场景(空表单提交、错误输入)
447-
4. 每个 fill 操作后必须有 assert_text 或 screenshot 验证
448-
5. 每次 navigate 必须有断言验证页面已正确加载
449-
6. 如果应用需要命令行启动(如 npm start、python app.py),必须填写 start_command 字段;纯HTML静态应用留空
450-
7. 如果 platform 是 miniprogram,必须固定自动化端口,不要依赖动态端口:
451-
- 在蓝本中写: "start_command": "cli auto --project . --auto-port 9420"
452-
- 或等效命令,核心是固定 --auto-port 9420
453-
454-
每个蓝本文件格式:
591+
蓝本文件格式:
455592
{
456-
"app_name": "模块名称(如:用户认证模块)",
457-
"description": "蓝本功能说明(50-200字,描述本蓝本覆盖哪些功能和测试范围)",
593+
"app_name": "模块名称",
594+
"description": "蓝本功能说明(50-200字)",
458595
"base_url": "http://localhost:端口",
459596
"version": "1.0",
460597
"platform": "web",
461598
"start_command": "npm start 或 python app.py(纯HTML留空)",
462-
"start_cwd": "./(启动命令的工作目录,默认项目根目录)",
599+
"start_cwd": "./",
463600
"pages": [
464601
{
465602
"url": "/",
@@ -482,9 +619,13 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
482619
483620
支持的 action:navigate / click / fill / select / wait / screenshot / assert_text / assert_visible / hover / scroll
484621
485-
重要提醒:以后修改代码时,主动审视并更新对应模块的蓝本,无需用户提醒!`;
622+
${commonRules}`;
623+
}
624+
486625
await vscode.env.clipboard.writeText(prompt);
487-
vscode.window.showInformationMessage("✅ 提示词已复制!请粘贴到 Cursor / Windsurf,让编程AI读取源码生成蓝本。");
626+
const platformNames: Record<string, string> = { web: "Web", miniprogram: "微信小程序", android: "Android", desktop: "Windows桌面", ios: "iOS" };
627+
const pName = platformNames[platform] || "Web";
628+
vscode.window.showInformationMessage(`✅ ${pName}蓝本提示词已复制!请粘贴到 Cursor / Windsurf,让编程AI读取源码生成蓝本。`);
488629
}
489630

490631
private _guessProjectPathFromBlueprint(blueprintPath: string): string {
@@ -1163,9 +1304,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
11631304
vscode.postMessage({ command: "platformPrecheck", platform: platform, blueprint_path: firstPath });
11641305
});
11651306
1166-
// 复制蓝本生成提示词
1307+
// 复制蓝本生成提示词(根据当前选中项目的平台生成不同提示词)
11671308
document.getElementById("btnCopyBlueprintPrompt").addEventListener("click", () => {
1168-
vscode.postMessage({ command: "copyBlueprintPrompt" });
1309+
const sel = document.getElementById("projectSelect");
1310+
const platform = sel && sel.selectedOptions[0] ? (sel.selectedOptions[0].dataset.platform || "web") : "web";
1311+
vscode.postMessage({ command: "copyBlueprintPrompt", platform: platform });
11691312
});
11701313
11711314
// 探索测试
@@ -1295,6 +1438,7 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
12951438
allProjects.forEach(function(proj, i) {
12961439
var opt = document.createElement("option");
12971440
opt.value = String(i);
1441+
opt.dataset.platform = proj.platform || "web";
12981442
var pName = platformNames[proj.platform] || proj.platform || "Web";
12991443
// iOS项目在Windows下灰色不可选
13001444
if (proj.platform === "ios" && isWindows) {

0 commit comments

Comments
 (0)