Skip to content

Commit f8b32da

Browse files
committed
v8.0: 微信小程序测试 - 627个测试全绿
新增功能: - MiniProgramController: Node.js桥接miniprogram-automator SDK - miniprogram_bridge.js: 14个action(connect/tap/input/screenshot/wxml/pageData等) - 11个小程序API端点(devtools状态/会话/导航/点击/输入/截图/WXML/文本/页面数据) - 前端MiniProgramTestPage + 侧栏导航集成 - 34个新增测试(Config/Controller/Bridge/WaitFor) 修复: - Vite proxy端口80008900 - 默认引擎地址80008900
1 parent 1954c4c commit f8b32da

10 files changed

Lines changed: 1235 additions & 7 deletions

File tree

desktop/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import AnalyticsPage from './pages/AnalyticsPage';
99
import DashboardPage from './pages/DashboardPage';
1010
import TeamPage from './pages/TeamPage';
1111
import DesktopTestPage from './pages/DesktopTestPage';
12+
import MiniProgramTestPage from './pages/MiniProgramTestPage';
1213

1314
export default function App() {
1415
return (
@@ -22,6 +23,7 @@ export default function App() {
2223
<Route path="/dashboard" element={<DashboardPage />} />
2324
<Route path="/team" element={<TeamPage />} />
2425
<Route path="/desktop-test" element={<DesktopTestPage />} />
26+
<Route path="/miniprogram-test" element={<MiniProgramTestPage />} />
2527
<Route path="/settings" element={<SettingsPage />} />
2628
<Route path="/help" element={<HelpPage />} />
2729
</Route>

desktop/src/components/AppLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import {
1414
User,
1515
Users,
1616
Monitor,
17+
Smartphone,
1718
} from 'lucide-react';
1819

1920
const navItems = [
2021
{ to: '/', icon: Play, label: '开始测试' },
2122
{ to: '/running', icon: Activity, label: '测试面板' },
2223
{ to: '/desktop-test', icon: Monitor, label: '桌面测试' },
24+
{ to: '/miniprogram-test', icon: Smartphone, label: '小程序测试' },
2325
{ to: '/history', icon: History, label: '历史记录' },
2426
{ to: '/analytics', icon: BarChart3, label: '报告分析' },
2527
{ to: '/dashboard', icon: User, label: '个人中心' },
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/**
2+
* 微信小程序测试页(v8.0)
3+
*
4+
* 功能:检测开发者工具/创建会话/导航/点击/输入/截图/WXML/页面数据
5+
*/
6+
7+
import { useState, useEffect } from 'react';
8+
import {
9+
Smartphone, Play, MousePointer, Type, Camera,
10+
Code2, Loader2, RefreshCw, Database, X, FolderOpen, CheckCircle, XCircle,
11+
} from 'lucide-react';
12+
import { request } from '../lib/engineClient';
13+
14+
interface SessionInfo { session_id: string; }
15+
interface DevtoolsStatus { found: boolean; path: string; message: string; }
16+
17+
export default function MiniProgramTestPage() {
18+
const [devtools, setDevtools] = useState<DevtoolsStatus | null>(null);
19+
const [session, setSession] = useState<SessionInfo | null>(null);
20+
const [loading, setLoading] = useState(false);
21+
const [projectPath, setProjectPath] = useState('');
22+
const [screenshot, setScreenshot] = useState('');
23+
const [wxml, setWxml] = useState('');
24+
const [pageData, setPageData] = useState('');
25+
const [selector, setSelector] = useState('');
26+
const [inputText, setInputText] = useState('');
27+
const [navUrl, setNavUrl] = useState('');
28+
const [log, setLog] = useState<string[]>([]);
29+
30+
const addLog = (msg: string) => setLog(prev => [`[${new Date().toLocaleTimeString()}] ${msg}`, ...prev].slice(0, 50));
31+
32+
const checkDevtools = async () => {
33+
try {
34+
const res = await request<DevtoolsStatus>('GET', '/api/v1/miniprogram/devtools/status');
35+
setDevtools(res);
36+
addLog(res.message);
37+
} catch (e: unknown) { addLog(`检测失败: ${e}`); }
38+
};
39+
40+
useEffect(() => { checkDevtools(); }, []);
41+
42+
const connect = async () => {
43+
if (!projectPath) { addLog('请输入小程序项目路径'); return; }
44+
setLoading(true);
45+
try {
46+
const res = await request<{ session_id: string }>('POST', '/api/v1/miniprogram/session/create', {
47+
project_path: projectPath,
48+
});
49+
setSession({ session_id: res.session_id });
50+
addLog(`已连接: ${projectPath}`);
51+
} catch (e: unknown) { addLog(`连接失败: ${e}`); }
52+
finally { setLoading(false); }
53+
};
54+
55+
const disconnect = async () => {
56+
if (!session) return;
57+
try {
58+
await request('DELETE', `/api/v1/miniprogram/session/${session.session_id}`);
59+
addLog('会话已关闭');
60+
} catch { /* ignore */ }
61+
setSession(null); setScreenshot(''); setWxml(''); setPageData('');
62+
};
63+
64+
const doNavigate = async () => {
65+
if (!session || !navUrl) return;
66+
try {
67+
await request('POST', `/api/v1/miniprogram/session/${session.session_id}/navigate`, { url: navUrl });
68+
addLog(`导航: ${navUrl}`);
69+
} catch (e: unknown) { addLog(`导航失败: ${e}`); }
70+
};
71+
72+
const doTap = async () => {
73+
if (!session || !selector) return;
74+
try {
75+
await request('POST', `/api/v1/miniprogram/session/${session.session_id}/tap`, { selector });
76+
addLog(`点击: ${selector}`);
77+
} catch (e: unknown) { addLog(`点击失败: ${e}`); }
78+
};
79+
80+
const doInput = async () => {
81+
if (!session || !selector) return;
82+
try {
83+
await request('POST', `/api/v1/miniprogram/session/${session.session_id}/input`, { selector, text: inputText });
84+
addLog(`输入: ${selector} -> "${inputText}"`);
85+
} catch (e: unknown) { addLog(`输入失败: ${e}`); }
86+
};
87+
88+
const doScreenshot = async () => {
89+
if (!session) return;
90+
try {
91+
const res = await request<{ base64: string; path: string }>('GET', `/api/v1/miniprogram/session/${session.session_id}/screenshot?name=manual`);
92+
setScreenshot(res.base64);
93+
addLog(`截图完成: ${res.path}`);
94+
} catch (e: unknown) { addLog(`截图失败: ${e}`); }
95+
};
96+
97+
const doSource = async () => {
98+
if (!session) return;
99+
try {
100+
const res = await request<{ source: string }>('GET', `/api/v1/miniprogram/session/${session.session_id}/source`);
101+
setWxml(res.source);
102+
addLog('WXML 已获取');
103+
} catch (e: unknown) { addLog(`获取WXML失败: ${e}`); }
104+
};
105+
106+
const doPageData = async () => {
107+
if (!session) return;
108+
try {
109+
const res = await request<{ data: unknown }>('GET', `/api/v1/miniprogram/session/${session.session_id}/page-data`);
110+
setPageData(JSON.stringify(res.data, null, 2));
111+
addLog('页面数据已获取');
112+
} catch (e: unknown) { addLog(`获取数据失败: ${e}`); }
113+
};
114+
115+
return (
116+
<div className="p-6 space-y-4 h-full overflow-y-auto">
117+
<div className="flex items-center justify-between">
118+
<h1 className="text-xl font-bold text-white flex items-center gap-2">
119+
<Smartphone className="w-5 h-5 text-green-400" /> 小程序测试
120+
</h1>
121+
{session && (
122+
<span className="text-xs text-emerald-400 bg-emerald-400/10 px-2 py-1 rounded">
123+
已连接
124+
</span>
125+
)}
126+
</div>
127+
128+
{/* 开发者工具状态 */}
129+
<div className="bg-gray-900 border border-gray-800 rounded-xl p-4">
130+
<div className="flex items-center gap-2 text-sm">
131+
{devtools?.found
132+
? <><CheckCircle className="w-4 h-4 text-green-400" /><span className="text-green-400">微信开发者工具已找到</span><span className="text-gray-500 text-xs ml-2">{devtools.path}</span></>
133+
: <><XCircle className="w-4 h-4 text-red-400" /><span className="text-red-400">未找到微信开发者工具</span><span className="text-gray-500 text-xs ml-2">请安装后开启"服务端口"</span></>
134+
}
135+
<button onClick={checkDevtools} className="ml-auto text-gray-500 hover:text-blue-400 p-1"><RefreshCw className="w-3.5 h-3.5" /></button>
136+
</div>
137+
</div>
138+
139+
{!session ? (
140+
/* ── 连接面板 ── */
141+
<div className="bg-gray-900 border border-gray-800 rounded-xl p-5 space-y-3">
142+
<div className="text-sm text-gray-400 font-medium flex items-center gap-2">
143+
<FolderOpen className="w-4 h-4" /> 小程序项目路径
144+
</div>
145+
<input value={projectPath} onChange={e => setProjectPath(e.target.value)}
146+
placeholder="如: D:\Projects\my-miniprogram"
147+
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 outline-none focus:border-green-500" />
148+
<button onClick={connect} disabled={loading || !projectPath}
149+
className="w-full flex items-center justify-center gap-2 py-2.5 bg-green-600 hover:bg-green-500 text-white rounded-lg text-sm disabled:opacity-40 transition-colors">
150+
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
151+
连接小程序
152+
</button>
153+
</div>
154+
) : (
155+
/* ── 操作面板 ── */
156+
<>
157+
<div className="flex items-center gap-2">
158+
<input value={navUrl} onChange={e => setNavUrl(e.target.value)} placeholder="页面路径 (如 /pages/index/index)"
159+
className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 outline-none focus:border-green-500" />
160+
<button onClick={doNavigate} className="px-3 py-2 bg-green-600 hover:bg-green-500 text-white text-xs rounded-lg">导航</button>
161+
</div>
162+
<div className="flex items-center gap-2">
163+
<input value={selector} onChange={e => setSelector(e.target.value)} placeholder="选择器 (.class / #id / view)"
164+
className="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 outline-none focus:border-green-500" />
165+
<input value={inputText} onChange={e => setInputText(e.target.value)} placeholder="输入文本"
166+
className="w-40 bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white placeholder-gray-500 outline-none focus:border-green-500" />
167+
</div>
168+
<div className="flex gap-2 flex-wrap">
169+
<Btn icon={<MousePointer className="w-3.5 h-3.5" />} label="点击" onClick={doTap} color="bg-blue-600 hover:bg-blue-500" />
170+
<Btn icon={<Type className="w-3.5 h-3.5" />} label="输入" onClick={doInput} color="bg-violet-600 hover:bg-violet-500" />
171+
<Btn icon={<Camera className="w-3.5 h-3.5" />} label="截图" onClick={doScreenshot} color="bg-amber-600 hover:bg-amber-500" />
172+
<Btn icon={<Code2 className="w-3.5 h-3.5" />} label="WXML" onClick={doSource} color="bg-cyan-600 hover:bg-cyan-500" />
173+
<Btn icon={<Database className="w-3.5 h-3.5" />} label="页面数据" onClick={doPageData} color="bg-pink-600 hover:bg-pink-500" />
174+
<Btn icon={<X className="w-3.5 h-3.5" />} label="断开" onClick={disconnect} color="bg-red-600/80 hover:bg-red-500" />
175+
</div>
176+
177+
<div className="grid grid-cols-2 gap-4">
178+
<div className="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
179+
<div className="px-4 py-2 border-b border-gray-800 text-xs text-gray-500 font-medium">截图预览</div>
180+
<div className="p-2 min-h-[200px] flex items-center justify-center">
181+
{screenshot ? (
182+
<img src={`data:image/png;base64,${screenshot}`} alt="截图" className="max-w-full max-h-[300px] rounded" />
183+
) : (
184+
<span className="text-gray-600 text-xs">点击「截图」按钮</span>
185+
)}
186+
</div>
187+
</div>
188+
<div className="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
189+
<div className="px-4 py-2 border-b border-gray-800 text-xs text-gray-500 font-medium">
190+
{pageData ? '页面数据' : 'WXML 结构'}
191+
</div>
192+
<pre className="p-3 text-xs text-gray-400 max-h-[320px] overflow-auto font-mono whitespace-pre-wrap">
193+
{pageData || wxml || '点击「WXML」或「页面数据」按钮'}
194+
</pre>
195+
</div>
196+
</div>
197+
</>
198+
)}
199+
200+
{/* 日志 */}
201+
<div className="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
202+
<div className="px-4 py-2 border-b border-gray-800 text-xs text-gray-500 font-medium">操作日志</div>
203+
<div className="p-3 max-h-40 overflow-y-auto">
204+
{log.length === 0 ? (
205+
<span className="text-gray-600 text-xs">暂无日志</span>
206+
) : log.map((l, i) => (
207+
<div key={i} className="text-xs text-gray-400 font-mono py-0.5">{l}</div>
208+
))}
209+
</div>
210+
</div>
211+
</div>
212+
);
213+
}
214+
215+
function Btn({ icon, label, onClick, color }: {
216+
icon: React.ReactNode; label: string; onClick: () => void; color: string;
217+
}) {
218+
return (
219+
<button onClick={onClick}
220+
className={`flex items-center gap-1.5 px-3 py-1.5 ${color} text-white text-xs rounded-lg transition-colors`}>
221+
{icon} {label}
222+
</button>
223+
);
224+
}

0 commit comments

Comments
 (0)