Skip to content

Commit 5699a06

Browse files
test(e2e): cover AI Hub make-public flow and public model_hub_table (BerriAI#29071)
* test(e2e): cover AI Hub make-public flow and public model_hub_table Three previously-uncovered manual-QA paths land in one spec: - Admin opens "Select Models to Make Public", advances through the multi-step modal, and verifies the success toast. - AI Hub tab strip exposes Model Hub / Agent Hub / MCP Hub / Skill Hub — note the manual-QA "Claude Code Plugin Marketplace" label was renamed to Skill Hub; the test pins the current name. - Anonymous /ui/model_hub_table loads with the master key as `?key=` and renders the Model Hub tab. Agent Hub / MCP Hub tabs are conditional on public data and are not asserted here. * test(e2e): harden AI Hub make-public + public hub assertions Address Greptile review: - Make-public test now asserts "Select All (N)" with N>=1 before clicking, so a missing-seed-data run surfaces immediately instead of timing out on the disabled Next button or the success toast. - Public model_hub_table test dismisses the feedback popup before the tab visibility assertion, matching the ordering used by navigateToPage so a popup race can't mask the tab mid-evaluation. * docs(e2e): explain admin vs public AI Hub tab asymmetry Greptile flagged the all-4-tabs assertion as a potential CI flake, inferring from the public-page comment that Agent Hub / MCP Hub might be data-conditional in the admin view too. They aren't — ModelHubTable renders all four tabs unconditionally for admins. Document the asymmetry inline so future readers (and future review passes) don't re-derive it.
1 parent bc31c57 commit 5699a06

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { test, expect } from "@playwright/test";
2+
import { ADMIN_STORAGE_PATH } from "../../constants";
3+
import { navigateToPage, dismissFeedbackPopup } from "../../helpers/navigation";
4+
import { Page } from "../../fixtures/pages";
5+
6+
test.describe("AI Hub (internal admin view)", () => {
7+
test.use({ storageState: ADMIN_STORAGE_PATH });
8+
9+
test("Make models public via the multi-step modal", async ({ page }) => {
10+
await navigateToPage(page, Page.ModelHubTable);
11+
12+
// Open the "Select Models to Make Public" modal
13+
await page.getByRole("button", { name: /Select Models to Make Public/i }).click();
14+
15+
const modal = page.locator(".ant-modal:visible").filter({ hasText: "Make Models Public" });
16+
await expect(modal).toBeVisible({ timeout: 5_000 });
17+
18+
// Guard: the "Select All (N)" label only shows a count when filteredData
19+
// has at least one row. Asserting N>=1 here turns a missing-seed-data
20+
// failure into an immediate diagnostic rather than a downstream timeout
21+
// on the disabled-Next button or the success toast.
22+
await expect(modal.getByText(/Select All \(\d+\)/)).toBeVisible({ timeout: 5_000 });
23+
24+
// Step 1: pick the seeded models via "Select All"
25+
await modal.getByText(/Select All/i).click();
26+
27+
// Move to confirm step
28+
await modal.getByRole("button", { name: "Next" }).click();
29+
await expect(modal.getByText("Confirm Making Models Public")).toBeVisible({ timeout: 5_000 });
30+
31+
// Submit
32+
await modal.getByRole("button", { name: "Make Public" }).click();
33+
34+
await expect(page.getByText(/Successfully made .* model group\(s\) public/i).first())
35+
.toBeVisible({ timeout: 15_000 });
36+
});
37+
38+
test("AI Hub tab list renders Model Hub, Agent Hub, MCP Hub and Skill Hub", async ({ page }) => {
39+
await navigateToPage(page, Page.ModelHubTable);
40+
41+
// The tab strip lives in the main view; check each tab is present and clickable.
42+
// (The "Claude Code Plugin Marketplace" tab from the manual-QA checklist was
43+
// renamed to "Skill Hub" — verify the current label here so the test stays
44+
// in sync with the UI.)
45+
//
46+
// Note: unlike the public /ui/model_hub_table view (test below), the admin
47+
// ModelHubTable renders all four tabs unconditionally — there are no `&&`
48+
// guards around <Tab>Agent Hub</Tab> or <Tab>MCP Hub</Tab> in the source
49+
// (ModelHubTable.tsx ~L436-439). Asserting all four here is intentional:
50+
// this pins the manual-QA contract that the AI Hub tab strip exposes
51+
// exactly these labels regardless of seeded agent/MCP data.
52+
for (const tabName of ["Model Hub", "Agent Hub", "MCP Hub", "Skill Hub"]) {
53+
const tab = page.getByRole("tab", { name: tabName });
54+
await expect(tab, `${tabName} tab should be present`).toBeVisible({ timeout: 5_000 });
55+
await tab.click();
56+
}
57+
});
58+
});
59+
60+
test.describe("Public model hub (/ui/model_hub_table)", () => {
61+
// No storageState — the public page is reached anonymously with a `key` query param.
62+
63+
test("Public model_hub_table loads and renders the Model Hub tab", async ({ page }) => {
64+
// The page expects the proxy key as the `key` query param. Use the master
65+
// key the e2e runner already exports — this matches what the AI Hub copy
66+
// button hands out.
67+
const masterKey = process.env.LITELLM_MASTER_KEY || "sk-1234";
68+
await page.goto(`/ui/model_hub_table?key=${masterKey}`);
69+
70+
// Dismiss the feedback popup before asserting on the tab, so a popup
71+
// race can't briefly mask the tab while we're evaluating visibility.
72+
await dismissFeedbackPopup(page);
73+
74+
// Page loads (no auth redirect) and the Model Hub tab is always present.
75+
// Agent Hub and MCP Hub tabs are conditionally rendered only when public
76+
// agents/MCP servers exist, so we don't assert on them in a fresh CI run.
77+
await expect(page.getByRole("tab", { name: "Model Hub" })).toBeVisible({ timeout: 10_000 });
78+
});
79+
});

0 commit comments

Comments
 (0)