From 9ec7e4bebf30e36c9eec14209e4933c81dcbe8fe Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Thu, 28 May 2026 14:03:23 -0300 Subject: [PATCH] test(agent-skills): add e2e playwright spec for agent-skills page (GAP-B) --- tests/e2e/agent-skills-page.spec.ts | 204 ++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 tests/e2e/agent-skills-page.spec.ts diff --git a/tests/e2e/agent-skills-page.spec.ts b/tests/e2e/agent-skills-page.spec.ts new file mode 100644 index 0000000000..50b8006d93 --- /dev/null +++ b/tests/e2e/agent-skills-page.spec.ts @@ -0,0 +1,204 @@ +import { expect, test, type Route } from "@playwright/test"; +import { gotoDashboardRoute } from "./helpers/dashboardAuth"; + +const NAVIGATION_TIMEOUT_MS = 300_000; + +type AgentSkill = { + id: string; + name: string; + description: string; + category: "api" | "cli"; + area: string; + icon: string; + endpoints?: string[]; + cliCommands?: string[]; + rawUrl: string; + githubUrl: string; +}; + +type SkillCoverage = { + api: { have: number; total: number }; + cli: { have: number; total: number }; + totalSkills: number; + generatedAt: string; +}; + +function makeAgentSkills(): AgentSkill[] { + const skills: AgentSkill[] = []; + for (let i = 0; i < 22; i++) { + skills.push({ + id: `omni-skill-${i}`, + name: `API Skill ${i}`, + description: `API skill description ${i}`, + category: "api", + area: `area-${i}`, + icon: "api", + endpoints: [`GET /api/skill-${i}`], + rawUrl: `https://raw.githubusercontent.com/example/OmniRoute/main/skills/omni-skill-${i}/SKILL.md`, + githubUrl: `https://github.com/example/OmniRoute/blob/main/skills/omni-skill-${i}/SKILL.md`, + }); + } + for (let i = 0; i < 20; i++) { + skills.push({ + id: `cli-skill-${i}`, + name: `CLI Skill ${i}`, + description: `CLI skill description ${i}`, + category: "cli", + area: `cli-area-${i}`, + icon: "terminal", + cliCommands: [`skill${i} run`], + rawUrl: `https://raw.githubusercontent.com/example/OmniRoute/main/skills/cli-skill-${i}/SKILL.md`, + githubUrl: `https://github.com/example/OmniRoute/blob/main/skills/cli-skill-${i}/SKILL.md`, + }); + } + return skills; +} + +const FULL_COVERAGE: SkillCoverage = { + api: { have: 22, total: 22 }, + cli: { have: 20, total: 20 }, + totalSkills: 42, + generatedAt: new Date().toISOString(), +}; + +async function fulfillJson(route: Route, body: unknown, status = 200) { + await route.fulfill({ + status, + contentType: "application/json", + body: JSON.stringify(body), + }); +} + +async function fulfillText(route: Route, body: string, status = 200) { + await route.fulfill({ + status, + contentType: "text/markdown; charset=utf-8", + body, + }); +} + +test.describe("Agent Skills page", () => { + test.setTimeout(600_000); + + test("renders SkillsConceptCard with data-testid skills-concept-card-agent", async ({ + page, + }) => { + const skills = makeAgentSkills(); + + await page.route(/\/api\/agent-skills(?:\?.*)?$/, async (route) => { + if (route.request().method() === "GET") { + await fulfillJson(route, { skills, coverage: FULL_COVERAGE }); + } else { + await route.continue(); + } + }); + + await gotoDashboardRoute(page, "/dashboard/agent-skills", { + timeoutMs: NAVIGATION_TIMEOUT_MS, + }); + + const conceptCard = page.locator("[data-testid='skills-concept-card-agent']"); + await expect(conceptCard).toBeVisible({ timeout: 15_000 }); + }); + + test("grid shows 42 skill cards when filter is 'all'", async ({ page }) => { + const skills = makeAgentSkills(); + + await page.route(/\/api\/agent-skills(?:\?.*)?$/, async (route) => { + if (route.request().method() === "GET") { + await fulfillJson(route, { skills, coverage: FULL_COVERAGE }); + } else { + await route.continue(); + } + }); + + await gotoDashboardRoute(page, "/dashboard/agent-skills", { + timeoutMs: NAVIGATION_TIMEOUT_MS, + }); + + // Wait for cards to render + await expect(page.locator("[data-testid^='skill-card-']").first()).toBeVisible({ + timeout: 15_000, + }); + + const cards = page.locator("[data-testid^='skill-card-']"); + await expect(cards).toHaveCount(42, { timeout: 15_000 }); + }); + + test("clicking omni-skill-0 card renders markdown in preview pane", async ({ + page, + }) => { + const skills = makeAgentSkills(); + const mockMarkdown = "# API Skill 0\n\nThis skill manages connections."; + + await page.route(/\/api\/agent-skills(?:\?.*)?$/, async (route) => { + if (route.request().method() === "GET") { + await fulfillJson(route, { skills, coverage: FULL_COVERAGE }); + } else { + await route.continue(); + } + }); + + await page.route(/\/api\/agent-skills\/omni-skill-0\/raw/, async (route) => { + await fulfillText(route, mockMarkdown); + }); + + await gotoDashboardRoute(page, "/dashboard/agent-skills", { + timeoutMs: NAVIGATION_TIMEOUT_MS, + }); + + // Wait for cards to render and click first card + const firstCard = page.locator("[data-testid='skill-card-omni-skill-0']"); + await expect(firstCard).toBeVisible({ timeout: 15_000 }); + await firstCard.click(); + + // Preview pane should show non-empty content + const previewPane = page.locator("[data-testid='skill-preview-pane']"); + await expect(previewPane).toBeVisible({ timeout: 15_000 }); + const previewText = await previewPane.textContent(); + expect(previewText?.trim().length).toBeGreaterThan(0); + }); + + test("cross-link 'Understand the difference' navigates to /dashboard/omni-skills", async ({ + page, + }) => { + const skills = makeAgentSkills(); + + await page.route(/\/api\/agent-skills(?:\?.*)?$/, async (route) => { + if (route.request().method() === "GET") { + await fulfillJson(route, { skills, coverage: FULL_COVERAGE }); + } else { + await route.continue(); + } + }); + + await gotoDashboardRoute(page, "/dashboard/agent-skills", { + timeoutMs: NAVIGATION_TIMEOUT_MS, + }); + + const conceptCard = page.locator("[data-testid='skills-concept-card-agent']"); + await expect(conceptCard).toBeVisible({ timeout: 15_000 }); + + // Find the cross-link inside the concept card + const crossLink = conceptCard.locator("a"); + await expect(crossLink).toBeVisible({ timeout: 5_000 }); + + await crossLink.click(); + await page.waitForURL(/\/dashboard\/omni-skills/, { timeout: 15_000 }); + expect(page.url()).toContain("/dashboard/omni-skills"); + }); + + test("/dashboard/skills redirects to /dashboard/omni-skills", async ({ page }) => { + await page.goto("/dashboard/skills", { waitUntil: "commit", timeout: NAVIGATION_TIMEOUT_MS }); + await page.waitForURL(/\/dashboard\/(omni-skills|login|onboarding)/, { + timeout: 15_000, + }); + // After auth, check that the final destination is omni-skills + const finalUrl = page.url(); + expect( + finalUrl.includes("/dashboard/omni-skills") || + finalUrl.includes("/login") || + finalUrl.includes("/onboarding"), + ).toBe(true); + }); +});