test(provider): add integration tests for additive provider key flows

Cover openclaw provider duplication scenario to verify that a generated
provider key is assigned automatically. Add MSW handlers for
get_openclaw_live_provider_ids, get_openclaw_default_model,
scan_openclaw_config_health, and check_env_conflicts endpoints.
Update EditProviderDialog mock to pass originalId alongside provider.
This commit is contained in:
YoVinchen
2026-03-28 01:49:15 +08:00
parent e9ead2841d
commit 31278e8916
2 changed files with 65 additions and 3 deletions

View File

@@ -2,7 +2,11 @@ import { Suspense, type ComponentType } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { resetProviderState } from "../msw/state";
import {
resetProviderState,
setCurrentProviderId,
setProviders,
} from "../msw/state";
import { emitTauriEvent } from "../msw/tauriMocks";
const toastSuccessMock = vi.fn();
@@ -75,8 +79,11 @@ vi.mock("@/components/providers/EditProviderDialog", () => ({
<button
onClick={() =>
onSubmit({
...provider,
name: `${provider.name}-edited`,
provider: {
...provider,
name: `${provider.name}-edited`,
},
originalId: provider.id,
})
}
>
@@ -114,6 +121,7 @@ vi.mock("@/components/AppSwitcher", () => ({
<span>{activeApp}</span>
<button onClick={() => onSwitch("claude")}>switch-claude</button>
<button onClick={() => onSwitch("codex")}>switch-codex</button>
<button onClick={() => onSwitch("openclaw")}>switch-openclaw</button>
</div>
),
}));
@@ -230,4 +238,46 @@ describe("App integration with MSW", () => {
expect(toastErrorMock).toHaveBeenCalled();
});
});
it("duplicates openclaw providers with a generated provider key", async () => {
setProviders("openclaw", {
deepseek: {
id: "deepseek",
name: "DeepSeek",
settingsConfig: {
baseUrl: "https://api.deepseek.com",
apiKey: "test-key",
api: "openai-completions",
models: [],
},
category: "custom",
sortIndex: 0,
createdAt: Date.now(),
},
});
setCurrentProviderId("openclaw", "deepseek");
const { default: App } = await import("@/App");
renderApp(App);
fireEvent.click(screen.getByText("switch-openclaw"));
await waitFor(() =>
expect(screen.getByTestId("provider-list").textContent).toContain(
"deepseek",
),
);
fireEvent.click(screen.getByText("duplicate"));
await waitFor(() => {
const providerList = screen.getByTestId("provider-list").textContent;
expect(providerList).toContain("deepseek-copy");
expect(providerList).toContain("DeepSeek copy");
});
expect(toastErrorMock).not.toHaveBeenCalledWith(
expect.stringContaining("Provider key is required for openclaw"),
);
});
});

View File

@@ -67,6 +67,16 @@ export const handlers = [
http.post(`${TAURI_ENDPOINT}/update_tray_menu`, () => success(true)),
http.post(`${TAURI_ENDPOINT}/get_openclaw_live_provider_ids`, () =>
success([]),
),
http.post(`${TAURI_ENDPOINT}/get_openclaw_default_model`, () =>
success({ primary: null, fallback: [] }),
),
http.post(`${TAURI_ENDPOINT}/scan_openclaw_config_health`, () => success([])),
http.post(`${TAURI_ENDPOINT}/switch_provider`, async ({ request }) => {
const { id, app } = await withJson<{ id: string; app: AppId }>(request);
const providers = listProviders(app);
@@ -174,6 +184,8 @@ export const handlers = [
http.post(`${TAURI_ENDPOINT}/get_settings`, () => success(getSettings())),
http.post(`${TAURI_ENDPOINT}/check_env_conflicts`, () => success([])),
http.post(`${TAURI_ENDPOINT}/save_settings`, async ({ request }) => {
const { settings } = await withJson<{ settings: Settings }>(request);
setSettings(settings);