mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-22 05:28:02 -05:00
Compare commits
16 Commits
master
...
abhi/add-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4864c8d377 | ||
|
|
4a2a6a3c6a | ||
|
|
7176f52492 | ||
|
|
d8bede1dd5 | ||
|
|
3cdbd48423 | ||
|
|
9ab5d1704b | ||
|
|
cd76330183 | ||
|
|
1b4ced3a13 | ||
|
|
25dbab8cf7 | ||
|
|
d9ca19672d | ||
|
|
6e2c7ffbeb | ||
|
|
da978a7410 | ||
|
|
6960ba8f3e | ||
|
|
b22a4098bb | ||
|
|
1a5010f9e5 | ||
|
|
12f47eda10 |
38
.github/workflows/platform-frontend-ci.yml
vendored
38
.github/workflows/platform-frontend-ci.yml
vendored
@@ -128,7 +128,7 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exitOnceUploaded: true
|
||||
|
||||
test:
|
||||
e2e_test:
|
||||
runs-on: big-boi
|
||||
needs: setup
|
||||
strategy:
|
||||
@@ -258,3 +258,39 @@ jobs:
|
||||
- name: Print Final Docker Compose logs
|
||||
if: always()
|
||||
run: docker compose -f ../docker-compose.yml logs
|
||||
|
||||
integration_test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "22.18.0"
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Restore dependencies cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.pnpm-store
|
||||
key: ${{ needs.setup.outputs.cache-key }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-${{ hashFiles('autogpt_platform/frontend/pnpm-lock.yaml') }}
|
||||
${{ runner.os }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate API client
|
||||
run: pnpm generate:api
|
||||
|
||||
- name: Run Integration Tests
|
||||
run: pnpm test:unit
|
||||
|
||||
@@ -29,4 +29,4 @@ NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY=
|
||||
NEXT_PUBLIC_TURNSTILE=disabled
|
||||
|
||||
# PR previews
|
||||
NEXT_PUBLIC_PREVIEW_STEALING_DEV=
|
||||
NEXT_PUBLIC_PREVIEW_STEALING_DEV=
|
||||
|
||||
@@ -16,6 +16,12 @@ export default defineConfig({
|
||||
client: "react-query",
|
||||
httpClient: "fetch",
|
||||
indexFiles: false,
|
||||
mock: {
|
||||
type: "msw",
|
||||
baseUrl: "http://localhost:3000/api/proxy",
|
||||
generateEachHttpStatus: true,
|
||||
delay: 0,
|
||||
},
|
||||
override: {
|
||||
mutator: {
|
||||
path: "./mutators/custom-mutator.ts",
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"types": "tsc --noEmit",
|
||||
"test": "NEXT_PUBLIC_PW_TEST=true next build --turbo && playwright test",
|
||||
"test-ui": "NEXT_PUBLIC_PW_TEST=true next build --turbo && playwright test --ui",
|
||||
"test:unit": "vitest run",
|
||||
"test:unit:watch": "vitest",
|
||||
"test:no-build": "playwright test",
|
||||
"gentests": "playwright codegen http://localhost:3000",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
@@ -118,6 +120,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "4.1.2",
|
||||
"happy-dom": "20.3.4",
|
||||
"@opentelemetry/instrumentation": "0.209.0",
|
||||
"@playwright/test": "1.56.1",
|
||||
"@storybook/addon-a11y": "9.1.5",
|
||||
@@ -127,6 +130,8 @@
|
||||
"@storybook/nextjs": "9.1.5",
|
||||
"@tanstack/eslint-plugin-query": "5.91.2",
|
||||
"@tanstack/react-query-devtools": "5.90.2",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
"@types/lodash": "4.17.20",
|
||||
"@types/negotiator": "0.6.4",
|
||||
@@ -135,6 +140,7 @@
|
||||
"@types/react-dom": "18.3.5",
|
||||
"@types/react-modal": "3.16.3",
|
||||
"@types/react-window": "1.8.8",
|
||||
"@vitejs/plugin-react": "5.1.2",
|
||||
"axe-playwright": "2.2.2",
|
||||
"chromatic": "13.3.3",
|
||||
"concurrently": "9.2.1",
|
||||
@@ -153,7 +159,9 @@
|
||||
"require-in-the-middle": "8.0.1",
|
||||
"storybook": "9.1.5",
|
||||
"tailwindcss": "3.4.17",
|
||||
"typescript": "5.9.3"
|
||||
"typescript": "5.9.3",
|
||||
"vite-tsconfig-paths": "6.0.4",
|
||||
"vitest": "4.0.17"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": [
|
||||
|
||||
1118
autogpt_platform/frontend/pnpm-lock.yaml
generated
1118
autogpt_platform/frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
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();
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
// import { render, screen } from "@testing-library/react";
|
||||
// import { describe, expect, it } from "vitest";
|
||||
// import { Badge } from "./Badge";
|
||||
|
||||
// describe("Badge Component", () => {
|
||||
// it("renders badge with content", () => {
|
||||
// render(<Badge variant="success">Success</Badge>);
|
||||
|
||||
// expect(screen.getByText("Success")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("applies correct variant styles", () => {
|
||||
// const { rerender } = render(<Badge variant="success">Success</Badge>);
|
||||
// let badge = screen.getByText("Success");
|
||||
// expect(badge).toHaveClass("bg-green-100", "text-green-800");
|
||||
|
||||
// rerender(<Badge variant="error">Error</Badge>);
|
||||
// badge = screen.getByText("Error");
|
||||
// expect(badge).toHaveClass("bg-red-100", "text-red-800");
|
||||
|
||||
// rerender(<Badge variant="info">Info</Badge>);
|
||||
// badge = screen.getByText("Info");
|
||||
// expect(badge).toHaveClass("bg-slate-100", "text-slate-800");
|
||||
// });
|
||||
|
||||
// it("applies custom className", () => {
|
||||
// render(
|
||||
// <Badge variant="success" className="custom-class">
|
||||
// Success
|
||||
// </Badge>,
|
||||
// );
|
||||
|
||||
// const badge = screen.getByText("Success");
|
||||
// expect(badge).toHaveClass("custom-class");
|
||||
// });
|
||||
|
||||
// it("renders as span element", () => {
|
||||
// render(<Badge variant="success">Success</Badge>);
|
||||
|
||||
// const badge = screen.getByText("Success");
|
||||
// expect(badge.tagName).toBe("SPAN");
|
||||
// });
|
||||
|
||||
// it("renders children correctly", () => {
|
||||
// render(
|
||||
// <Badge variant="success">
|
||||
// <span>Custom</span> Content
|
||||
// </Badge>,
|
||||
// );
|
||||
|
||||
// expect(screen.getByText("Custom")).toBeInTheDocument();
|
||||
// expect(screen.getByText("Content")).toBeInTheDocument();
|
||||
// });
|
||||
|
||||
// it("supports all badge variants", () => {
|
||||
// const variants = ["success", "error", "info"] as const;
|
||||
|
||||
// variants.forEach((variant) => {
|
||||
// const { unmount } = render(
|
||||
// <Badge variant={variant} data-testid={`badge-${variant}`}>
|
||||
// {variant}
|
||||
// </Badge>,
|
||||
// );
|
||||
|
||||
// expect(screen.getByTestId(`badge-${variant}`)).toBeInTheDocument();
|
||||
// unmount();
|
||||
// });
|
||||
// });
|
||||
|
||||
// it("handles long text content", () => {
|
||||
// render(
|
||||
// <Badge variant="info">
|
||||
// Very long text that should be handled properly by the component
|
||||
// </Badge>,
|
||||
// );
|
||||
|
||||
// const badge = screen.getByText(/Very long text/);
|
||||
// expect(badge).toBeInTheDocument();
|
||||
// expect(badge).toHaveClass("overflow-hidden", "text-ellipsis");
|
||||
// });
|
||||
// });
|
||||
13
autogpt_platform/frontend/src/mocks/index.ts
Normal file
13
autogpt_platform/frontend/src/mocks/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// We are not using this for tests because Vitest runs our tests in a Node.js environment.
|
||||
// However, we can use it for development purposes to test our UI in the browser with fake data.
|
||||
export async function initMocks() {
|
||||
if (typeof window === "undefined") {
|
||||
const { server } = await import("./mock-server");
|
||||
server.listen({ onUnhandledRequest: "bypass" });
|
||||
console.log("[MSW] Server mock initialized");
|
||||
} else {
|
||||
const { worker } = await import("./mock-browser");
|
||||
await worker.start({ onUnhandledRequest: "bypass" });
|
||||
console.log("[MSW] Browser mock initialized");
|
||||
}
|
||||
}
|
||||
4
autogpt_platform/frontend/src/mocks/mock-browser.ts
Normal file
4
autogpt_platform/frontend/src/mocks/mock-browser.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { setupWorker } from "msw/browser";
|
||||
import { mockHandlers } from "./mock-handlers";
|
||||
|
||||
export const worker = setupWorker(...mockHandlers);
|
||||
48
autogpt_platform/frontend/src/mocks/mock-handlers.ts
Normal file
48
autogpt_platform/frontend/src/mocks/mock-handlers.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { getAdminMock } from "@/app/api/__generated__/endpoints/admin/admin.msw";
|
||||
import { getAnalyticsMock } from "@/app/api/__generated__/endpoints/analytics/analytics.msw";
|
||||
import { getApiKeysMock } from "@/app/api/__generated__/endpoints/api-keys/api-keys.msw";
|
||||
import { getAuthMock } from "@/app/api/__generated__/endpoints/auth/auth.msw";
|
||||
import { getBlocksMock } from "@/app/api/__generated__/endpoints/blocks/blocks.msw";
|
||||
import { getChatMock } from "@/app/api/__generated__/endpoints/chat/chat.msw";
|
||||
import { getCreditsMock } from "@/app/api/__generated__/endpoints/credits/credits.msw";
|
||||
import { getDefaultMock } from "@/app/api/__generated__/endpoints/default/default.msw";
|
||||
import { getEmailMock } from "@/app/api/__generated__/endpoints/email/email.msw";
|
||||
import { getExecutionsMock } from "@/app/api/__generated__/endpoints/executions/executions.msw";
|
||||
import { getFilesMock } from "@/app/api/__generated__/endpoints/files/files.msw";
|
||||
import { getGraphsMock } from "@/app/api/__generated__/endpoints/graphs/graphs.msw";
|
||||
import { getHealthMock } from "@/app/api/__generated__/endpoints/health/health.msw";
|
||||
import { getIntegrationsMock } from "@/app/api/__generated__/endpoints/integrations/integrations.msw";
|
||||
import { getLibraryMock } from "@/app/api/__generated__/endpoints/library/library.msw";
|
||||
import { getMetricsMock } from "@/app/api/__generated__/endpoints/metrics/metrics.msw";
|
||||
import { getOauthMock } from "@/app/api/__generated__/endpoints/oauth/oauth.msw";
|
||||
import { getOnboardingMock } from "@/app/api/__generated__/endpoints/onboarding/onboarding.msw";
|
||||
import { getOttoMock } from "@/app/api/__generated__/endpoints/otto/otto.msw";
|
||||
import { getPresetsMock } from "@/app/api/__generated__/endpoints/presets/presets.msw";
|
||||
import { getSchedulesMock } from "@/app/api/__generated__/endpoints/schedules/schedules.msw";
|
||||
import { getStoreMock } from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||
|
||||
// Pass hard-coded data to individual handler functions to override faker-generated data.
|
||||
export const mockHandlers = [
|
||||
...getAdminMock(),
|
||||
...getAnalyticsMock(),
|
||||
...getApiKeysMock(),
|
||||
...getAuthMock(),
|
||||
...getBlocksMock(),
|
||||
...getChatMock(),
|
||||
...getCreditsMock(),
|
||||
...getDefaultMock(),
|
||||
...getEmailMock(),
|
||||
...getExecutionsMock(),
|
||||
...getFilesMock(),
|
||||
...getGraphsMock(),
|
||||
...getHealthMock(),
|
||||
...getIntegrationsMock(),
|
||||
...getLibraryMock(),
|
||||
...getMetricsMock(),
|
||||
...getOauthMock(),
|
||||
...getOnboardingMock(),
|
||||
...getOttoMock(),
|
||||
...getPresetsMock(),
|
||||
...getSchedulesMock(),
|
||||
...getStoreMock(),
|
||||
];
|
||||
4
autogpt_platform/frontend/src/mocks/mock-server.ts
Normal file
4
autogpt_platform/frontend/src/mocks/mock-server.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { setupServer } from "msw/node";
|
||||
import { mockHandlers } from "./mock-handlers";
|
||||
|
||||
export const server = setupServer(...mockHandlers);
|
||||
220
autogpt_platform/frontend/src/tests/CLAUDE.md
Normal file
220
autogpt_platform/frontend/src/tests/CLAUDE.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Frontend Testing Rules 🧪
|
||||
|
||||
## Testing Types Overview
|
||||
|
||||
| Type | Tool | Speed | Purpose |
|
||||
| --------------- | --------------------- | --------------- | -------------------------------- |
|
||||
| **E2E** | Playwright | Slow (~5s/test) | Real browser, full user journeys |
|
||||
| **Integration** | Vitest + RTL | Fast (~100ms) | Component + mocked API |
|
||||
| **Unit** | Vitest + RTL | Fastest (~10ms) | Individual functions/components |
|
||||
| **Visual** | Storybook + Chromatic | N/A | UI appearance, design system |
|
||||
|
||||
---
|
||||
|
||||
## When to Use Each
|
||||
|
||||
### ✅ E2E Tests (Playwright)
|
||||
|
||||
**Use for:** Critical user journeys that MUST work in a real browser.
|
||||
|
||||
- Authentication flows (login, signup, logout)
|
||||
- Payment or sensitive transactions
|
||||
- Flows requiring real browser APIs (clipboard, downloads)
|
||||
- Cross-page navigation that must work end-to-end
|
||||
|
||||
**Location:** `src/tests/*.spec.ts` (centralized, as there will be fewer of them)
|
||||
|
||||
### ✅ Integration Tests (Vitest + RTL)
|
||||
|
||||
**Use for:** Testing components with their dependencies (API calls, state).
|
||||
|
||||
- Page-level behavior with mocked API responses
|
||||
- Components that fetch data
|
||||
- User interactions that trigger API calls
|
||||
- Feature flows within a single page
|
||||
|
||||
**Location:** Place tests in a `__tests__` folder next to the component:
|
||||
|
||||
```
|
||||
ComponentName/
|
||||
__tests__/
|
||||
main.test.tsx
|
||||
some-flow.test.tsx
|
||||
ComponentName.tsx
|
||||
useComponentName.ts
|
||||
```
|
||||
|
||||
**Start at page level:** Initially write integration tests at the "page" level. No need to write them for every small component.
|
||||
|
||||
```
|
||||
/library/
|
||||
__tests__/
|
||||
main.test.tsx
|
||||
searching-agents.test.tsx
|
||||
agents-pagination.test.tsx
|
||||
page.tsx
|
||||
useLibraryPage.ts
|
||||
```
|
||||
|
||||
Start with a `main.test.tsx` file and split into smaller files as it grows.
|
||||
|
||||
**What integration tests should do:**
|
||||
|
||||
1. Render a page or complex modal (e.g., `AgentPublishModal`)
|
||||
2. Mock API requests via MSW
|
||||
3. Assert UI scenarios via Testing Library
|
||||
|
||||
```tsx
|
||||
// Example: Test page renders data from API
|
||||
import { server } from "@/mocks/mock-server";
|
||||
import { getDeleteV2DeleteStoreSubmissionMockHandler422 } from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||
|
||||
test("shows error when submission fails", async () => {
|
||||
// Override default handler to return error status
|
||||
server.use(getDeleteV2DeleteStoreSubmissionMockHandler422());
|
||||
|
||||
render(<MarketplacePage />);
|
||||
await screen.findByText("Featured Agents");
|
||||
// ... assert error UI
|
||||
});
|
||||
```
|
||||
|
||||
**Tip:** Use `findBy...` methods most of the time—they wait for elements to appear, so async code won't cause flaky tests. The regular `getBy...` methods don't wait and error immediately.
|
||||
|
||||
### ✅ Unit Tests (Vitest + RTL)
|
||||
|
||||
**Use for:** Testing isolated components and utility functions.
|
||||
|
||||
- Pure utility functions (`lib/utils.ts`)
|
||||
- Component rendering with various props
|
||||
- Component state changes
|
||||
- Custom hooks
|
||||
|
||||
**Location:** Co-located with the file: `Component.test.tsx` next to `Component.tsx`
|
||||
|
||||
```tsx
|
||||
// Example: Test component renders correctly
|
||||
render(<AgentCard title="My Agent" />);
|
||||
expect(screen.getByText("My Agent")).toBeInTheDocument();
|
||||
```
|
||||
|
||||
### ✅ Storybook Tests (Visual)
|
||||
|
||||
**Use for:** Design system, visual appearance, component documentation.
|
||||
|
||||
- Atoms (Button, Input, Badge)
|
||||
- Molecules (Dialog, Card)
|
||||
- Visual states (hover, disabled, loading)
|
||||
- Responsive layouts
|
||||
|
||||
**Location:** Co-located: `Component.stories.tsx` next to `Component.tsx`
|
||||
|
||||
---
|
||||
|
||||
## Decision Flowchart
|
||||
|
||||
```
|
||||
Does it need a REAL browser/backend?
|
||||
├─ YES → E2E (Playwright)
|
||||
└─ NO
|
||||
└─ Does it involve API calls or complex state?
|
||||
├─ YES → Integration (Vitest + RTL)
|
||||
└─ NO
|
||||
└─ Is it about visual appearance?
|
||||
├─ YES → Storybook
|
||||
└─ NO → Unit (Vitest + RTL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What NOT to Test
|
||||
|
||||
❌ Third-party library internals (Radix UI, React Query)
|
||||
❌ CSS styling details (use Storybook)
|
||||
❌ Simple prop-passing components with no logic
|
||||
❌ TypeScript types
|
||||
|
||||
---
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ └── atoms/
|
||||
│ └── Button/
|
||||
│ ├── Button.tsx
|
||||
│ ├── Button.test.tsx # Unit test
|
||||
│ └── Button.stories.tsx # Visual test
|
||||
├── app/
|
||||
│ └── (platform)/
|
||||
│ └── marketplace/
|
||||
│ └── components/
|
||||
│ └── MainMarketplacePage/
|
||||
│ ├── __tests__/
|
||||
│ │ ├── main.test.tsx # Integration test
|
||||
│ │ └── search-agents.test.tsx # Integration test
|
||||
│ ├── MainMarketplacePage.tsx
|
||||
│ └── useMainMarketplacePage.ts
|
||||
├── lib/
|
||||
│ ├── utils.ts
|
||||
│ └── utils.test.ts # Unit test
|
||||
├── mocks/
|
||||
│ ├── mock-handlers.ts # MSW handlers (auto-generated via Orval)
|
||||
│ └── mock-server.ts # MSW server setup
|
||||
└── tests/
|
||||
├── integrations/
|
||||
│ ├── test-utils.tsx # Testing utilities
|
||||
│ └── vitest.setup.tsx # Integration test setup
|
||||
└── *.spec.ts # E2E tests (Playwright) - centralized
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority Matrix
|
||||
|
||||
| Component Type | Test Priority | Recommended Test |
|
||||
| ------------------- | ------------- | ---------------- |
|
||||
| Pages/Features | **Highest** | Integration |
|
||||
| Custom Hooks | High | Unit |
|
||||
| Utility Functions | High | Unit |
|
||||
| Organisms (complex) | High | Integration |
|
||||
| Molecules | Medium | Unit + Storybook |
|
||||
| Atoms | Medium | Storybook only\* |
|
||||
|
||||
\*Atoms are typically simple enough that Storybook visual tests suffice.
|
||||
|
||||
---
|
||||
|
||||
## MSW Mocking
|
||||
|
||||
API mocking is handled via MSW (Mock Service Worker). Handlers are auto-generated by Orval from the OpenAPI schema.
|
||||
|
||||
**Default behavior:** All client-side requests are intercepted and return 200 status with faker-generated data.
|
||||
|
||||
**Override for specific tests:** Use generated error handlers to test non-OK status scenarios:
|
||||
|
||||
```tsx
|
||||
import { server } from "@/mocks/mock-server";
|
||||
import { getDeleteV2DeleteStoreSubmissionMockHandler422 } from "@/app/api/__generated__/endpoints/store/store.msw";
|
||||
|
||||
test("shows error when deletion fails", async () => {
|
||||
server.use(getDeleteV2DeleteStoreSubmissionMockHandler422());
|
||||
|
||||
render(<MyComponent />);
|
||||
// ... assert error UI
|
||||
});
|
||||
```
|
||||
|
||||
**Generated handlers location:** `src/app/api/__generated__/endpoints/*/` - each endpoint has handlers for different status codes.
|
||||
|
||||
---
|
||||
|
||||
## Golden Rules
|
||||
|
||||
1. **Test behavior, not implementation** - Query by role/text, not class names
|
||||
2. **One assertion per concept** - Tests should be focused
|
||||
3. **Mock at boundaries** - Mock API calls, not internal functions
|
||||
4. **Co-locate integration tests** - Keep `__tests__/` folder next to the component
|
||||
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
|
||||
@@ -0,0 +1,25 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
const mockSupabaseClient = {
|
||||
auth: {
|
||||
getUser: vi.fn().mockResolvedValue({
|
||||
data: { user: null },
|
||||
error: null,
|
||||
}),
|
||||
getSession: vi.fn().mockResolvedValue({
|
||||
data: { session: null },
|
||||
error: null,
|
||||
}),
|
||||
signOut: vi.fn().mockResolvedValue({ error: null }),
|
||||
refreshSession: vi.fn().mockResolvedValue({
|
||||
data: { session: null, user: null },
|
||||
error: null,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const mockSupabaseRequest = () => {
|
||||
vi.mock("@/lib/supabase/server/getServerSupabase", () => ({
|
||||
getServerSupabase: vi.fn().mockResolvedValue(mockSupabaseClient),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
export const mockNextjsModules = () => {
|
||||
vi.mock("next/image", () => ({
|
||||
__esModule: true,
|
||||
default: ({
|
||||
fill: _fill,
|
||||
priority: _priority,
|
||||
quality: _quality,
|
||||
placeholder: _placeholder,
|
||||
blurDataURL: _blurDataURL,
|
||||
loader: _loader,
|
||||
...props
|
||||
}: any) => {
|
||||
// eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element
|
||||
return <img {...props} />;
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("next/headers", () => ({
|
||||
cookies: vi.fn(() => ({
|
||||
get: vi.fn(() => undefined),
|
||||
getAll: vi.fn(() => []),
|
||||
set: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
has: vi.fn(() => false),
|
||||
})),
|
||||
headers: vi.fn(() => new Headers()),
|
||||
}));
|
||||
|
||||
vi.mock("next/dist/server/request/cookies", () => ({
|
||||
cookies: vi.fn(() => ({
|
||||
get: vi.fn(() => undefined),
|
||||
getAll: vi.fn(() => []),
|
||||
set: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
has: vi.fn(() => false),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({
|
||||
push: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
prefetch: vi.fn(),
|
||||
back: vi.fn(),
|
||||
forward: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
}),
|
||||
usePathname: () => "/marketplace",
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
useParams: () => ({}),
|
||||
}));
|
||||
|
||||
vi.mock("next/link", () => ({
|
||||
__esModule: true,
|
||||
default: ({ children, href, ...props }: any) => (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { BackendAPIProvider } from "@/lib/autogpt-server-api/context";
|
||||
import OnboardingProvider from "@/providers/onboarding/onboarding-provider";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { render, RenderOptions } from "@testing-library/react";
|
||||
import { ReactElement, ReactNode } from "react";
|
||||
|
||||
function createTestQueryClient() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function TestProviders({ children }: { children: ReactNode }) {
|
||||
const queryClient = createTestQueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BackendAPIProvider>
|
||||
<OnboardingProvider>{children}</OnboardingProvider>
|
||||
</BackendAPIProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function customRender(
|
||||
ui: ReactElement,
|
||||
options?: Omit<RenderOptions, "wrapper">,
|
||||
) {
|
||||
return render(ui, { wrapper: TestProviders, ...options });
|
||||
}
|
||||
|
||||
export * from "@testing-library/react";
|
||||
export { customRender as render };
|
||||
@@ -0,0 +1,12 @@
|
||||
import { beforeAll, afterAll, afterEach } from "vitest";
|
||||
import { server } from "@/mocks/mock-server";
|
||||
import { mockNextjsModules } from "./setup-nextjs-mocks";
|
||||
import { mockSupabaseRequest } from "./mock-supabase-request";
|
||||
|
||||
beforeAll(() => {
|
||||
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]
|
||||
return server.listen({ onUnhandledRequest: "error" });
|
||||
});
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterAll(() => server.close());
|
||||
12
autogpt_platform/frontend/vitest.config.mts
Normal file
12
autogpt_platform/frontend/vitest.config.mts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths(), react()],
|
||||
test: {
|
||||
environment: "happy-dom",
|
||||
include: ["src/**/*.test.tsx"],
|
||||
setupFiles: ["./src/tests/integrations/vitest.setup.tsx"],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user