mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-13 08:14:58 -05:00
Compare commits
7 Commits
fix/copilo
...
pr-11826
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
657190e759 | ||
|
|
caabee9278 | ||
|
|
0fcaa63162 | ||
|
|
6299045f98 | ||
|
|
24cd34ed3f | ||
|
|
876c6677de | ||
|
|
3e3af45456 |
@@ -132,6 +132,7 @@
|
|||||||
"@tanstack/eslint-plugin-query": "5.91.2",
|
"@tanstack/eslint-plugin-query": "5.91.2",
|
||||||
"@tanstack/react-query-devtools": "5.90.2",
|
"@tanstack/react-query-devtools": "5.90.2",
|
||||||
"@testing-library/dom": "10.4.1",
|
"@testing-library/dom": "10.4.1",
|
||||||
|
"@testing-library/jest-dom": "6.9.1",
|
||||||
"@testing-library/react": "16.3.2",
|
"@testing-library/react": "16.3.2",
|
||||||
"@types/canvas-confetti": "1.9.0",
|
"@types/canvas-confetti": "1.9.0",
|
||||||
"@types/lodash": "4.17.20",
|
"@types/lodash": "4.17.20",
|
||||||
|
|||||||
3
autogpt_platform/frontend/pnpm-lock.yaml
generated
3
autogpt_platform/frontend/pnpm-lock.yaml
generated
@@ -312,6 +312,9 @@ importers:
|
|||||||
'@testing-library/dom':
|
'@testing-library/dom':
|
||||||
specifier: 10.4.1
|
specifier: 10.4.1
|
||||||
version: 10.4.1
|
version: 10.4.1
|
||||||
|
'@testing-library/jest-dom':
|
||||||
|
specifier: 6.9.1
|
||||||
|
version: 6.9.1
|
||||||
'@testing-library/react':
|
'@testing-library/react':
|
||||||
specifier: 16.3.2
|
specifier: 16.3.2
|
||||||
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export const AgentInfo = ({
|
|||||||
const allVersions = storeData?.versions
|
const allVersions = storeData?.versions
|
||||||
? storeData.versions
|
? storeData.versions
|
||||||
.map((versionStr: string) => parseInt(versionStr, 10))
|
.map((versionStr: string) => parseInt(versionStr, 10))
|
||||||
|
.filter((versionNum: number) => !isNaN(versionNum))
|
||||||
.sort((a: number, b: number) => b - a)
|
.sort((a: number, b: number) => b - a)
|
||||||
.map((versionNum: number) => ({
|
.map((versionNum: number) => ({
|
||||||
version: versionNum,
|
version: versionNum,
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { describe, expect, test, afterEach } from "vitest";
|
||||||
|
import { render, screen, waitFor } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainAgentPage } from "../MainAgentPage";
|
||||||
|
import {
|
||||||
|
mockAuthenticatedUser,
|
||||||
|
mockUnauthenticatedUser,
|
||||||
|
resetAuthState,
|
||||||
|
} from "@/tests/integrations/helpers/mock-supabase-auth";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
slug: "test-agent",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainAgentPage - Auth State", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetAuthState();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows add to library button when authenticated", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByTestId("agent-add-library-button"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("hides add to library button when not authenticated", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-title")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId("agent-add-library-button"),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged out", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-title")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByTestId("agent-download-button")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged in", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-title")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByTestId("agent-add-library-button")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen, waitFor, act } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainAgentPage } from "../MainAgentPage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import { getGetV2GetSpecificAgentMockHandler422 } from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||||
|
import { create500Handler } from "@/tests/integrations/helpers/create-500-handler";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
slug: "test-agent",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainAgentPage - Error Handling", () => {
|
||||||
|
test("displays error when agent API returns 422", async () => {
|
||||||
|
server.use(getGetV2GetSpecificAgentMockHandler422());
|
||||||
|
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText("Failed to load agent data", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when API returns 500", async () => {
|
||||||
|
server.use(
|
||||||
|
create500Handler("get", "*/api/store/agents/test-creator/test-agent"),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText("Failed to load agent data", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("retry button is visible on error", async () => {
|
||||||
|
server.use(getGetV2GetSpecificAgentMockHandler422());
|
||||||
|
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: /try again/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen, waitFor } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainAgentPage } from "../MainAgentPage";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
slug: "test-agent",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainAgentPage - Rendering", () => {
|
||||||
|
test("renders agent info with title", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-title")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders agent creator info", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-creator")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders agent description", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-description")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders breadcrumbs with marketplace link", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByRole("link", { name: /marketplace/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders download button", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("agent-download-button")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders similar agents section", async () => {
|
||||||
|
render(<MainAgentPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText("Similar agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { describe, expect, test, afterEach } from "vitest";
|
||||||
|
import { render, screen, waitFor } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainCreatorPage } from "../MainCreatorPage";
|
||||||
|
import {
|
||||||
|
mockAuthenticatedUser,
|
||||||
|
mockUnauthenticatedUser,
|
||||||
|
resetAuthState,
|
||||||
|
} from "@/tests/integrations/helpers/mock-supabase-auth";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainCreatorPage - Auth State", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetAuthState();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged out", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("creator-description")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged in", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("creator-description")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainCreatorPage } from "../MainCreatorPage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import {
|
||||||
|
getGetV2GetCreatorDetailsMockHandler422,
|
||||||
|
getGetV2ListStoreAgentsMockHandler422,
|
||||||
|
} from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||||
|
import { create500Handler } from "@/tests/integrations/helpers/create-500-handler";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainCreatorPage - Error Handling", () => {
|
||||||
|
test("displays error when creator details API returns 422", async () => {
|
||||||
|
server.use(getGetV2GetCreatorDetailsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load creator data", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when creator agents API returns 422", async () => {
|
||||||
|
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load creator data", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when API returns 500", async () => {
|
||||||
|
server.use(create500Handler("get", "*/api/store/creator/test-creator"));
|
||||||
|
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load creator data", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("retry button is visible on error", async () => {
|
||||||
|
server.use(getGetV2GetCreatorDetailsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByRole("button", { name: /try again/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainCreatorPage } from "../MainCreatorPage";
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
creator: "test-creator",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainCreatorPage - Rendering", () => {
|
||||||
|
test("renders creator description", async () => {
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
expect(
|
||||||
|
await screen.findByTestId("creator-description"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders breadcrumbs with marketplace link", async () => {
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByRole("link", { name: /marketplace/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders about section", async () => {
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText("About")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders agents by creator section", async () => {
|
||||||
|
render(<MainCreatorPage params={defaultParams} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText(/Agents by/i, { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { describe, expect, test, afterEach } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
||||||
|
import {
|
||||||
|
mockAuthenticatedUser,
|
||||||
|
mockUnauthenticatedUser,
|
||||||
|
resetAuthState,
|
||||||
|
} from "@/tests/integrations/helpers/mock-supabase-auth";
|
||||||
|
|
||||||
|
describe("MainMarketplacePage - Auth State", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetAuthState();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged out", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText("Top Agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged in", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText("Top Agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import { http, HttpResponse } from "msw";
|
||||||
|
|
||||||
|
describe("MainMarketplacePage - Empty State", () => {
|
||||||
|
test("handles empty featured agents gracefully", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured creators", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("handles empty creators gracefully", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/creators*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("handles all empty data gracefully", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/store/creators*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(await screen.findByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import {
|
||||||
|
getGetV2ListStoreAgentsMockHandler422,
|
||||||
|
getGetV2ListStoreCreatorsMockHandler422,
|
||||||
|
} from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||||
|
import { create500Handler } from "@/tests/integrations/helpers/create-500-handler";
|
||||||
|
|
||||||
|
describe("MainMarketplacePage - Error Handling", () => {
|
||||||
|
test("displays error when featured agents API returns 422", async () => {
|
||||||
|
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when creators API returns 422", async () => {
|
||||||
|
server.use(getGetV2ListStoreCreatorsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when API returns 500", async () => {
|
||||||
|
server.use(create500Handler("get", "*/api/store/agents*"));
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("retry button is visible on error", async () => {
|
||||||
|
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByRole("button", { name: /try again/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, waitFor } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import { http, HttpResponse, delay } from "msw";
|
||||||
|
|
||||||
|
describe("MainMarketplacePage - Loading State", () => {
|
||||||
|
test("shows loading skeleton while data is being fetched", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", async () => {
|
||||||
|
await delay(500);
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/store/creators*", async () => {
|
||||||
|
await delay(500);
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container } = render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const loadingElements = container.querySelectorAll(
|
||||||
|
'[class*="animate-pulse"]',
|
||||||
|
);
|
||||||
|
expect(loadingElements.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { expect, test } from "vitest";
|
|
||||||
import { render, screen } from "@/tests/integrations/test-utils";
|
|
||||||
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
|
||||||
import { server } from "@/mocks/mock-server";
|
|
||||||
import { getDeleteV2DeleteStoreSubmissionMockHandler422 } from "@/app/api/__generated__/endpoints/store/store.msw";
|
|
||||||
|
|
||||||
// Only for CI testing purpose, will remove it in future PR
|
|
||||||
test("MainMarketplacePage", async () => {
|
|
||||||
server.use(getDeleteV2DeleteStoreSubmissionMockHandler422());
|
|
||||||
|
|
||||||
render(<MainMarkeplacePage />);
|
|
||||||
expect(
|
|
||||||
await screen.findByText("Featured agents", { exact: false }),
|
|
||||||
).toBeDefined();
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainMarkeplacePage } from "../MainMarketplacePage";
|
||||||
|
|
||||||
|
describe("MainMarketplacePage - Rendering", () => {
|
||||||
|
test("renders hero section with search bar", async () => {
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders featured agents section", async () => {
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders top agents section", async () => {
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Top Agents", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders featured creators section", async () => {
|
||||||
|
render(<MainMarkeplacePage />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Featured creators", { exact: false }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { describe, expect, test, afterEach } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainSearchResultPage } from "../MainSearchResultPage";
|
||||||
|
import {
|
||||||
|
mockAuthenticatedUser,
|
||||||
|
mockUnauthenticatedUser,
|
||||||
|
resetAuthState,
|
||||||
|
} from "@/tests/integrations/helpers/mock-supabase-auth";
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
searchTerm: "test-search",
|
||||||
|
sort: "runs" as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainSearchResultPage - Auth State", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetAuthState();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged out", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Results for:")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders page correctly when logged in", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Results for:")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainSearchResultPage } from "../MainSearchResultPage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import {
|
||||||
|
getGetV2ListStoreAgentsMockHandler422,
|
||||||
|
getGetV2ListStoreCreatorsMockHandler422,
|
||||||
|
} from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||||
|
import { create500Handler } from "@/tests/integrations/helpers/create-500-handler";
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
searchTerm: "test-search",
|
||||||
|
sort: "runs" as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainSearchResultPage - Error Handling", () => {
|
||||||
|
test("displays error when agents API returns 422", async () => {
|
||||||
|
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when creators API returns 422", async () => {
|
||||||
|
server.use(getGetV2ListStoreCreatorsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays error when API returns 500", async () => {
|
||||||
|
server.use(create500Handler("get", "*/api/store/agents*"));
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("Failed to load marketplace data", {
|
||||||
|
exact: false,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("retry button is visible on error", async () => {
|
||||||
|
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByRole("button", { name: /try again/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainSearchResultPage } from "../MainSearchResultPage";
|
||||||
|
import { server } from "@/mocks/mock-server";
|
||||||
|
import { http, HttpResponse } from "msw";
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
searchTerm: "nonexistent-search-term-xyz",
|
||||||
|
sort: "runs" as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainSearchResultPage - No Results", () => {
|
||||||
|
test("shows empty state when no agents match search", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/store/creators*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Results for:")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("nonexistent-search-term-xyz")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("displays search term even with no results", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/store/creators*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText("nonexistent-search-term-xyz"),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("search bar is present with no results", async () => {
|
||||||
|
server.use(
|
||||||
|
http.get("*/api/store/agents*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
agents: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
http.get("*/api/store/creators*", () => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
creators: [],
|
||||||
|
pagination: {
|
||||||
|
total_items: 0,
|
||||||
|
total_pages: 0,
|
||||||
|
current_page: 1,
|
||||||
|
page_size: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
import { render, screen } from "@/tests/integrations/test-utils";
|
||||||
|
import { MainSearchResultPage } from "../MainSearchResultPage";
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
searchTerm: "test-search",
|
||||||
|
sort: "runs" as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("MainSearchResultPage - Rendering", () => {
|
||||||
|
test("renders search results header with search term", async () => {
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByText("Results for:")).toBeInTheDocument();
|
||||||
|
expect(await screen.findByText("test-search")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders search bar", async () => {
|
||||||
|
render(<MainSearchResultPage {...defaultProps} />);
|
||||||
|
|
||||||
|
expect(await screen.findByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -136,16 +136,19 @@ export const customMutator = async <
|
|||||||
response.statusText ||
|
response.statusText ||
|
||||||
`HTTP ${response.status}`;
|
`HTTP ${response.status}`;
|
||||||
|
|
||||||
console.error(
|
const isTestEnv = process.env.NODE_ENV === "test";
|
||||||
`Request failed ${environment.isServerSide() ? "on server" : "on client"}`,
|
if (!isTestEnv) {
|
||||||
{
|
console.error(
|
||||||
status: response.status,
|
`Request failed ${environment.isServerSide() ? "on server" : "on client"}`,
|
||||||
method,
|
{
|
||||||
url: fullUrl.replace(baseUrl, ""), // Show relative URL for cleaner logs
|
status: response.status,
|
||||||
errorMessage,
|
method,
|
||||||
responseData: responseData || "No response data",
|
url: fullUrl.replace(baseUrl, ""), // Show relative URL for cleaner logs
|
||||||
},
|
errorMessage,
|
||||||
);
|
responseData: responseData || "No response data",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
throw new ApiError(errorMessage, response.status, responseData);
|
throw new ApiError(errorMessage, response.status, responseData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,3 +218,61 @@ test("shows error when deletion fails", async () => {
|
|||||||
4. **Co-locate integration tests** - Keep `__tests__/` folder next to the component
|
4. **Co-locate integration tests** - Keep `__tests__/` folder next to the component
|
||||||
5. **E2E is expensive** - Only for critical happy paths; prefer integration tests
|
5. **E2E is expensive** - Only for critical happy paths; prefer integration tests
|
||||||
6. **AI agents are good at writing integration tests** - Start with these when adding test coverage
|
6. **AI agents are good at writing integration tests** - Start with these when adding test coverage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing 500 Server Errors
|
||||||
|
|
||||||
|
Orval auto-generates 422 validation error handlers, but 500 errors must be created manually. Use the helper:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { create500Handler } from "@/tests/integrations/helpers/create-500-handler";
|
||||||
|
|
||||||
|
test("handles server error", async () => {
|
||||||
|
server.use(create500Handler("get", "*/api/store/agents"));
|
||||||
|
render(<Component />);
|
||||||
|
expect(await screen.findByText("Failed to load")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `delayMs`: Add delay before response (for testing loading states)
|
||||||
|
- `body`: Custom error response body
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Auth-Dependent Components
|
||||||
|
|
||||||
|
For components that behave differently based on login state:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
mockAuthenticatedUser,
|
||||||
|
mockUnauthenticatedUser,
|
||||||
|
resetAuthState,
|
||||||
|
} from "@/tests/integrations/helpers/mock-supabase-auth";
|
||||||
|
|
||||||
|
describe("MyComponent", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
resetAuthState();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows feature when logged in", async () => {
|
||||||
|
mockAuthenticatedUser();
|
||||||
|
render(<MyComponent />);
|
||||||
|
expect(await screen.findByText("Premium Feature")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("hides feature when logged out", async () => {
|
||||||
|
mockUnauthenticatedUser();
|
||||||
|
render(<MyComponent />);
|
||||||
|
expect(screen.queryByText("Premium Feature")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("with custom user data", async () => {
|
||||||
|
mockAuthenticatedUser({ email: "custom@test.com" });
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { http, HttpResponse, delay } from "msw";
|
||||||
|
|
||||||
|
type HttpMethod = "get" | "post" | "put" | "patch" | "delete";
|
||||||
|
|
||||||
|
interface Create500HandlerOptions {
|
||||||
|
delayMs?: number;
|
||||||
|
body?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create500Handler(
|
||||||
|
method: HttpMethod,
|
||||||
|
url: string,
|
||||||
|
options?: Create500HandlerOptions,
|
||||||
|
) {
|
||||||
|
const { delayMs = 0, body } = options ?? {};
|
||||||
|
|
||||||
|
const responseBody = body ?? {
|
||||||
|
detail: "Internal Server Error",
|
||||||
|
};
|
||||||
|
|
||||||
|
return http[method](url, async () => {
|
||||||
|
if (delayMs > 0) {
|
||||||
|
await delay(delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse.json(responseBody, {
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { createContext, useContext, ReactNode } from "react";
|
||||||
|
import { UserOnboarding } from "@/lib/autogpt-server-api";
|
||||||
|
import { PostV1CompleteOnboardingStepStep } from "@/app/api/__generated__/models/postV1CompleteOnboardingStepStep";
|
||||||
|
import type { LocalOnboardingStateUpdate } from "@/providers/onboarding/helpers";
|
||||||
|
|
||||||
|
const MockOnboardingContext = createContext<{
|
||||||
|
state: UserOnboarding | null;
|
||||||
|
updateState: (state: LocalOnboardingStateUpdate) => void;
|
||||||
|
step: number;
|
||||||
|
setStep: (step: number) => void;
|
||||||
|
completeStep: (step: PostV1CompleteOnboardingStepStep) => void;
|
||||||
|
}>({
|
||||||
|
state: null,
|
||||||
|
updateState: () => {},
|
||||||
|
step: 1,
|
||||||
|
setStep: () => {},
|
||||||
|
completeStep: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useOnboarding(
|
||||||
|
_step?: number,
|
||||||
|
_completeStep?: PostV1CompleteOnboardingStepStep,
|
||||||
|
) {
|
||||||
|
const context = useContext(MockOnboardingContext);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MockOnboardingProvider({ children }: Props) {
|
||||||
|
return (
|
||||||
|
<MockOnboardingContext.Provider
|
||||||
|
value={{
|
||||||
|
state: null,
|
||||||
|
updateState: () => {},
|
||||||
|
step: 1,
|
||||||
|
setStep: () => {},
|
||||||
|
completeStep: () => {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MockOnboardingContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { User } from "@supabase/supabase-js";
|
||||||
|
import { useSupabaseStore } from "@/lib/supabase/hooks/useSupabaseStore";
|
||||||
|
|
||||||
|
export const mockUser: User = {
|
||||||
|
id: "test-user-id",
|
||||||
|
email: "test@example.com",
|
||||||
|
aud: "authenticated",
|
||||||
|
role: "authenticated",
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
app_metadata: {},
|
||||||
|
user_metadata: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function mockAuthenticatedUser(user: Partial<User> = {}): User {
|
||||||
|
const mergedUser = { ...mockUser, ...user };
|
||||||
|
|
||||||
|
useSupabaseStore.setState({
|
||||||
|
user: mergedUser,
|
||||||
|
isUserLoading: false,
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isValidating: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return mergedUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockUnauthenticatedUser(): void {
|
||||||
|
useSupabaseStore.setState({
|
||||||
|
user: null,
|
||||||
|
isUserLoading: false,
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isValidating: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetAuthState(): void {
|
||||||
|
useSupabaseStore.setState({
|
||||||
|
user: null,
|
||||||
|
isUserLoading: true,
|
||||||
|
hasLoadedUser: false,
|
||||||
|
isValidating: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
export function suppressReactQueryUpdateWarning() {
|
||||||
|
const originalError = console.error;
|
||||||
|
|
||||||
|
console.error = (...args: unknown[]) => {
|
||||||
|
const isActWarning = args.some(
|
||||||
|
(arg) =>
|
||||||
|
typeof arg === "string" &&
|
||||||
|
(arg.includes("not wrapped in act(...)") ||
|
||||||
|
(arg.includes("An update to") && arg.includes("inside a test"))),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isActWarning) {
|
||||||
|
const fullMessage = args
|
||||||
|
.map((arg) => String(arg))
|
||||||
|
.join("\n")
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
const isReactQueryRelated =
|
||||||
|
fullMessage.includes("queryclientprovider") ||
|
||||||
|
fullMessage.includes("react query") ||
|
||||||
|
fullMessage.includes("@tanstack/react-query");
|
||||||
|
|
||||||
|
if (isReactQueryRelated || fullMessage.includes("testproviders")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
originalError(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
console.error = originalError;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,14 +1,25 @@
|
|||||||
import { BackendAPIProvider } from "@/lib/autogpt-server-api/context";
|
import { BackendAPIProvider } from "@/lib/autogpt-server-api/context";
|
||||||
import OnboardingProvider from "@/providers/onboarding/onboarding-provider";
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { render, RenderOptions } from "@testing-library/react";
|
import { render, RenderOptions } from "@testing-library/react";
|
||||||
import { ReactElement, ReactNode } from "react";
|
import { ReactElement, ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
MockOnboardingProvider,
|
||||||
|
useOnboarding as mockUseOnboarding,
|
||||||
|
} from "./helpers/mock-onboarding-provider";
|
||||||
|
|
||||||
|
vi.mock("@/providers/onboarding/onboarding-provider", () => ({
|
||||||
|
useOnboarding: mockUseOnboarding,
|
||||||
|
default: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
function createTestQueryClient() {
|
function createTestQueryClient() {
|
||||||
return new QueryClient({
|
return new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
retry: false,
|
retry: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -19,7 +30,7 @@ function TestProviders({ children }: { children: ReactNode }) {
|
|||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<BackendAPIProvider>
|
<BackendAPIProvider>
|
||||||
<OnboardingProvider>{children}</OnboardingProvider>
|
<MockOnboardingProvider>{children}</MockOnboardingProvider>
|
||||||
</BackendAPIProvider>
|
</BackendAPIProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,11 +2,23 @@ import { beforeAll, afterAll, afterEach } from "vitest";
|
|||||||
import { server } from "@/mocks/mock-server";
|
import { server } from "@/mocks/mock-server";
|
||||||
import { mockNextjsModules } from "./setup-nextjs-mocks";
|
import { mockNextjsModules } from "./setup-nextjs-mocks";
|
||||||
import { mockSupabaseRequest } from "./mock-supabase-request";
|
import { mockSupabaseRequest } from "./mock-supabase-request";
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import { suppressReactQueryUpdateWarning } from "./helpers/suppress-react-query-update-warning";
|
||||||
|
|
||||||
|
let restoreConsoleError: (() => void) | null = null;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockNextjsModules();
|
mockNextjsModules();
|
||||||
mockSupabaseRequest(); // If you need user's data - please mock supabase actions in your specific test - it sends null user [It's only to avoid cookies() call]
|
mockSupabaseRequest();
|
||||||
return server.listen({ onUnhandledRequest: "error" });
|
restoreConsoleError = suppressReactQueryUpdateWarning();
|
||||||
|
server.listen({ onUnhandledRequest: "error" });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
server.resetHandlers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
restoreConsoleError?.();
|
||||||
|
server.close();
|
||||||
});
|
});
|
||||||
afterEach(() => server.resetHandlers());
|
|
||||||
afterAll(() => server.close());
|
|
||||||
|
|||||||
@@ -2,85 +2,14 @@ import { expect, test } from "@playwright/test";
|
|||||||
import { getTestUserWithLibraryAgents } from "./credentials";
|
import { getTestUserWithLibraryAgents } from "./credentials";
|
||||||
import { LoginPage } from "./pages/login.page";
|
import { LoginPage } from "./pages/login.page";
|
||||||
import { MarketplacePage } from "./pages/marketplace.page";
|
import { MarketplacePage } from "./pages/marketplace.page";
|
||||||
import { hasUrl, isVisible, matchesUrl } from "./utils/assertion";
|
import { hasUrl, isVisible } from "./utils/assertion";
|
||||||
import { getSelectors } from "./utils/selectors";
|
import { getSelectors } from "./utils/selectors";
|
||||||
|
|
||||||
function escapeRegExp(value: string) {
|
function escapeRegExp(value: string) {
|
||||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe("Marketplace Agent Page - Basic Functionality", () => {
|
test.describe("Marketplace Agent Page - Cross-Page Flows", () => {
|
||||||
test("User can access agent page when logged out", async ({ page }) => {
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
|
||||||
await firstStoreCard.click();
|
|
||||||
|
|
||||||
await page.waitForURL("**/marketplace/agent/**");
|
|
||||||
await matchesUrl(page, /\/marketplace\/agent\/.+/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("User can access agent page when logged in", async ({ page }) => {
|
|
||||||
const loginPage = new LoginPage(page);
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await loginPage.goto();
|
|
||||||
const richUser = getTestUserWithLibraryAgents();
|
|
||||||
await loginPage.login(richUser.email, richUser.password);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
|
||||||
await firstStoreCard.click();
|
|
||||||
|
|
||||||
await page.waitForURL("**/marketplace/agent/**");
|
|
||||||
await matchesUrl(page, /\/marketplace\/agent\/.+/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Agent page details are visible", async ({ page }) => {
|
|
||||||
const { getId } = getSelectors(page);
|
|
||||||
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
|
|
||||||
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
|
||||||
await firstStoreCard.click();
|
|
||||||
await page.waitForURL("**/marketplace/agent/**");
|
|
||||||
|
|
||||||
const agentTitle = getId("agent-title");
|
|
||||||
await isVisible(agentTitle);
|
|
||||||
|
|
||||||
const agentDescription = getId("agent-description");
|
|
||||||
await isVisible(agentDescription);
|
|
||||||
|
|
||||||
const creatorInfo = getId("agent-creator");
|
|
||||||
await isVisible(creatorInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Download button functionality works", async ({ page }) => {
|
|
||||||
const { getId, getText } = getSelectors(page);
|
|
||||||
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
|
|
||||||
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
|
||||||
await firstStoreCard.click();
|
|
||||||
await page.waitForURL("**/marketplace/agent/**");
|
|
||||||
|
|
||||||
const downloadButton = getId("agent-download-button");
|
|
||||||
await isVisible(downloadButton);
|
|
||||||
await downloadButton.click();
|
|
||||||
|
|
||||||
const downloadSuccessMessage = getText(
|
|
||||||
"Your agent has been successfully downloaded.",
|
|
||||||
);
|
|
||||||
await isVisible(downloadSuccessMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Add to library button works and agent appears in library", async ({
|
test("Add to library button works and agent appears in library", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -93,7 +22,6 @@ test.describe("Marketplace Agent Page - Basic Functionality", () => {
|
|||||||
const richUser = getTestUserWithLibraryAgents();
|
const richUser = getTestUserWithLibraryAgents();
|
||||||
await loginPage.login(richUser.email, richUser.password);
|
await loginPage.login(richUser.email, richUser.password);
|
||||||
await hasUrl(page, "/marketplace");
|
await hasUrl(page, "/marketplace");
|
||||||
await marketplacePage.goto(page);
|
|
||||||
|
|
||||||
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
const firstStoreCard = await marketplacePage.getFirstTopAgent();
|
||||||
await firstStoreCard.click();
|
await firstStoreCard.click();
|
||||||
|
|||||||
@@ -1,64 +1,8 @@
|
|||||||
import { test } from "@playwright/test";
|
import { test } from "@playwright/test";
|
||||||
import { getTestUserWithLibraryAgents } from "./credentials";
|
|
||||||
import { LoginPage } from "./pages/login.page";
|
|
||||||
import { MarketplacePage } from "./pages/marketplace.page";
|
import { MarketplacePage } from "./pages/marketplace.page";
|
||||||
import { hasUrl, isVisible, matchesUrl } from "./utils/assertion";
|
import { hasUrl, matchesUrl } from "./utils/assertion";
|
||||||
import { getSelectors } from "./utils/selectors";
|
|
||||||
|
|
||||||
test.describe("Marketplace Creator Page – Basic Functionality", () => {
|
|
||||||
test("User can access creator's page when logged out", async ({ page }) => {
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const firstCreatorProfile =
|
|
||||||
await marketplacePage.getFirstCreatorProfile(page);
|
|
||||||
await firstCreatorProfile.click();
|
|
||||||
|
|
||||||
await page.waitForURL("**/marketplace/creator/**");
|
|
||||||
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("User can access creator's page when logged in", async ({ page }) => {
|
|
||||||
const loginPage = new LoginPage(page);
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await loginPage.goto();
|
|
||||||
const richUser = getTestUserWithLibraryAgents();
|
|
||||||
await loginPage.login(richUser.email, richUser.password);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const firstCreatorProfile =
|
|
||||||
await marketplacePage.getFirstCreatorProfile(page);
|
|
||||||
await firstCreatorProfile.click();
|
|
||||||
|
|
||||||
await page.waitForURL("**/marketplace/creator/**");
|
|
||||||
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Creator page details are visible", async ({ page }) => {
|
|
||||||
const { getId } = getSelectors(page);
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const firstCreatorProfile =
|
|
||||||
await marketplacePage.getFirstCreatorProfile(page);
|
|
||||||
await firstCreatorProfile.click();
|
|
||||||
await page.waitForURL("**/marketplace/creator/**");
|
|
||||||
|
|
||||||
const creatorTitle = getId("creator-title");
|
|
||||||
await isVisible(creatorTitle);
|
|
||||||
|
|
||||||
const creatorDescription = getId("creator-description");
|
|
||||||
await isVisible(creatorDescription);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
test.describe("Marketplace Creator Page – Cross-Page Flows", () => {
|
||||||
test("Agents in agent by sections navigation works", async ({ page }) => {
|
test("Agents in agent by sections navigation works", async ({ page }) => {
|
||||||
const marketplacePage = new MarketplacePage(page);
|
const marketplacePage = new MarketplacePage(page);
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,8 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { getTestUserWithLibraryAgents } from "./credentials";
|
|
||||||
import { LoginPage } from "./pages/login.page";
|
|
||||||
import { MarketplacePage } from "./pages/marketplace.page";
|
import { MarketplacePage } from "./pages/marketplace.page";
|
||||||
import { hasMinCount, hasUrl, isVisible, matchesUrl } from "./utils/assertion";
|
import { isVisible, matchesUrl } from "./utils/assertion";
|
||||||
|
|
||||||
// Marketplace tests for store agent search functionality
|
|
||||||
test.describe("Marketplace – Basic Functionality", () => {
|
|
||||||
test("User can access marketplace page when logged out", async ({ page }) => {
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const marketplaceTitle = await marketplacePage.getMarketplaceTitle(page);
|
|
||||||
await isVisible(marketplaceTitle);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"User can access marketplace page when logged out test passed ✅",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("User can access marketplace page when logged in", async ({ page }) => {
|
|
||||||
const loginPage = new LoginPage(page);
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
|
|
||||||
await loginPage.goto();
|
|
||||||
const richUser = getTestUserWithLibraryAgents();
|
|
||||||
await loginPage.login(richUser.email, richUser.password);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
await hasUrl(page, "/marketplace");
|
|
||||||
|
|
||||||
const marketplaceTitle = await marketplacePage.getMarketplaceTitle(page);
|
|
||||||
await isVisible(marketplaceTitle);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"User can access marketplace page when logged in test passed ✅",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Featured agents, top agents, and featured creators are visible", async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
|
|
||||||
const featuredAgentsSection =
|
|
||||||
await marketplacePage.getFeaturedAgentsSection(page);
|
|
||||||
await isVisible(featuredAgentsSection);
|
|
||||||
const featuredAgentCards =
|
|
||||||
await marketplacePage.getFeaturedAgentCards(page);
|
|
||||||
await hasMinCount(featuredAgentCards, 1);
|
|
||||||
|
|
||||||
const topAgentsSection = await marketplacePage.getTopAgentsSection(page);
|
|
||||||
await isVisible(topAgentsSection);
|
|
||||||
const topAgentCards = await marketplacePage.getTopAgentCards(page);
|
|
||||||
await hasMinCount(topAgentCards, 1);
|
|
||||||
|
|
||||||
const featuredCreatorsSection =
|
|
||||||
await marketplacePage.getFeaturedCreatorsSection(page);
|
|
||||||
await isVisible(featuredCreatorsSection);
|
|
||||||
const creatorProfiles = await marketplacePage.getCreatorProfiles(page);
|
|
||||||
await hasMinCount(creatorProfiles, 1);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"Featured agents, top agents, and featured creators are visible test passed ✅",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
test.describe("Marketplace – Navigation", () => {
|
||||||
test("Can navigate and interact with marketplace elements", async ({
|
test("Can navigate and interact with marketplace elements", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -95,7 +29,7 @@ test.describe("Marketplace – Basic Functionality", () => {
|
|||||||
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"Can navigate and interact with marketplace elements test passed ✅",
|
"Can navigate and interact with marketplace elements test passed",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -128,32 +62,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
|||||||
const results = await marketplacePage.getSearchResultsCount(page);
|
const results = await marketplacePage.getSearchResultsCount(page);
|
||||||
expect(results).toBeGreaterThan(0);
|
expect(results).toBeGreaterThan(0);
|
||||||
|
|
||||||
console.log("Complete search flow works correctly test passed ✅");
|
console.log("Complete search flow works correctly test passed");
|
||||||
});
|
|
||||||
|
|
||||||
// We need to add a test search with filters, but the current business logic for filters doesn't work as expected. We'll add it once we modify that.
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe("Marketplace – Edge Cases", () => {
|
|
||||||
test("Search for non-existent item shows no results", async ({ page }) => {
|
|
||||||
const marketplacePage = new MarketplacePage(page);
|
|
||||||
await marketplacePage.goto(page);
|
|
||||||
|
|
||||||
await marketplacePage.searchAndNavigate("xyznonexistentitemxyz123", page);
|
|
||||||
|
|
||||||
await marketplacePage.waitForSearchResults();
|
|
||||||
|
|
||||||
await matchesUrl(page, /\/marketplace\/search\?searchTerm=/);
|
|
||||||
|
|
||||||
const resultsHeading = page.getByText("Results for:");
|
|
||||||
await isVisible(resultsHeading);
|
|
||||||
|
|
||||||
const searchTerm = page.getByText("xyznonexistentitemxyz123");
|
|
||||||
await isVisible(searchTerm);
|
|
||||||
|
|
||||||
const results = await marketplacePage.getSearchResultsCount(page);
|
|
||||||
expect(results).toBe(0);
|
|
||||||
|
|
||||||
console.log("Search for non-existent item shows no results test passed ✅");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"plugins": [{ "name": "next" }],
|
"plugins": [{ "name": "next" }],
|
||||||
|
"types": ["vitest/globals", "@testing-library/jest-dom/vitest"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ export default defineConfig({
|
|||||||
environment: "happy-dom",
|
environment: "happy-dom",
|
||||||
include: ["src/**/*.test.tsx"],
|
include: ["src/**/*.test.tsx"],
|
||||||
setupFiles: ["./src/tests/integrations/vitest.setup.tsx"],
|
setupFiles: ["./src/tests/integrations/vitest.setup.tsx"],
|
||||||
|
globals: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user