chore: tests

This commit is contained in:
Lluis Agusti
2025-07-11 18:31:44 +04:00
parent b9d293f181
commit 5368fdc998
3 changed files with 463 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
import { test } from "./fixtures";
import { LibraryPage } from "./pages/library.page";
test.describe("Agent Notifications", () => {
let libraryPage: LibraryPage;
test.beforeEach(async ({ page, loginPage, testUser }) => {
libraryPage = new LibraryPage(page);
// Start each test with login using worker auth
await page.goto("/login");
await loginPage.login(testUser.email, testUser.password);
await test.expect(page).toHaveURL("/marketplace");
});
test("notification badge appears when agent is running", async ({ page }) => {
// Navigate to library
await libraryPage.navigateToLibrary();
await test.expect(page).toHaveURL(new RegExp("/library"));
// Click on first available agent
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
// Verify we're on agent page
await test.expect(libraryPage.isLoaded()).resolves.toBeTruthy();
// Initially, no notification badge should be visible
await test
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
.resolves.toBeFalsy();
// Run the agent
await libraryPage.runAgent();
// Wait for run to start and notification badge to appear
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Check that notification badge appears
await test
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
.resolves.toBeTruthy();
// Check that notification count is at least 1
const notificationCount =
await libraryPage.agentNotifications.getNotificationCount();
test.expect(parseInt(notificationCount)).toBeGreaterThan(0);
});
test("notification dropdown shows running agent with correct status", async ({
page: _page,
}) => {
// Navigate to library and run an agent
await libraryPage.navigateToLibrary();
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
const agentName = await libraryPage.getAgentName();
// Run the agent
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Click on notification button to open dropdown
await libraryPage.agentNotifications.clickNotificationButton();
// Verify dropdown is visible
await test
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
.resolves.toBeTruthy();
// Check that running agent appears in dropdown
await test
.expect(
libraryPage.agentNotifications.hasNotificationWithStatus("running"),
)
.resolves.toBeTruthy();
// Check that the agent name appears in notifications
const notification =
await libraryPage.agentNotifications.getNotificationByAgentName(
agentName,
);
test.expect(notification).not.toBeNull();
test.expect(notification?.status).toBe("running");
});
test("notification dropdown shows completed agent after run finishes", async ({
page: _page,
}, testInfo) => {
// Increase timeout for this test since we need to wait for completion
await test.setTimeout(testInfo.timeout * 3);
// Navigate to library and run an agent
await libraryPage.navigateToLibrary();
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
const agentName = await libraryPage.getAgentName();
// Run the agent
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Wait for agent to complete (with longer timeout)
await libraryPage.waitForRunToComplete(60000);
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Click on notification button to open dropdown
await libraryPage.agentNotifications.clickNotificationButton();
// Verify dropdown is visible
await test
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
.resolves.toBeTruthy();
// Check that completed agent appears in dropdown
const notification =
await libraryPage.agentNotifications.getNotificationByAgentName(
agentName,
);
test.expect(notification).not.toBeNull();
test.expect(notification?.status).toMatch(/completed|failed|terminated/);
});
test("notification dropdown shows correct time information", async ({
page: _page,
}) => {
// Navigate to library and run an agent
await libraryPage.navigateToLibrary();
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
const agentName = await libraryPage.getAgentName();
// Run the agent
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Click on notification button to open dropdown
await libraryPage.agentNotifications.clickNotificationButton();
// Get notification for this agent
const notification =
await libraryPage.agentNotifications.getNotificationByAgentName(
agentName,
);
test.expect(notification).not.toBeNull();
// Check that time information is present and contains expected text
test.expect(notification?.time).toContain("Started");
test.expect(notification?.time).toMatch(/Started.*ago.*seconds/);
});
test("notification dropdown shows multiple agents when multiple are running", async ({
page: _page,
}) => {
// Navigate to library
await libraryPage.navigateToLibrary();
// Run first agent
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
const firstAgentName = await libraryPage.getAgentName();
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Go back to library and run another agent (if available)
await libraryPage.navigateToLibrary();
const agentCards = await libraryPage.agentCards.count();
if (agentCards > 1) {
await libraryPage.agentCards.nth(1).click();
await libraryPage.waitForAgentPageLoad();
const secondAgentName = await libraryPage.getAgentName();
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Click on notification button to open dropdown
await libraryPage.agentNotifications.clickNotificationButton();
// Verify both agents appear in dropdown
const firstNotification =
await libraryPage.agentNotifications.getNotificationByAgentName(
firstAgentName,
);
const secondNotification =
await libraryPage.agentNotifications.getNotificationByAgentName(
secondAgentName,
);
test.expect(firstNotification).not.toBeNull();
test.expect(secondNotification).not.toBeNull();
// Check that notification count reflects multiple running agents
const notificationCount =
await libraryPage.agentNotifications.getNotificationCount();
test.expect(parseInt(notificationCount)).toBeGreaterThanOrEqual(2);
} else {
// Skip this part if only one agent is available
console.log("Only one agent available, skipping multiple agent test");
}
});
test("notification dropdown closes when clicking outside", async ({
page,
}) => {
// Navigate to library and run an agent
await libraryPage.navigateToLibrary();
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
// Run the agent
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Click on notification button to open dropdown
await libraryPage.agentNotifications.clickNotificationButton();
// Verify dropdown is visible
await test
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
.resolves.toBeTruthy();
// Click outside the dropdown
await page.click("body");
// Verify dropdown is no longer visible
await test
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
.resolves.toBeFalsy();
});
test("notification badge count updates correctly", async ({
page: _page,
}) => {
// Navigate to library and run an agent
await libraryPage.navigateToLibrary();
await libraryPage.clickFirstAgent();
await libraryPage.waitForAgentPageLoad();
// Initially, no notification badge should be visible
await test
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
.resolves.toBeFalsy();
// Run the agent
await libraryPage.runAgent();
await libraryPage.agentNotifications.waitForNotificationUpdate();
// Check that notification count is 1
const notificationCount =
await libraryPage.agentNotifications.getNotificationCount();
test.expect(notificationCount).toBe("1");
// Check that badge is visible and animating
await test
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
.resolves.toBeTruthy();
});
});

View File

@@ -0,0 +1,95 @@
import { Locator, Page } from "@playwright/test";
export class AgentNotificationsPage {
constructor(private page: Page) {}
get notificationButton(): Locator {
return this.page.locator('button[title="Agent Activity"]');
}
get notificationBadge(): Locator {
return this.page
.locator('button[title="Agent Activity"] .animate-spin')
.first();
}
get notificationDropdown(): Locator {
return this.page.locator('[role="dialog"]:has-text("Agent Activity")');
}
get notificationItems(): Locator {
return this.notificationDropdown.locator('[role="button"]');
}
async clickNotificationButton(): Promise<void> {
await this.notificationButton.click();
}
async isNotificationBadgeVisible(): Promise<boolean> {
return await this.notificationBadge.isVisible();
}
async isNotificationDropdownVisible(): Promise<boolean> {
return await this.notificationDropdown.isVisible();
}
async getNotificationCount(): Promise<string> {
const badge = this.page.locator(
'button[title="Agent Activity"] .bg-purple-600',
);
return (await badge.textContent()) || "0";
}
async getNotificationItems(): Promise<
{ name: string; status: string; time: string }[]
> {
const items = await this.notificationItems.all();
const results = [];
for (const item of items) {
const name = (await item.locator(".truncate").textContent()) || "";
const time =
(await item.locator(".\\!text-zinc-500").textContent()) || "";
// Determine status from icon classes and text content
let status = "unknown";
if (await item.locator(".animate-spin").isVisible()) {
status = "running";
} else if (await item.locator("svg").first().isVisible()) {
// For non-animated icons, check the text content to determine status
const timeText = time.toLowerCase();
if (timeText.includes("completed")) {
status = "completed";
} else if (timeText.includes("failed")) {
status = "failed";
} else if (timeText.includes("stopped")) {
status = "terminated";
} else if (timeText.includes("incomplete")) {
status = "incomplete";
} else if (timeText.includes("queued")) {
status = "queued";
}
}
results.push({ name, status, time });
}
return results;
}
async waitForNotificationUpdate(_timeout = 10000): Promise<void> {
await this.page.waitForTimeout(1000); // Wait for potential updates
}
async hasNotificationWithStatus(status: string): Promise<boolean> {
const items = await this.getNotificationItems();
return items.some((item) => item.status === status);
}
async getNotificationByAgentName(
agentName: string,
): Promise<{ name: string; status: string; time: string } | null> {
const items = await this.getNotificationItems();
return items.find((item) => item.name.includes(agentName)) || null;
}
}

View File

@@ -0,0 +1,107 @@
import { Locator, Page } from "@playwright/test";
import { AgentNotificationsPage } from "./agent-notifications.page";
export class LibraryPage {
public agentNotifications: AgentNotificationsPage;
constructor(private page: Page) {
this.agentNotifications = new AgentNotificationsPage(page);
}
get libraryTab(): Locator {
return this.page.locator('a[href="/library"]');
}
get agentCards(): Locator {
return this.page.locator(".agpt-div").filter({ hasText: /^test-agent-/ });
}
get runButton(): Locator {
return this.page.locator('button:has-text("Run")');
}
get newRunButton(): Locator {
return this.page.locator('button:has-text("New run")');
}
get runDialogRunButton(): Locator {
return this.page.locator('button:has-text("Run"):last-child');
}
get agentTitle(): Locator {
return this.page.locator("h1").first();
}
async navigateToLibrary(): Promise<void> {
await this.libraryTab.click();
await this.page.waitForURL(/.*\/library/);
}
async clickFirstAgent(): Promise<void> {
const firstAgent = this.agentCards.first();
await firstAgent.click();
}
async navigateToAgentByName(agentName: string): Promise<void> {
const agentCard = this.agentCards.filter({ hasText: agentName }).first();
await agentCard.click();
}
async clickRunButton(): Promise<void> {
await this.runButton.click();
}
async clickNewRunButton(): Promise<void> {
await this.newRunButton.click();
}
async runAgent(inputs: Record<string, string> = {}): Promise<void> {
await this.clickRunButton();
// Fill in any required inputs
for (const [key, value] of Object.entries(inputs)) {
const input = this.page.locator(
`input[placeholder*="${key}"], textarea[placeholder*="${key}"]`,
);
if (await input.isVisible()) {
await input.fill(value);
}
}
// Click the run button in the dialog
await this.runDialogRunButton.click();
}
async waitForAgentPageLoad(): Promise<void> {
await this.page.waitForURL(/.*\/library\/agents\/[^/]+/);
await this.page.waitForLoadState("networkidle");
}
async getAgentName(): Promise<string> {
return (await this.agentTitle.textContent()) || "";
}
async isLoaded(): Promise<boolean> {
return await this.page.locator("h1").isVisible();
}
async waitForRunToComplete(timeout = 30000): Promise<void> {
// Wait for completion badge or status change
await this.page.waitForSelector(
".bg-green-500, .bg-red-500, .bg-purple-500",
{ timeout },
);
}
async getRunStatus(): Promise<string> {
// Check for different status indicators
if (await this.page.locator(".animate-spin").isVisible()) {
return "running";
} else if (await this.page.locator(".bg-green-500").isVisible()) {
return "completed";
} else if (await this.page.locator(".bg-red-500").isVisible()) {
return "failed";
}
return "unknown";
}
}