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
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";

Expand All @@ -15,6 +16,18 @@ vi.mock("@/components/Editor/IOEditor/IOZIndexEditor", () => ({
IOZIndexEditor: () => null,
}));

vi.mock("@/services/componentService", () => ({
hydrateComponentReference: vi.fn((ref) =>
Promise.resolve({
...ref,
spec: ref.spec || { inputs: [], outputs: [] },
text: ref.text || "",
digest: ref.digest || "mock-digest",
name: ref.name || "mock-component",
}),
),
}));

vi.mock("@/providers/ComponentSpecProvider", () => ({
useComponentSpec: () => ({
componentSpec: {
Expand Down Expand Up @@ -42,6 +55,9 @@ vi.mock("@/providers/ComponentSpecProvider", () => ({
currentSubgraphPath: ["root"],
setComponentSpec: mockSetComponentSpec,
}),
ComponentSpecProvider: ({ children }: { children: React.ReactNode }) => (
<>{children}</>
),
}));

vi.mock("@/providers/ContextPanelProvider", () => ({
Expand Down Expand Up @@ -95,12 +111,28 @@ describe("InputValueEditor", () => {
default: "default value",
};

let queryClient: QueryClient;

beforeEach(() => {
vi.clearAllMocks();

queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
});

const renderWithQueryClient = (ui: React.ReactElement) => {
return render(
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>,
);
};

it("displays input description in field", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);

const descriptionInput = screen.getByLabelText(
"Description",
Expand All @@ -109,7 +141,7 @@ describe("InputValueEditor", () => {
});

it("calls onChange when input value changes", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);

const valueInput = screen.getByLabelText("Value") as HTMLTextAreaElement;
fireEvent.change(valueInput, { target: { value: "new value" } });
Expand All @@ -120,7 +152,7 @@ describe("InputValueEditor", () => {
});

it("calls onNameChange when input name changes", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);
const nameInput = screen.getByLabelText("Name") as HTMLInputElement;
fireEvent.change(nameInput, { target: { value: "NewName" } });
fireEvent.blur(nameInput);
Expand All @@ -131,7 +163,7 @@ describe("InputValueEditor", () => {
});

it("shows validation error when renaming to existing input name", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);

const nameInput = screen.getByLabelText("Name") as HTMLInputElement;
fireEvent.change(nameInput, { target: { value: "ExistingInput" } });
Expand All @@ -146,7 +178,7 @@ describe("InputValueEditor", () => {
});

it("clears validation error when renaming to unique name", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);

const nameInput = screen.getByLabelText("Name") as HTMLInputElement;

Expand All @@ -170,7 +202,7 @@ describe("InputValueEditor", () => {
type: "String",
};

render(<InputValueEditor input={inputWithoutDefault} />);
renderWithQueryClient(<InputValueEditor input={inputWithoutDefault} />);

const valueInput = screen.getByLabelText("Value") as HTMLInputElement;
expect(valueInput.getAttribute("placeholder")).toBe(
Expand All @@ -179,7 +211,7 @@ describe("InputValueEditor", () => {
});

it("shows default value as placeholder when available", () => {
render(<InputValueEditor input={mockInput} />);
renderWithQueryClient(<InputValueEditor input={mockInput} />);

const valueInput = screen.getByLabelText("Value") as HTMLInputElement;
expect(valueInput.getAttribute("placeholder")).toBe("default value");
Expand Down
154 changes: 106 additions & 48 deletions src/components/PipelineRun/RunDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { screen, waitFor } from "@testing-library/dom";
import { cleanup, render } from "@testing-library/react";
import { ReactFlowProvider } from "@xyflow/react";
import { Suspense } from "react";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";

import type {
Expand All @@ -19,6 +20,22 @@ import type { ComponentSpec } from "@/utils/componentSpec";

import { RunDetails } from "./RunDetails";

// Use vi.hoisted to create mocks that can be referenced in vi.mock
const { mockHydrateComponentReference } = vi.hoisted(() => ({
mockHydrateComponentReference: vi.fn(),
}));

vi.mock("@/services/componentService", () => ({
hydrateComponentReference: mockHydrateComponentReference,
fetchAndStoreComponentLibrary: vi.fn(),
fetchAndStoreComponent: vi.fn(),
fetchAndStoreComponentByUrl: vi.fn(),
getComponentText: vi.fn(),
fetchComponentTextFromUrl: vi.fn(),
parseComponentData: vi.fn(),
getExistingAndNewUserComponent: vi.fn(),
}));

// Mock the hooks and services
vi.mock("@tanstack/react-router", async (importOriginal) => {
return {
Expand Down Expand Up @@ -77,6 +94,11 @@ describe("<RunDetails/>", () => {
},
inputs: [],
outputs: [],
implementation: {
graph: {
tasks: {},
},
},
},
},
},
Expand Down Expand Up @@ -104,10 +126,8 @@ describe("<RunDetails/>", () => {
inputs: [],
outputs: [],
implementation: {
container: {
image: "test-image",
command: ["test-command"],
args: ["test-arg"],
graph: {
tasks: {},
},
},
};
Expand All @@ -123,6 +143,14 @@ describe("<RunDetails/>", () => {
// Reset all mocks
vi.clearAllMocks();

// Mock hydrateComponentReference to return the spec immediately
mockHydrateComponentReference.mockResolvedValue({
spec: mockComponentSpec,
name: mockComponentSpec.name,
digest: "test-digest",
text: "name: Test Pipeline",
});

vi.mocked(usePipelineRunData).mockReturnValue({
executionData: {
details: mockExecutionDetails,
Expand Down Expand Up @@ -159,15 +187,17 @@ describe("<RunDetails/>", () => {
const renderWithProviders = (component: React.ReactElement) => {
return render(component, {
wrapper: ({ children }) => (
<ComponentSpecProvider spec={mockComponentSpec}>
<QueryClientProvider client={queryClient}>
<ExecutionDataProvider pipelineRunId="123">
<ReactFlowProvider>
<ContextPanelProvider>{children}</ContextPanelProvider>
</ReactFlowProvider>
</ExecutionDataProvider>
</QueryClientProvider>
</ComponentSpecProvider>
<QueryClientProvider client={queryClient}>
<Suspense fallback={<div>Loading...</div>}>
<ComponentSpecProvider spec={mockComponentSpec}>
<ExecutionDataProvider pipelineRunId="123">
<ReactFlowProvider>
<ContextPanelProvider>{children}</ContextPanelProvider>
</ReactFlowProvider>
</ExecutionDataProvider>
</ComponentSpecProvider>
</Suspense>
</QueryClientProvider>
),
});
};
Expand All @@ -176,48 +206,63 @@ describe("<RunDetails/>", () => {
test("should render pipeline name", async () => {
renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
});
await waitFor(
() => {
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should render run metadata", async () => {
renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(screen.getByText("Run Info")).toBeInTheDocument();
expect(screen.getByText("123")).toBeInTheDocument();
expect(screen.getByText("456")).toBeInTheDocument();
expect(screen.getByText("test-user")).toBeInTheDocument();
});
await waitFor(
() => {
expect(screen.getByText("Run Info")).toBeInTheDocument();
expect(screen.getByText("123")).toBeInTheDocument();
expect(screen.getByText("456")).toBeInTheDocument();
expect(screen.getByText("test-user")).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should render description", async () => {
renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(
screen.getByText("Test pipeline description"),
).toBeInTheDocument();
});
await waitFor(
() => {
expect(
screen.getByText("Test pipeline description"),
).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should render status", async () => {
renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(screen.getByText("Status")).toBeInTheDocument();
});
await waitFor(
() => {
expect(screen.getByText("Status")).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should render annotations", async () => {
renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(screen.getByText("Annotations")).toBeInTheDocument();
expect(screen.getByText("test-annotation")).toBeInTheDocument();
expect(screen.getByText("test-value")).toBeInTheDocument();
});
await waitFor(
() => {
expect(screen.getByText("Annotations")).toBeInTheDocument();
expect(screen.getByText("test-annotation")).toBeInTheDocument();
expect(screen.getByText("test-value")).toBeInTheDocument();
},
{ timeout: 5000 },
);
});
});

Expand All @@ -235,11 +280,14 @@ describe("<RunDetails/>", () => {

renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(
screen.getByText("Pipeline Run could not be loaded."),
).toBeInTheDocument();
});
await waitFor(
() => {
expect(
screen.getByText("Pipeline Run could not be loaded."),
).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should show loading screen when data is loading", async () => {
Expand All @@ -255,9 +303,14 @@ describe("<RunDetails/>", () => {

renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(screen.getByText("Loading run details...")).toBeInTheDocument();
});
await waitFor(
() => {
expect(
screen.getByText("Loading run details..."),
).toBeInTheDocument();
},
{ timeout: 5000 },
);
});

test("should show backend not configured message when backend is not configured", async () => {
Expand All @@ -276,11 +329,16 @@ describe("<RunDetails/>", () => {

renderWithProviders(<RunDetails />);

await waitFor(() => {
expect(
screen.getByText("Configure a backend to view execution artifacts."),
).toBeInTheDocument();
});
await waitFor(
() => {
expect(
screen.getByText(
"Configure a backend to view execution artifacts.",
),
).toBeInTheDocument();
},
{ timeout: 5000 },
);
});
});
});
Loading
Loading