feat(frontend): Add main marketplace page tests and page object structure (#10427)

- Resolves -
https://github.com/Significant-Gravitas/AutoGPT/issues/10426
- Need to review this pr, once this issue is fixed -
https://github.com/Significant-Gravitas/AutoGPT/issues/10404

I’ve created additional tests for the main page, divided into two parts:
one for basic functionality and the other for edge cases.

**Basic functionality:**
- Users can access the marketplace page when logged out.
- Users can access the marketplace page when logged in.
- Featured agents, top agents, and featured creators are visible.
- Users can navigate and interact with marketplace elements.
- The complete search flow works correctly.

**Edge cases:**
- Searching for a non-existent item shows no results.

### Changes

- Introduced a new test suite for the marketplace, covering basic
functionality and edge cases.
- Implemented the MarketplacePage class to encapsulate interactions with
the marketplace page.
- Added utility functions for assertions, including visibility checks
and URL matching.
- Enhanced the LoginPage class with a goto method for navigation.
- Established a comprehensive search flow test to validate search
functionality.

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] I have done all the tests and they are working perfectly

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
Co-authored-by: Lluis Agusti <hi@llu.lu>
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
Co-authored-by: Ubbe <hi@ubbe.dev>
This commit is contained in:
Abhimanyu Yadav
2025-08-01 12:13:53 +05:30
committed by GitHub
parent d323dc2821
commit e371ef853a
4 changed files with 293 additions and 1 deletions

View File

@@ -0,0 +1,150 @@
import { test, expect } from "@playwright/test";
import { MarketplacePage } from "./pages/marketplace.page";
import { LoginPage } from "./pages/login.page";
import { isVisible, hasUrl, hasMinCount, matchesUrl } from "./utils/assertion";
import { TEST_CREDENTIALS } from "./credentials";
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();
await loginPage.login(TEST_CREDENTIALS.email, TEST_CREDENTIALS.password);
await hasUrl(page, "/marketplace");
await marketplacePage.goto(page);
await hasUrl(page, "/marketplace");
const marketplaceTitle = await marketplacePage.getMarketplaceTitle(page);
await isVisible(marketplaceTitle);
console.log(
"User can access marketplace page when logged in test passed ✅",
);
});
test("Featured agents, top agents, and featured creators are visible", async ({
page,
}) => {
const marketplacePage = new MarketplacePage(page);
await marketplacePage.goto(page);
const featuredAgentsSection =
await marketplacePage.getFeaturedAgentsSection(page);
await isVisible(featuredAgentsSection);
const featuredAgentCards =
await marketplacePage.getFeaturedAgentCards(page);
await hasMinCount(featuredAgentCards, 1);
const topAgentsSection = await marketplacePage.getTopAgentsSection(page);
await isVisible(topAgentsSection);
const topAgentCards = await marketplacePage.getTopAgentCards(page);
await hasMinCount(topAgentCards, 1);
const featuredCreatorsSection =
await marketplacePage.getFeaturedCreatorsSection(page);
await isVisible(featuredCreatorsSection);
const creatorProfiles = await marketplacePage.getCreatorProfiles(page);
await hasMinCount(creatorProfiles, 1);
console.log(
"Featured agents, top agents, and featured creators are visible test passed ✅",
);
});
test("Can navigate and interact with marketplace elements", async ({
page,
}) => {
const marketplacePage = new MarketplacePage(page);
await marketplacePage.goto(page);
const firstFeaturedAgent =
await marketplacePage.getFirstFeaturedAgent(page);
await firstFeaturedAgent.waitFor({ state: "visible" });
await firstFeaturedAgent.click();
await page.waitForURL("**/marketplace/agent/**");
await matchesUrl(page, /\/marketplace\/agent\/.+/);
await marketplacePage.goto(page);
const firstTopAgent = await marketplacePage.getFirstTopAgent();
await firstTopAgent.click();
await page.waitForURL("**/marketplace/agent/**");
await matchesUrl(page, /\/marketplace\/agent\/.+/);
await marketplacePage.goto(page);
const firstCreatorProfile =
await marketplacePage.getFirstCreatorProfile(page);
await firstCreatorProfile.click();
await page.waitForURL("**/marketplace/creator/**");
await matchesUrl(page, /\/marketplace\/creator\/.+/);
console.log(
"Can navigate and interact with marketplace elements test passed ✅",
);
});
test("Complete search flow works correctly", async ({ page }) => {
const marketplacePage = new MarketplacePage(page);
await marketplacePage.goto(page);
await marketplacePage.searchAndNavigate("DummyInput", page);
await marketplacePage.waitForSearchResults();
await matchesUrl(page, /\/marketplace\/search\?searchTerm=/);
const resultsHeading = page.getByText("Results for:");
await isVisible(resultsHeading);
const searchTerm = page.getByText("DummyInput").first();
await isVisible(searchTerm);
await page.waitForTimeout(10000);
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 ✅");
});
});

View File

@@ -3,6 +3,10 @@ import { Page } from "@playwright/test";
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto("/login");
}
async login(email: string, password: string) {
console.log(` Attempting login on ${this.page.url()} with`, {
email,

View File

@@ -0,0 +1,129 @@
import { Page } from "@playwright/test";
import { BasePage } from "./base.page";
import { getSelectors } from "../utils/selectors";
export class MarketplacePage extends BasePage {
constructor(page: Page) {
super(page);
}
async goto(page: Page) {
await page.goto("/marketplace");
}
async getMarketplaceTitle(page: Page) {
const { getText } = getSelectors(page);
return getText("Explore AI agents", { exact: false });
}
async getCreatorsSection(page: Page) {
const { getId, getText } = getSelectors(page);
return getId("creators-section") || getText("Creators", { exact: false });
}
async getAgentsSection(page: Page) {
const { getId, getText } = getSelectors(page);
return getId("agents-section") || getText("Agents", { exact: false });
}
async getCreatorsLink(page: Page) {
const { getLink } = getSelectors(page);
return getLink(/creators/i);
}
async getAgentsLink(page: Page) {
const { getLink } = getSelectors(page);
return getLink(/agents/i);
}
async getSearchInput(page: Page) {
const { getField, getId } = getSelectors(page);
return getId("store-search-input") || getField(/search/i);
}
async getFilterDropdown(page: Page) {
const { getId, getButton } = getSelectors(page);
return getId("filter-dropdown") || getButton(/filter/i);
}
async searchFor(query: string, page: Page) {
const searchInput = await this.getSearchInput(page);
await searchInput.fill(query);
await searchInput.press("Enter");
}
async clickCreators(page: Page) {
const creatorsLink = await this.getCreatorsLink(page);
await creatorsLink.click();
}
async clickAgents(page: Page) {
const agentsLink = await this.getAgentsLink(page);
await agentsLink.click();
}
async openFilter(page: Page) {
const filterDropdown = await this.getFilterDropdown(page);
await filterDropdown.click();
}
async getFeaturedAgentsSection(page: Page) {
const { getText } = getSelectors(page);
return getText("Featured agents");
}
async getTopAgentsSection(page: Page) {
const { getText } = getSelectors(page);
return getText("Top Agents");
}
async getFeaturedCreatorsSection(page: Page) {
const { getText } = getSelectors(page);
return getText("Featured Creators");
}
async getFeaturedAgentCards(page: Page) {
const { getId } = getSelectors(page);
return getId("featured-store-card");
}
async getTopAgentCards(page: Page) {
const { getId } = getSelectors(page);
return getId("store-card");
}
async getCreatorProfiles(page: Page) {
const { getId } = getSelectors(page);
return getId("creator-card");
}
async searchAndNavigate(query: string, page: Page) {
const searchInput = await this.getSearchInput(page);
await searchInput.fill(query);
await searchInput.press("Enter");
}
async waitForSearchResults() {
await this.page.waitForURL("**/marketplace/search**");
}
async getFirstFeaturedAgent(page: Page) {
const { getId } = getSelectors(page);
return getId("featured-store-card").first();
}
async getFirstTopAgent() {
return this.page.locator('[data-testid="store-card"]:visible').first();
}
async getFirstCreatorProfile(page: Page) {
const { getId } = getSelectors(page);
return getId("creator-card").first();
}
async getSearchResultsCount(page: Page) {
const { getId } = getSelectors(page);
const storeCards = getId("store-card");
return await storeCards.count();
}
}

View File

@@ -12,7 +12,7 @@ export async function hasAttribute(
await expect(el).toHaveAttribute(label, val);
}
export async function hasTextContent(el: Locator, text: string) {
export async function hasTextContent(el: Locator, text: string | RegExp) {
await expect(el).toContainText(text);
}
@@ -43,3 +43,12 @@ export async function isDisabled(el: Locator) {
export async function isEnabled(el: Locator) {
await expect(el).toBeEnabled();
}
export async function hasMinCount(el: Locator, minCount: number) {
const count = await el.count();
expect(count).toBeGreaterThanOrEqual(minCount);
}
export async function matchesUrl(page: Page, pattern: RegExp) {
expect(page.url()).toMatch(pattern);
}