AI 大模型接入 + 自动化测试(手动/AI/定时三种触发)

- 自动化测试核心(packages/core/src/testing.ts):
  - JSONPath 求值($.a.b[0].c)、断言评估(状态码/耗时/Header/Body × 10 种操作)
  - 变量提取(响应 → 当前环境,{{var}} 链式传值)、Markdown 报告导出
- 用例模型:场景用例 = 有序步骤(引用 HTTP 接口 + 断言 + 提取),失败后继续可选
- 执行器 testRunner + 定时调度器 scheduler(应用运行期间按间隔自动执行)
- TestingView:用例列表 / 步骤编排 / 断言与提取编辑器 / 报告(统计卡+断言明细+过滤+再次运行+导出)
- AI 助手:OpenAI 兼容客户端(DeepSeek/智谱/Kimi/OpenAI/Ollama 预设)
  - 设置 → AI 助手:供应商 CRUD + 测试连接 + 当前使用切换
  - AI 生成场景用例:读取集合接口清单 → 编排步骤/断言/提取 → 预览保存
- 入口:侧栏底部「自动化测试」+ ⋯ 菜单;文档新增 /docs/testing /docs/ai
- 新增 11 个引擎单测(总 48 前端用例全绿);本地 E2E:运行通过+断言通过+petId 提取验证
This commit is contained in:
logan committed 2026-09-18 01:57:57 +08:00
1 parent 9a019408ab
commit 1bcd7be2ac
23 files changed
+2538 -7

No files matched your search

+13
View File
@@ -8,6 +8,8 @@ import { Icon } from "./components/icons";
import { Logo } from "./components/Logo";
import { useWorkspace } from "./workspaceStore";
import { Sidebar } from "./components/Sidebar";
import { TestingView } from "./components/TestingView";
import { startScheduler, stopScheduler } from "./scheduler";
import { TabBar } from "./components/TabBar";
import { TopBar } from "./components/TopBar";
import { GraphqlEditor } from "./components/editors/GraphqlEditor";
@@ -32,6 +34,7 @@ export function OpsxWorkspace() {
const activeTabId = useWorkspace((s) => s.activeTabId);
const dialog = useWorkspace((s) => s.dialog);
const openTab = useWorkspace((s) => s.openTab);
const view = useWorkspace((s) => s.view);
useEffect(() => {
init();
@@ -45,12 +48,21 @@ export function OpsxWorkspace() {
});
}, []);
// 定时任务调度器(应用运行期间常驻)
useEffect(() => {
startScheduler();
return () => stopScheduler();
}, []);
const activeRef = activeTabId ? findNode(collections, activeTabId) : null;
const activeNode = activeRef?.node;
return (
<div className="flex h-screen min-h-[640px] flex-col overflow-hidden">
<TopBar />
{view === "testing" ? (
<TestingView />
) : (
<div className="flex min-h-0 flex-1">
<Sidebar />
<main className="flex min-w-0 flex-1 flex-col">
@@ -74,6 +86,7 @@ export function OpsxWorkspace() {
</main>
</div>
)}
{dialog?.type === "new" && <NewItemDialog collectionId={dialog.collectionId} parentId={dialog.parentId} />}
{dialog?.type === "import" && <ImportDialog />}
{dialog?.type === "export" && <ExportDialog collectionId={dialog.collectionId} />}