mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
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:
150
autogpt_platform/frontend/src/tests/marketplace.spec.ts
Normal file
150
autogpt_platform/frontend/src/tests/marketplace.spec.ts
Normal 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 ✅");
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
129
autogpt_platform/frontend/src/tests/pages/marketplace.page.ts
Normal file
129
autogpt_platform/frontend/src/tests/pages/marketplace.page.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user