mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-30 01:18:07 -05:00
Compare commits
5 Commits
feat/sub-a
...
abhi/marke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6299045f98 | ||
|
|
24cd34ed3f | ||
|
|
582c6cad36 | ||
|
|
876c6677de | ||
|
|
3e3af45456 |
@@ -43,19 +43,24 @@ faker = Faker()
|
||||
# Constants for data generation limits (reduced for E2E tests)
|
||||
NUM_USERS = 15
|
||||
NUM_AGENT_BLOCKS = 30
|
||||
MIN_GRAPHS_PER_USER = 15
|
||||
MAX_GRAPHS_PER_USER = 15
|
||||
MIN_GRAPHS_PER_USER = 25
|
||||
MAX_GRAPHS_PER_USER = 25
|
||||
MIN_NODES_PER_GRAPH = 3
|
||||
MAX_NODES_PER_GRAPH = 6
|
||||
MIN_PRESETS_PER_USER = 2
|
||||
MAX_PRESETS_PER_USER = 3
|
||||
MIN_AGENTS_PER_USER = 15
|
||||
MAX_AGENTS_PER_USER = 15
|
||||
MIN_AGENTS_PER_USER = 25
|
||||
MAX_AGENTS_PER_USER = 25
|
||||
MIN_EXECUTIONS_PER_GRAPH = 2
|
||||
MAX_EXECUTIONS_PER_GRAPH = 8
|
||||
MIN_REVIEWS_PER_VERSION = 2
|
||||
MAX_REVIEWS_PER_VERSION = 5
|
||||
|
||||
# Guaranteed minimums for marketplace tests (deterministic)
|
||||
GUARANTEED_FEATURED_AGENTS = 8
|
||||
GUARANTEED_FEATURED_CREATORS = 5
|
||||
GUARANTEED_TOP_AGENTS = 10
|
||||
|
||||
|
||||
def get_image():
|
||||
"""Generate a consistent image URL using picsum.photos service."""
|
||||
@@ -385,7 +390,7 @@ class TestDataCreator:
|
||||
|
||||
library_agents = []
|
||||
for user in self.users:
|
||||
num_agents = 10 # Create exactly 10 agents per user
|
||||
num_agents = random.randint(MIN_AGENTS_PER_USER, MAX_AGENTS_PER_USER)
|
||||
|
||||
# Get available graphs for this user
|
||||
user_graphs = [
|
||||
@@ -507,14 +512,17 @@ class TestDataCreator:
|
||||
existing_profiles, min(num_creators, len(existing_profiles))
|
||||
)
|
||||
|
||||
# Mark about 50% of creators as featured (more for testing)
|
||||
num_featured = max(2, int(num_creators * 0.5))
|
||||
# Guarantee at least GUARANTEED_FEATURED_CREATORS featured creators
|
||||
num_featured = max(GUARANTEED_FEATURED_CREATORS, int(num_creators * 0.5))
|
||||
num_featured = min(
|
||||
num_featured, len(selected_profiles)
|
||||
) # Don't exceed available profiles
|
||||
featured_profile_ids = set(
|
||||
random.sample([p.id for p in selected_profiles], num_featured)
|
||||
)
|
||||
print(
|
||||
f"🎯 Creating {num_featured} featured creators (min: {GUARANTEED_FEATURED_CREATORS})"
|
||||
)
|
||||
|
||||
for profile in selected_profiles:
|
||||
try:
|
||||
@@ -545,21 +553,25 @@ class TestDataCreator:
|
||||
return profiles
|
||||
|
||||
async def create_test_store_submissions(self) -> List[Dict[str, Any]]:
|
||||
"""Create test store submissions using the API function."""
|
||||
"""Create test store submissions using the API function.
|
||||
|
||||
DETERMINISTIC: Guarantees minimum featured agents for E2E tests.
|
||||
"""
|
||||
print("Creating test store submissions...")
|
||||
|
||||
submissions = []
|
||||
approved_submissions = []
|
||||
featured_count = 0
|
||||
submission_counter = 0
|
||||
|
||||
# Create a special test submission for test123@gmail.com
|
||||
# Create a special test submission for test123@gmail.com (ALWAYS approved + featured)
|
||||
test_user = next(
|
||||
(user for user in self.users if user["email"] == "test123@gmail.com"), None
|
||||
)
|
||||
if test_user:
|
||||
# Special test data for consistent testing
|
||||
if test_user and self.agent_graphs:
|
||||
test_submission_data = {
|
||||
"user_id": test_user["id"],
|
||||
"agent_id": self.agent_graphs[0]["id"], # Use first available graph
|
||||
"agent_id": self.agent_graphs[0]["id"],
|
||||
"agent_version": 1,
|
||||
"slug": "test-agent-submission",
|
||||
"name": "Test Agent Submission",
|
||||
@@ -580,37 +592,24 @@ class TestDataCreator:
|
||||
submissions.append(test_submission.model_dump())
|
||||
print("✅ Created special test store submission for test123@gmail.com")
|
||||
|
||||
# Randomly approve, reject, or leave pending the test submission
|
||||
# ALWAYS approve and feature the test submission
|
||||
if test_submission.store_listing_version_id:
|
||||
random_value = random.random()
|
||||
if random_value < 0.4: # 40% chance to approve
|
||||
approved_submission = await review_store_submission(
|
||||
store_listing_version_id=test_submission.store_listing_version_id,
|
||||
is_approved=True,
|
||||
external_comments="Test submission approved",
|
||||
internal_comments="Auto-approved test submission",
|
||||
reviewer_id=test_user["id"],
|
||||
)
|
||||
approved_submissions.append(approved_submission.model_dump())
|
||||
print("✅ Approved test store submission")
|
||||
approved_submission = await review_store_submission(
|
||||
store_listing_version_id=test_submission.store_listing_version_id,
|
||||
is_approved=True,
|
||||
external_comments="Test submission approved",
|
||||
internal_comments="Auto-approved test submission",
|
||||
reviewer_id=test_user["id"],
|
||||
)
|
||||
approved_submissions.append(approved_submission.model_dump())
|
||||
print("✅ Approved test store submission")
|
||||
|
||||
# Mark approved submission as featured
|
||||
await prisma.storelistingversion.update(
|
||||
where={"id": test_submission.store_listing_version_id},
|
||||
data={"isFeatured": True},
|
||||
)
|
||||
print("🌟 Marked test agent as FEATURED")
|
||||
elif random_value < 0.7: # 30% chance to reject (40% to 70%)
|
||||
await review_store_submission(
|
||||
store_listing_version_id=test_submission.store_listing_version_id,
|
||||
is_approved=False,
|
||||
external_comments="Test submission rejected - needs improvements",
|
||||
internal_comments="Auto-rejected test submission for E2E testing",
|
||||
reviewer_id=test_user["id"],
|
||||
)
|
||||
print("❌ Rejected test store submission")
|
||||
else: # 30% chance to leave pending (70% to 100%)
|
||||
print("⏳ Left test submission pending for review")
|
||||
await prisma.storelistingversion.update(
|
||||
where={"id": test_submission.store_listing_version_id},
|
||||
data={"isFeatured": True},
|
||||
)
|
||||
featured_count += 1
|
||||
print("🌟 Marked test agent as FEATURED")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating test store submission: {e}")
|
||||
@@ -620,7 +619,6 @@ class TestDataCreator:
|
||||
|
||||
# Create regular submissions for all users
|
||||
for user in self.users:
|
||||
# Get available graphs for this specific user
|
||||
user_graphs = [
|
||||
g for g in self.agent_graphs if g.get("userId") == user["id"]
|
||||
]
|
||||
@@ -631,18 +629,17 @@ class TestDataCreator:
|
||||
)
|
||||
continue
|
||||
|
||||
# Create exactly 4 store submissions per user
|
||||
for submission_index in range(4):
|
||||
graph = random.choice(user_graphs)
|
||||
submission_counter += 1
|
||||
|
||||
try:
|
||||
print(
|
||||
f"Creating store submission for user {user['id']} with graph {graph['id']} (owner: {graph.get('userId')})"
|
||||
f"Creating store submission for user {user['id']} with graph {graph['id']}"
|
||||
)
|
||||
|
||||
# Use the API function to create store submission with correct parameters
|
||||
submission = await create_store_submission(
|
||||
user_id=user["id"], # Must match graph's userId
|
||||
user_id=user["id"],
|
||||
agent_id=graph["id"],
|
||||
agent_version=graph.get("version", 1),
|
||||
slug=faker.slug(),
|
||||
@@ -651,22 +648,24 @@ class TestDataCreator:
|
||||
video_url=get_video_url() if random.random() < 0.3 else None,
|
||||
image_urls=[get_image() for _ in range(3)],
|
||||
description=faker.text(),
|
||||
categories=[
|
||||
get_category()
|
||||
], # Single category from predefined list
|
||||
categories=[get_category()],
|
||||
changes_summary="Initial E2E test submission",
|
||||
)
|
||||
submissions.append(submission.model_dump())
|
||||
print(f"✅ Created store submission: {submission.name}")
|
||||
|
||||
# Randomly approve, reject, or leave pending the submission
|
||||
if submission.store_listing_version_id:
|
||||
random_value = random.random()
|
||||
if random_value < 0.4: # 40% chance to approve
|
||||
try:
|
||||
# Pick a random user as the reviewer (admin)
|
||||
reviewer_id = random.choice(self.users)["id"]
|
||||
# DETERMINISTIC: First N submissions are always approved
|
||||
# First GUARANTEED_FEATURED_AGENTS of those are always featured
|
||||
should_approve = (
|
||||
submission_counter <= GUARANTEED_TOP_AGENTS
|
||||
or random.random() < 0.4
|
||||
)
|
||||
should_feature = featured_count < GUARANTEED_FEATURED_AGENTS
|
||||
|
||||
if should_approve:
|
||||
try:
|
||||
reviewer_id = random.choice(self.users)["id"]
|
||||
approved_submission = await review_store_submission(
|
||||
store_listing_version_id=submission.store_listing_version_id,
|
||||
is_approved=True,
|
||||
@@ -681,16 +680,7 @@ class TestDataCreator:
|
||||
f"✅ Approved store submission: {submission.name}"
|
||||
)
|
||||
|
||||
# Mark some agents as featured during creation (30% chance)
|
||||
# More likely for creators and first submissions
|
||||
is_creator = user["id"] in [
|
||||
p.get("userId") for p in self.profiles
|
||||
]
|
||||
feature_chance = (
|
||||
0.5 if is_creator else 0.2
|
||||
) # 50% for creators, 20% for others
|
||||
|
||||
if random.random() < feature_chance:
|
||||
if should_feature:
|
||||
try:
|
||||
await prisma.storelistingversion.update(
|
||||
where={
|
||||
@@ -698,8 +688,25 @@ class TestDataCreator:
|
||||
},
|
||||
data={"isFeatured": True},
|
||||
)
|
||||
featured_count += 1
|
||||
print(
|
||||
f"🌟 Marked agent as FEATURED: {submission.name}"
|
||||
f"🌟 Marked agent as FEATURED ({featured_count}/{GUARANTEED_FEATURED_AGENTS}): {submission.name}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Warning: Could not mark submission as featured: {e}"
|
||||
)
|
||||
elif random.random() < 0.2:
|
||||
try:
|
||||
await prisma.storelistingversion.update(
|
||||
where={
|
||||
"id": submission.store_listing_version_id
|
||||
},
|
||||
data={"isFeatured": True},
|
||||
)
|
||||
featured_count += 1
|
||||
print(
|
||||
f"🌟 Marked agent as FEATURED (bonus): {submission.name}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(
|
||||
@@ -710,11 +717,9 @@ class TestDataCreator:
|
||||
print(
|
||||
f"Warning: Could not approve submission {submission.name}: {e}"
|
||||
)
|
||||
elif random_value < 0.7: # 30% chance to reject (40% to 70%)
|
||||
elif random.random() < 0.5:
|
||||
try:
|
||||
# Pick a random user as the reviewer (admin)
|
||||
reviewer_id = random.choice(self.users)["id"]
|
||||
|
||||
await review_store_submission(
|
||||
store_listing_version_id=submission.store_listing_version_id,
|
||||
is_approved=False,
|
||||
@@ -729,7 +734,7 @@ class TestDataCreator:
|
||||
print(
|
||||
f"Warning: Could not reject submission {submission.name}: {e}"
|
||||
)
|
||||
else: # 30% chance to leave pending (70% to 100%)
|
||||
else:
|
||||
print(
|
||||
f"⏳ Left submission pending for review: {submission.name}"
|
||||
)
|
||||
@@ -743,9 +748,13 @@ class TestDataCreator:
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
||||
print("\n📊 Store Submissions Summary:")
|
||||
print(f" Created: {len(submissions)}")
|
||||
print(f" Approved: {len(approved_submissions)}")
|
||||
print(
|
||||
f"Created {len(submissions)} store submissions, approved {len(approved_submissions)}"
|
||||
f" Featured: {featured_count} (guaranteed min: {GUARANTEED_FEATURED_AGENTS})"
|
||||
)
|
||||
|
||||
self.store_submissions = submissions
|
||||
return submissions
|
||||
|
||||
@@ -825,12 +834,15 @@ class TestDataCreator:
|
||||
print(f"✅ Agent blocks available: {len(self.agent_blocks)}")
|
||||
print(f"✅ Agent graphs created: {len(self.agent_graphs)}")
|
||||
print(f"✅ Library agents created: {len(self.library_agents)}")
|
||||
print(f"✅ Creator profiles updated: {len(self.profiles)} (some featured)")
|
||||
print(
|
||||
f"✅ Store submissions created: {len(self.store_submissions)} (some marked as featured during creation)"
|
||||
)
|
||||
print(f"✅ Creator profiles updated: {len(self.profiles)}")
|
||||
print(f"✅ Store submissions created: {len(self.store_submissions)}")
|
||||
print(f"✅ API keys created: {len(self.api_keys)}")
|
||||
print(f"✅ Presets created: {len(self.presets)}")
|
||||
print("\n🎯 Deterministic Guarantees:")
|
||||
print(f" • Featured agents: >= {GUARANTEED_FEATURED_AGENTS}")
|
||||
print(f" • Featured creators: >= {GUARANTEED_FEATURED_CREATORS}")
|
||||
print(f" • Top agents (approved): >= {GUARANTEED_TOP_AGENTS}")
|
||||
print(f" • Library agents per user: >= {MIN_AGENTS_PER_USER}")
|
||||
print("\n🚀 Your E2E test database is ready to use!")
|
||||
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
"@tanstack/eslint-plugin-query": "5.91.2",
|
||||
"@tanstack/react-query-devtools": "5.90.2",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
"@types/canvas-confetti": "1.9.0",
|
||||
"@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':
|
||||
specifier: 10.4.1
|
||||
version: 10.4.1
|
||||
'@testing-library/jest-dom':
|
||||
specifier: 6.9.1
|
||||
version: 6.9.1
|
||||
'@testing-library/react':
|
||||
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)
|
||||
|
||||
@@ -80,6 +80,7 @@ export const AgentInfo = ({
|
||||
const allVersions = storeData?.versions
|
||||
? storeData.versions
|
||||
.map((versionStr: string) => parseInt(versionStr, 10))
|
||||
.filter((versionNum: number) => !isNaN(versionNum))
|
||||
.sort((a: number, b: number) => b - a)
|
||||
.map((versionNum: number) => ({
|
||||
version: versionNum,
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
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,63 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } 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} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("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} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("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} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("Failed to load creator data", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("retry button is visible on error", async () => {
|
||||
server.use(getGetV2GetCreatorDetailsMockHandler422());
|
||||
|
||||
render(<MainCreatorPage params={defaultParams} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByRole("button", { name: /try again/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } from "@/tests/integrations/test-utils";
|
||||
import { MainCreatorPage } from "../MainCreatorPage";
|
||||
|
||||
const defaultParams = {
|
||||
creator: "test-creator",
|
||||
};
|
||||
|
||||
describe("MainCreatorPage - Rendering", () => {
|
||||
test("renders creator info card", async () => {
|
||||
render(<MainCreatorPage params={defaultParams} />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("creator-description")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("renders breadcrumbs with marketplace link", async () => {
|
||||
render(<MainCreatorPage params={defaultParams} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByRole("link", { name: /marketplace/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("renders about section", async () => {
|
||||
render(<MainCreatorPage params={defaultParams} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("About")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("renders agents by creator section", async () => {
|
||||
render(<MainCreatorPage params={defaultParams} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/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,87 @@
|
||||
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 />);
|
||||
|
||||
// Page should still render without crashing
|
||||
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 />);
|
||||
|
||||
// Page should still render without crashing
|
||||
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 />);
|
||||
|
||||
// Page should still render the search bar
|
||||
expect(
|
||||
await screen.findByPlaceholderText(/search/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } 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 />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("Failed to load marketplace data", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("displays error when creators API returns 422", async () => {
|
||||
server.use(getGetV2ListStoreCreatorsMockHandler422());
|
||||
|
||||
render(<MainMarkeplacePage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("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 />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("Failed to load marketplace data", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("retry button is visible on error", async () => {
|
||||
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||
|
||||
render(<MainMarkeplacePage />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByRole("button", { name: /try again/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render } 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 () => {
|
||||
// Override handlers to add delay to simulate loading
|
||||
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 />);
|
||||
|
||||
// Check for loading skeleton elements (animated pulse elements)
|
||||
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,37 @@
|
||||
import { describe, expect, test, afterEach } from "vitest";
|
||||
import { render, screen, waitFor } 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: undefined as undefined,
|
||||
};
|
||||
|
||||
describe("MainSearchResultPage - Auth State", () => {
|
||||
afterEach(() => {
|
||||
resetAuthState();
|
||||
});
|
||||
|
||||
test("renders page correctly when logged out", async () => {
|
||||
mockUnauthenticatedUser();
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Results for:")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("renders page correctly when logged in", async () => {
|
||||
mockAuthenticatedUser();
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Results for:")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } 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: undefined as undefined,
|
||||
};
|
||||
|
||||
describe("MainSearchResultPage - Error Handling", () => {
|
||||
test("displays error when agents API returns 422", async () => {
|
||||
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("Failed to load marketplace data", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("displays error when creators API returns 422", async () => {
|
||||
server.use(getGetV2ListStoreCreatorsMockHandler422());
|
||||
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("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} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText("Failed to load marketplace data", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("retry button is visible on error", async () => {
|
||||
server.use(getGetV2ListStoreAgentsMockHandler422());
|
||||
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByRole("button", { name: /try again/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } 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: undefined as undefined,
|
||||
};
|
||||
|
||||
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} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Results for:")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Verify search term is displayed
|
||||
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,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("nonexistent-search-term-xyz")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("search bar remains functional 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,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { render, screen, waitFor } from "@/tests/integrations/test-utils";
|
||||
import { MainSearchResultPage } from "../MainSearchResultPage";
|
||||
|
||||
const defaultProps = {
|
||||
searchTerm: "test-search",
|
||||
sort: undefined as undefined,
|
||||
};
|
||||
|
||||
describe("MainSearchResultPage - Rendering", () => {
|
||||
test("renders search results header with search term", async () => {
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Results for:")).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText("test-search")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders search bar", async () => {
|
||||
render(<MainSearchResultPage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -136,16 +136,19 @@ export const customMutator = async <
|
||||
response.statusText ||
|
||||
`HTTP ${response.status}`;
|
||||
|
||||
console.error(
|
||||
`Request failed ${environment.isServerSide() ? "on server" : "on client"}`,
|
||||
{
|
||||
status: response.status,
|
||||
method,
|
||||
url: fullUrl.replace(baseUrl, ""), // Show relative URL for cleaner logs
|
||||
errorMessage,
|
||||
responseData: responseData || "No response data",
|
||||
},
|
||||
);
|
||||
const isTestEnv = process.env.NODE_ENV === 'test';
|
||||
if (!isTestEnv) {
|
||||
console.error(
|
||||
`Request failed ${environment.isServerSide() ? "on server" : "on client"}`,
|
||||
{
|
||||
status: response.status,
|
||||
method,
|
||||
url: fullUrl.replace(baseUrl, ""), // Show relative URL for cleaner logs
|
||||
errorMessage,
|
||||
responseData: responseData || "No response data",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
---
|
||||
|
||||
## 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?: Record<string, 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,42 @@
|
||||
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;
|
||||
}
|
||||
|
||||
export function MockOnboardingProvider({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<MockOnboardingContext.Provider
|
||||
value={{
|
||||
state: null,
|
||||
updateState: () => {},
|
||||
step: 1,
|
||||
setStep: () => {},
|
||||
completeStep: () => {},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</MockOnboardingContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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,
|
||||
});
|
||||
|
||||
return mergedUser;
|
||||
}
|
||||
|
||||
export function mockUnauthenticatedUser(): void {
|
||||
useSupabaseStore.setState({
|
||||
user: null,
|
||||
isUserLoading: false,
|
||||
hasLoadedUser: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function resetAuthState(): void {
|
||||
useSupabaseStore.setState({
|
||||
user: null,
|
||||
isUserLoading: true,
|
||||
hasLoadedUser: false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Suppresses expected act(...) warnings from React Query and component async updates.
|
||||
// These warnings are normal behavior with React Query and don't indicate test failures.
|
||||
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 cleanup function
|
||||
return () => {
|
||||
console.error = originalError;
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
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 { render, RenderOptions, act } from "@testing-library/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() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -19,7 +27,7 @@ function TestProviders({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BackendAPIProvider>
|
||||
<OnboardingProvider>{children}</OnboardingProvider>
|
||||
<MockOnboardingProvider>{children}</MockOnboardingProvider>
|
||||
</BackendAPIProvider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,17 @@ import { beforeAll, afterAll, afterEach } from "vitest";
|
||||
import { server } from "@/mocks/mock-server";
|
||||
import { mockNextjsModules } from "./setup-nextjs-mocks";
|
||||
import { mockSupabaseRequest } from "./mock-supabase-request";
|
||||
import "@testing-library/jest-dom";
|
||||
import { suppressReactQueryUpdateWarning } from "./helpers/supress-react-query-update-warning";
|
||||
|
||||
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]
|
||||
mockSupabaseRequest();
|
||||
const restoreConsoleError = suppressReactQueryUpdateWarning();
|
||||
afterAll(() => {
|
||||
restoreConsoleError();
|
||||
});
|
||||
return server.listen({ onUnhandledRequest: "error" });
|
||||
});
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterEach(() => {server.resetHandlers()});
|
||||
afterAll(() => server.close());
|
||||
|
||||
@@ -59,12 +59,13 @@ test.describe("Library", () => {
|
||||
});
|
||||
|
||||
test("pagination works correctly", async ({ page }, testInfo) => {
|
||||
test.setTimeout(testInfo.timeout * 3); // Increase timeout for pagination operations
|
||||
test.setTimeout(testInfo.timeout * 3);
|
||||
await page.goto("/library");
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
const paginationResult = await libraryPage.testPagination();
|
||||
|
||||
if (paginationResult.initialCount >= 10) {
|
||||
if (paginationResult.initialCount >= PAGE_SIZE) {
|
||||
expect(paginationResult.finalCount).toBeGreaterThanOrEqual(
|
||||
paginationResult.initialCount,
|
||||
);
|
||||
@@ -133,7 +134,10 @@ test.describe("Library", () => {
|
||||
test.expect(clearedSearchValue).toBe("");
|
||||
});
|
||||
|
||||
test("pagination while searching works correctly", async ({ page }) => {
|
||||
test("pagination while searching works correctly", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.setTimeout(testInfo.timeout * 3);
|
||||
await page.goto("/library");
|
||||
|
||||
const allAgents = await libraryPage.getAgents();
|
||||
@@ -152,9 +156,10 @@ test.describe("Library", () => {
|
||||
);
|
||||
expect(matchingResults.length).toEqual(initialSearchResults.length);
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
const searchPaginationResult = await libraryPage.testPagination();
|
||||
|
||||
if (searchPaginationResult.initialCount >= 10) {
|
||||
if (searchPaginationResult.initialCount >= PAGE_SIZE) {
|
||||
expect(searchPaginationResult.finalCount).toBeGreaterThanOrEqual(
|
||||
searchPaginationResult.initialCount,
|
||||
);
|
||||
|
||||
@@ -9,78 +9,7 @@ function escapeRegExp(value: string) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
test.describe("Marketplace Agent Page - Basic Functionality", () => {
|
||||
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.describe("Marketplace Agent Page - Cross-Page Flows", () => {
|
||||
test("Add to library button works and agent appears in library", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
@@ -1,64 +1,8 @@
|
||||
import { test } from "@playwright/test";
|
||||
import { getTestUserWithLibraryAgents } from "./credentials";
|
||||
import { LoginPage } from "./pages/login.page";
|
||||
import { MarketplacePage } from "./pages/marketplace.page";
|
||||
import { hasUrl, isVisible, 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);
|
||||
});
|
||||
import { hasUrl, matchesUrl } from "./utils/assertion";
|
||||
|
||||
test.describe("Marketplace Creator Page – Cross-Page Flows", () => {
|
||||
test("Agents in agent by sections navigation works", async ({ page }) => {
|
||||
const marketplacePage = new MarketplacePage(page);
|
||||
|
||||
@@ -69,9 +13,12 @@ test.describe("Marketplace Creator Page – Basic Functionality", () => {
|
||||
await marketplacePage.getFirstCreatorProfile(page);
|
||||
await firstCreatorProfile.click();
|
||||
await page.waitForURL("**/marketplace/creator/**");
|
||||
await page.waitForLoadState("networkidle").catch(() => {});
|
||||
|
||||
const firstAgent = page
|
||||
.locator('[data-testid="store-card"]:visible')
|
||||
.first();
|
||||
await firstAgent.waitFor({ state: "visible", timeout: 30000 });
|
||||
|
||||
await firstAgent.click();
|
||||
await page.waitForURL("**/marketplace/agent/**");
|
||||
|
||||
@@ -1,74 +1,8 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { getTestUserWithLibraryAgents } from "./credentials";
|
||||
import { LoginPage } from "./pages/login.page";
|
||||
import { MarketplacePage } from "./pages/marketplace.page";
|
||||
import { hasMinCount, hasUrl, 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 ✅",
|
||||
);
|
||||
});
|
||||
import { isVisible, matchesUrl } from "./utils/assertion";
|
||||
|
||||
test.describe("Marketplace – Navigation", () => {
|
||||
test("Can navigate and interact with marketplace elements", async ({
|
||||
page,
|
||||
}) => {
|
||||
@@ -77,7 +11,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
|
||||
const firstFeaturedAgent =
|
||||
await marketplacePage.getFirstFeaturedAgent(page);
|
||||
await firstFeaturedAgent.waitFor({ state: "visible" });
|
||||
await firstFeaturedAgent.click();
|
||||
await page.waitForURL("**/marketplace/agent/**");
|
||||
await matchesUrl(page, /\/marketplace\/agent\/.+/);
|
||||
@@ -96,7 +29,7 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
||||
|
||||
console.log(
|
||||
"Can navigate and interact with marketplace elements test passed ✅",
|
||||
"Can navigate and interact with marketplace elements test passed",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -116,37 +49,19 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
const searchTerm = page.getByText("DummyInput").first();
|
||||
await isVisible(searchTerm);
|
||||
|
||||
await page.waitForTimeout(10000);
|
||||
await page.waitForLoadState("networkidle").catch(() => {});
|
||||
|
||||
await page
|
||||
.waitForFunction(
|
||||
() =>
|
||||
document.querySelectorAll('[data-testid="store-card"]').length > 0,
|
||||
{ timeout: 15000 },
|
||||
)
|
||||
.catch(() => console.log("No search results appeared within timeout"));
|
||||
|
||||
const results = await marketplacePage.getSearchResultsCount(page);
|
||||
expect(results).toBeGreaterThan(0);
|
||||
|
||||
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 ✅");
|
||||
console.log("Complete search flow works correctly test passed");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -300,21 +300,27 @@ export class LibraryPage extends BasePage {
|
||||
async scrollToLoadMore(): Promise<void> {
|
||||
console.log(`scrolling to load more agents`);
|
||||
|
||||
// Get initial agent count
|
||||
const initialCount = await this.getAgentCount();
|
||||
console.log(`Initial agent count: ${initialCount}`);
|
||||
const initialCount = await this.getAgentCountByListLength();
|
||||
console.log(`Initial agent count (DOM cards): ${initialCount}`);
|
||||
|
||||
// Scroll down to trigger pagination
|
||||
await this.scrollToBottom();
|
||||
|
||||
// Wait for potential new agents to load
|
||||
await this.page.waitForTimeout(2000);
|
||||
await this.page
|
||||
.waitForLoadState("networkidle", { timeout: 10000 })
|
||||
.catch(() => console.log("Network idle timeout, continuing..."));
|
||||
|
||||
// Check if more agents loaded
|
||||
const newCount = await this.getAgentCount();
|
||||
console.log(`New agent count after scroll: ${newCount}`);
|
||||
await this.page
|
||||
.waitForFunction(
|
||||
(prevCount) =>
|
||||
document.querySelectorAll('[data-testid="library-agent-card"]')
|
||||
.length > prevCount,
|
||||
initialCount,
|
||||
{ timeout: 5000 },
|
||||
)
|
||||
.catch(() => {});
|
||||
|
||||
return;
|
||||
const newCount = await this.getAgentCountByListLength();
|
||||
console.log(`New agent count after scroll (DOM cards): ${newCount}`);
|
||||
}
|
||||
|
||||
async testPagination(): Promise<{
|
||||
|
||||
@@ -9,6 +9,7 @@ export class MarketplacePage extends BasePage {
|
||||
|
||||
async goto(page: Page) {
|
||||
await page.goto("/marketplace");
|
||||
await page.waitForLoadState("networkidle").catch(() => {});
|
||||
}
|
||||
|
||||
async getMarketplaceTitle(page: Page) {
|
||||
@@ -109,16 +110,24 @@ export class MarketplacePage extends BasePage {
|
||||
|
||||
async getFirstFeaturedAgent(page: Page) {
|
||||
const { getId } = getSelectors(page);
|
||||
return getId("featured-store-card").first();
|
||||
const card = getId("featured-store-card").first();
|
||||
await card.waitFor({ state: "visible", timeout: 30000 });
|
||||
return card;
|
||||
}
|
||||
|
||||
async getFirstTopAgent() {
|
||||
return this.page.locator('[data-testid="store-card"]:visible').first();
|
||||
const card = this.page
|
||||
.locator('[data-testid="store-card"]:visible')
|
||||
.first();
|
||||
await card.waitFor({ state: "visible", timeout: 30000 });
|
||||
return card;
|
||||
}
|
||||
|
||||
async getFirstCreatorProfile(page: Page) {
|
||||
const { getId } = getSelectors(page);
|
||||
return getId("creator-card").first();
|
||||
const card = getId("creator-card").first();
|
||||
await card.waitFor({ state: "visible", timeout: 30000 });
|
||||
return card;
|
||||
}
|
||||
|
||||
async getSearchResultsCount(page: Page) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }],
|
||||
"types": ["vitest/globals", "@testing-library/jest-dom/vitest"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ export default defineConfig({
|
||||
environment: "happy-dom",
|
||||
include: ["src/**/*.test.tsx"],
|
||||
setupFiles: ["./src/tests/integrations/vitest.setup.tsx"],
|
||||
globals: true,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user