mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(ui): preserve credential actions across dashboard renders (#50845)
Co-authored-by: mateo <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
64d84e557e
commit
84b0a4297a
3 changed files with 45 additions and 6 deletions
|
|
@ -155,11 +155,13 @@ async function openAddCredentialDialog(
|
|||
async function openEditDialog(
|
||||
page: PlaywrightPage,
|
||||
name: string,
|
||||
afterMenuOpen?: () => Promise<void>,
|
||||
): Promise<Locator> {
|
||||
await expect(page.getByText(UPDATED_TOAST)).toHaveCount(0);
|
||||
const row = page.locator("tr", { hasText: name });
|
||||
await expect(row).toBeVisible({ timeout: 15_000 });
|
||||
await row.getByTestId(`credential-actions-${name}`).click();
|
||||
await afterMenuOpen?.();
|
||||
await page.getByTestId("credential-action-edit").click();
|
||||
const dialog = page.getByRole("dialog", { name: "Edit Credential" });
|
||||
await expect(dialog).toBeVisible();
|
||||
|
|
@ -478,6 +480,15 @@ test("the Edit Credential dialog sends only the changed values and deletes the k
|
|||
};
|
||||
const ruleId = `fdrl_${suffix}`;
|
||||
const tokenFile = `/var/run/secrets/anthropic/${suffix}/token`;
|
||||
let releaseModels!: () => void;
|
||||
const modelsReleased = new Promise<void>((resolve) => {
|
||||
releaseModels = resolve;
|
||||
});
|
||||
await page.route("**/v2/model/info?*", async (route) => {
|
||||
const response = await route.fetch();
|
||||
await modelsReleased;
|
||||
await route.fulfill({ response });
|
||||
});
|
||||
try {
|
||||
await createCredential(request, names.fromApiKey, {
|
||||
api_key: `sk-ant-${suffix}`,
|
||||
|
|
@ -500,7 +511,11 @@ test("the Edit Credential dialog sends only the changed values and deletes the k
|
|||
await logInAsAdmin(page);
|
||||
await openModelsTab(page, "LLM Credentials");
|
||||
|
||||
const keyDialog = await openEditDialog(page, names.fromApiKey);
|
||||
const keyDialog = await openEditDialog(page, names.fromApiKey, async () => {
|
||||
const response = page.waitForResponse((response) => response.url().includes("/v2/model/info?"));
|
||||
releaseModels();
|
||||
await (await response).finished();
|
||||
});
|
||||
await pickAuthMethod(page, keyDialog, FEDERATION_BADGE);
|
||||
await fillFields(keyDialog, {
|
||||
anthropic_federation_rule_id: ruleId,
|
||||
|
|
@ -612,6 +627,7 @@ test("the Edit Credential dialog sends only the changed values and deletes the k
|
|||
page.locator("tr", { hasText: names.switchProvider }),
|
||||
).not.toContainText(FEDERATION_BADGE);
|
||||
} finally {
|
||||
releaseModels();
|
||||
await deleteCredentials(request, Object.values(names));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -142,6 +142,29 @@ describe("CredentialsPanel", () => {
|
|||
expect(screen.getByText("No credentials configured")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps an open credential action usable when the panel renders again", async () => {
|
||||
const user = userEvent.setup();
|
||||
mockUseAuthorized.mockReturnValue({ accessToken: "test-token", userRole: "Admin" });
|
||||
mockUseCredentials.mockReturnValue({ data: { credentials }, isLoading: false, refetch: vi.fn() });
|
||||
const client = createQueryClient();
|
||||
const { rerender } = render(
|
||||
<QueryClientProvider client={client}>
|
||||
<CredentialsPanel />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByTestId("credential-actions-openai-key"));
|
||||
expect(await screen.findByTestId("credential-action-edit")).toBeVisible();
|
||||
rerender(
|
||||
<QueryClientProvider client={client}>
|
||||
<CredentialsPanel />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByTestId("credential-action-edit"));
|
||||
expect(screen.getByTestId("credential-modal-edit-submit")).toBeVisible();
|
||||
});
|
||||
|
||||
it("shows the loading skeleton instead of the empty state while credentials load", () => {
|
||||
mockUseAuthorized.mockReturnValue({ accessToken: "test-token", userRole: "Admin" });
|
||||
mockUseCredentials.mockReturnValue({ data: undefined, isLoading: true, refetch: vi.fn() });
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { useTerminology } from "@/contexts/TerminologyContext";
|
||||
import { Plus } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useCredentials } from "@/app/(dashboard)/hooks/credentials/useCredentials";
|
||||
|
|
@ -91,15 +91,15 @@ export default function CredentialsPanel() {
|
|||
}
|
||||
};
|
||||
|
||||
const openEditModal = (credential: CredentialItem) => {
|
||||
const openEditModal = useCallback((credential: CredentialItem) => {
|
||||
setSelectedCredential(credential);
|
||||
setIsUpdateModalOpen(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const openDeleteModal = (credential: CredentialItem) => {
|
||||
const openDeleteModal = useCallback((credential: CredentialItem) => {
|
||||
setCredentialToDelete(credential);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const closeDeleteModal = () => {
|
||||
setCredentialToDelete(null);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue