Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/e2e/accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ test.describe("Accessibility", () => {
// Navigate to config, set active, return to chat so input is enabled
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10000 });
await page.getByRole("button", { name: /expand all/i }).click();
const setActiveBtn = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtn).toBeVisible({ timeout: 5000 });
await setActiveBtn.click();
Expand Down Expand Up @@ -94,6 +95,7 @@ test.describe("Accessibility", () => {
// Navigate to config, set active, return to chat so input is enabled
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10000 });
await page.getByRole("button", { name: /expand all/i }).click();
const setActiveBtn = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtn).toBeVisible({ timeout: 5000 });
await setActiveBtn.click();
Expand Down
9 changes: 8 additions & 1 deletion frontend/e2e/chat.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ async function activateMockTarget(page: Page) {
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10000 });

// Sections are collapsed by default — expand all to reveal Set Active buttons
await page.getByRole("button", { name: /expand all/i }).click();

// Set the mock target active
const setActiveBtn = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtn).toBeVisible({ timeout: 5000 });
Expand Down Expand Up @@ -713,9 +716,13 @@ test.describe("Target type scenarios", () => {

await page.goto("/");
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10000 });

// Expand all sections to reveal targets
await page.getByRole("button", { name: /expand all/i }).click();
await expect(page.getByText("dall-e-3")).toBeVisible({ timeout: 10000 });

// Activate the DALL-E target (second row)
// Activate the DALL-E target (second row across all expanded sections)
const setActiveBtns = page.getByRole("button", { name: /set active/i });
await setActiveBtns.nth(1).click();

Expand Down
39 changes: 23 additions & 16 deletions frontend/e2e/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ test.describe("Target Configuration Page", () => {

await goToConfig(page);

// Table should appear with both targets
await expect(page.getByText("gpt-4o")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("dall-e-3")).toBeVisible();
await expect(page.getByText("OpenAIChatTarget")).toBeVisible();
// Section headings should appear (collapsed by default)
await expect(page.getByText("OpenAIChatTarget")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("OpenAIImageTarget")).toBeVisible();

// Expand all to see target data
await page.getByRole("button", { name: /expand all/i }).click();
await expect(page.getByText("gpt-4o")).toBeVisible();
await expect(page.getByText("dall-e-3")).toBeVisible();
});

test("should show empty state when no targets exist", async ({ page }) => {
Expand Down Expand Up @@ -86,9 +89,11 @@ test.describe("Target Configuration Page", () => {
});

await goToConfig(page);
await expect(page.getByText("gpt-4o")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("OpenAIChatTarget")).toBeVisible({ timeout: 10000 });

// Expand section to reveal Set Active buttons
await page.getByRole("button", { name: /OpenAIChatTarget/i }).click();

// Both rows should have a "Set Active" button initially
const setActiveBtns = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtns.first()).toBeVisible();
await setActiveBtns.first().click();
Expand Down Expand Up @@ -123,16 +128,16 @@ test.describe("Target Configuration Page", () => {
});

await goToConfig(page);
// First load shows one target
await expect(page.getByText("gpt-4o")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("dall-e-3")).not.toBeVisible();
// First load shows one section heading
await expect(page.getByText("OpenAIChatTarget")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("OpenAIImageTarget")).not.toBeVisible();

// Flip the flag and click refresh
showExtra = true;
await page.getByRole("button", { name: /refresh/i }).click();

// Second target should now appear
await expect(page.getByText("dall-e-3")).toBeVisible({ timeout: 10000 });
// Second target type section should now appear
await expect(page.getByText("OpenAIImageTarget")).toBeVisible({ timeout: 10000 });
});
});

Expand Down Expand Up @@ -177,7 +182,7 @@ test.describe("Create Target Dialog", () => {
await dialog.getByPlaceholder("https://your-resource.openai.azure.com/").fill("https://my-endpoint.openai.azure.com/");

// Fill model name
await dialog.getByPlaceholder("e.g. gpt-4o, dall-e-3").fill("gpt-4o-test");
await dialog.getByPlaceholder("e.g. gpt-4o, my-deployment").fill("gpt-4o-test");

// Click Create Target
await dialog.getByRole("button", { name: "Create Target" }).click();
Expand Down Expand Up @@ -225,9 +230,10 @@ test.describe("Target Config ↔ Chat Navigation", () => {
});

await goToConfig(page);
await expect(page.getByText("gpt-4o")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("OpenAIChatTarget")).toBeVisible({ timeout: 10000 });

// Set first target active
// Expand section and set first target active
await page.getByRole("button", { name: /OpenAIChatTarget/i }).click();
await page.getByRole("button", { name: /set active/i }).first().click();

// Navigate back to chat
Expand All @@ -248,9 +254,10 @@ test.describe("Target Config ↔ Chat Navigation", () => {
await page.goto("/");
await expect(page.getByTestId("no-target-banner")).toBeVisible();

// Go to config, set a target
// Go to config, expand section, set a target
await page.getByTitle("Configuration").click();
await expect(page.getByText("gpt-4o")).toBeVisible({ timeout: 10000 });
await expect(page.getByText("OpenAIChatTarget")).toBeVisible({ timeout: 10000 });
await page.getByRole("button", { name: /OpenAIChatTarget/i }).click();
await page.getByRole("button", { name: /set active/i }).first().click();

// Return to chat — send should be enabled when there's text
Expand Down
3 changes: 3 additions & 0 deletions frontend/e2e/converters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ async function activateMockTarget(page: Page) {
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10000 });

// Sections are collapsed by default — expand all to reveal Set Active buttons
await page.getByRole("button", { name: /expand all/i }).click();

const setActiveBtn = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtn).toBeVisible({ timeout: 5000 });
await setActiveBtn.click();
Expand Down
2 changes: 2 additions & 0 deletions frontend/e2e/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ async function activateMockTarget(page: Page) {
await expect(page.getByText("Target Configuration")).toBeVisible({
timeout: 10000,
});
// Sections are collapsed by default — expand all to reveal Set Active buttons
await page.getByRole("button", { name: /expand all/i }).click();
const setActiveBtn = page.getByRole("button", { name: /set active/i });
await expect(setActiveBtn).toBeVisible({ timeout: 5000 });
await setActiveBtn.click();
Expand Down
2 changes: 2 additions & 0 deletions frontend/e2e/flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ async function createConversation(
async function activateTarget(page: Page, targetType: string): Promise<void> {
await page.getByTitle("Configuration").click();
await expect(page.getByText("Target Configuration")).toBeVisible({ timeout: 10_000 });
// Sections are collapsed by default — expand the target type's section
await page.getByRole("button", { name: new RegExp(targetType, "i") }).click();
// The table displays target_type (not registry name), so match by type.
// Use .first() because multiple targets of the same type may exist.
const row = page.locator("tr", { has: page.getByText(targetType, { exact: true }) }).first();
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Chat/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ export default function ChatWindow({
<Tooltip content={activeTarget.target_registry_name} relationship="label">
<Badge appearance="outline" size="medium">
{activeTarget.target_type}
{activeTarget.model_name ? ` (${activeTarget.model_name})` : ''}
{activeTarget.deployment_name ? ` (${activeTarget.deployment_name})` : activeTarget.model_name ? ` (${activeTarget.model_name})` : ''}
</Badge>
</Tooltip>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Config/CreateTargetDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe("CreateTargetDialog", () => {
fireEvent.change(endpointInput, { target: { value: "https://api.openai.com" } });

// Fill model name — use fireEvent.change for consistency (same reason as endpoint)
const modelInput = screen.getByPlaceholderText("e.g. gpt-4o, dall-e-3");
const modelInput = screen.getByPlaceholderText("e.g. gpt-4o, my-deployment");
fireEvent.change(modelInput, { target: { value: "gpt-4" } });

// Submit
Expand Down
33 changes: 32 additions & 1 deletion frontend/src/components/Config/CreateTargetDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
Input,
Label,
Select,
Switch,
Text,
tokens,
Field,
MessageBar,
Expand Down Expand Up @@ -38,6 +40,8 @@ export default function CreateTargetDialog({ open, onClose, onCreated }: CreateT
const [targetType, setTargetType] = useState('')
const [endpoint, setEndpoint] = useState('')
const [modelName, setModelName] = useState('')
const [hasDifferentUnderlying, setHasDifferentUnderlying] = useState(false)
const [underlyingModel, setUnderlyingModel] = useState('')
const [apiKey, setApiKey] = useState('')
const [submitting, setSubmitting] = useState(false)
const [error, setError] = useState<string | null>(null)
Expand All @@ -47,6 +51,8 @@ export default function CreateTargetDialog({ open, onClose, onCreated }: CreateT
setTargetType('')
setEndpoint('')
setModelName('')
setHasDifferentUnderlying(false)
setUnderlyingModel('')
setApiKey('')
setError(null)
setFieldErrors({})
Expand Down Expand Up @@ -75,6 +81,7 @@ export default function CreateTargetDialog({ open, onClose, onCreated }: CreateT
endpoint,
}
if (modelName) params.model_name = modelName
if (hasDifferentUnderlying && underlyingModel) params.underlying_model = underlyingModel
if (apiKey) params.api_key = apiKey

await targetsApi.createTarget({
Expand Down Expand Up @@ -139,12 +146,36 @@ export default function CreateTargetDialog({ open, onClose, onCreated }: CreateT

<Field label="Model / Deployment Name">
<Input
placeholder="e.g. gpt-4o, dall-e-3"
placeholder="e.g. gpt-4o, my-deployment"
value={modelName}
onChange={(_, data) => setModelName(data.value)}
/>
</Field>

<div>
<Switch
checked={hasDifferentUnderlying}
onChange={(_, data) => {
setHasDifferentUnderlying(data.checked)
if (!data.checked) setUnderlyingModel('')
}}
label="Underlying model differs from deployment name"
/>
<Text size={200} style={{ color: tokens.colorNeutralForeground3, display: 'block', marginTop: '2px' }}>
On Azure, the deployment name (e.g. my-gpt4-deployment) may differ from the actual model (e.g. gpt-4o).
</Text>
</div>

{hasDifferentUnderlying && (
<Field label="Underlying Model">
<Input
placeholder="e.g. gpt-4o-2024-08-06"
value={underlyingModel}
onChange={(_, data) => setUnderlyingModel(data.value)}
/>
</Field>
)}

<Field label="API Key">
<Input
type="password"
Expand Down
19 changes: 18 additions & 1 deletion frontend/src/components/Config/TargetConfig.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ describe("TargetConfig", () => {
expect(screen.getByText("OpenAIChatTarget")).toBeInTheDocument();
});

// Expand section to reveal Set Active buttons
await userEvent.click(screen.getByRole("button", { name: /OpenAIChatTarget/i }));

const setActiveButtons = screen.getAllByText("Set Active");
await userEvent.click(setActiveButtons[0]);

Expand Down Expand Up @@ -272,6 +275,12 @@ describe("TargetConfig", () => {

await waitFor(() => {
expect(screen.getByText("OpenAIChatTarget")).toBeInTheDocument();
});

// Expand section to reveal target data
await userEvent.click(screen.getByRole("button", { name: /OpenAIChatTarget/i }));

await waitFor(() => {
expect(screen.getByText("gpt-4")).toBeInTheDocument();
expect(
screen.getAllByText("https://api.openai.com").length
Expand Down Expand Up @@ -307,7 +316,12 @@ describe("TargetConfig", () => {

await waitFor(() => {
expect(screen.getByText("OpenAIResponseTarget")).toBeInTheDocument();
// formatParams renders as "key: value, key: value"
});

// Expand section to reveal params
await userEvent.click(screen.getByRole("button", { name: /OpenAIResponseTarget/i }));

await waitFor(() => {
expect(screen.getByText(/reasoning_effort: high/)).toBeInTheDocument();
expect(screen.getByText(/reasoning_summary: auto/)).toBeInTheDocument();
expect(screen.getByText(/max_output_tokens: 4096/)).toBeInTheDocument();
Expand Down Expand Up @@ -339,6 +353,9 @@ describe("TargetConfig", () => {
expect(screen.getByText("TextTarget")).toBeInTheDocument();
});

// Expand section
await userEvent.click(screen.getByRole("button", { name: /TextTarget/i }));

// No reasoning or other special params should be displayed
expect(screen.queryByText(/reasoning_effort/)).not.toBeInTheDocument();
});
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/Config/TargetTable.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export const useTargetTableStyles = makeStyles({
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
},
paramsCell: {
whiteSpace: 'pre-line',
wordBreak: 'break-word',
},
})
Loading
Loading