|
| 1 | +import { test, expect } from "@playwright/test"; |
| 2 | + |
| 3 | +// Server configurations |
| 4 | +const SERVERS = [ |
| 5 | + { key: "basic-react", index: 0, name: "Basic MCP App Server (React-based)" }, |
| 6 | + { |
| 7 | + key: "basic-vanillajs", |
| 8 | + index: 1, |
| 9 | + name: "Basic MCP App Server (Vanilla JS)", |
| 10 | + }, |
| 11 | + { key: "budget-allocator", index: 2, name: "Budget Allocator Server" }, |
| 12 | + { key: "cohort-heatmap", index: 3, name: "Cohort Heatmap Server" }, |
| 13 | + { |
| 14 | + key: "customer-segmentation", |
| 15 | + index: 4, |
| 16 | + name: "Customer Segmentation Server", |
| 17 | + }, |
| 18 | + { key: "scenario-modeler", index: 5, name: "SaaS Scenario Modeler" }, |
| 19 | + { key: "system-monitor", index: 6, name: "System Monitor Server" }, |
| 20 | + { key: "threejs", index: 7, name: "Three.js Server" }, |
| 21 | +]; |
| 22 | + |
| 23 | +// Increase timeout for iframe-heavy tests |
| 24 | +test.setTimeout(90000); |
| 25 | + |
| 26 | +test.describe("Host UI", () => { |
| 27 | + test("initial state shows controls", async ({ page }) => { |
| 28 | + await page.goto("/"); |
| 29 | + await expect(page.locator("label:has-text('Server')")).toBeVisible(); |
| 30 | + await expect(page.locator("label:has-text('Tool')")).toBeVisible(); |
| 31 | + await expect(page.locator('button:has-text("Call Tool")')).toBeVisible(); |
| 32 | + }); |
| 33 | + |
| 34 | + test("screenshot of initial state", async ({ page }) => { |
| 35 | + await page.goto("/"); |
| 36 | + await page.waitForTimeout(1000); |
| 37 | + await expect(page).toHaveScreenshot("host-initial.png"); |
| 38 | + }); |
| 39 | +}); |
| 40 | + |
| 41 | +// Generate tests for each server |
| 42 | +for (const server of SERVERS) { |
| 43 | + test.describe(`${server.name}`, () => { |
| 44 | + test(`loads app UI`, async ({ page }) => { |
| 45 | + await page.goto("/"); |
| 46 | + |
| 47 | + // Select server |
| 48 | + const serverSelect = page.locator("select").first(); |
| 49 | + await serverSelect.selectOption({ index: server.index }); |
| 50 | + |
| 51 | + // Click Call Tool |
| 52 | + await page.click('button:has-text("Call Tool")'); |
| 53 | + |
| 54 | + // Wait for outer iframe |
| 55 | + await page.waitForSelector("iframe", { timeout: 10000 }); |
| 56 | + |
| 57 | + // Wait for content to load (generous timeout for nested iframes) |
| 58 | + await page.waitForTimeout(5000); |
| 59 | + |
| 60 | + // Verify iframe structure exists |
| 61 | + const outerFrame = page.frameLocator("iframe").first(); |
| 62 | + await expect(outerFrame.locator("iframe")).toBeVisible({ |
| 63 | + timeout: 10000, |
| 64 | + }); |
| 65 | + }); |
| 66 | + |
| 67 | + test(`screenshot matches golden`, async ({ page }) => { |
| 68 | + await page.goto("/"); |
| 69 | + |
| 70 | + // Select server |
| 71 | + const serverSelect = page.locator("select").first(); |
| 72 | + await serverSelect.selectOption({ index: server.index }); |
| 73 | + |
| 74 | + // Click Call Tool |
| 75 | + await page.click('button:has-text("Call Tool")'); |
| 76 | + |
| 77 | + // Wait for app to fully load |
| 78 | + await page.waitForSelector("iframe", { timeout: 10000 }); |
| 79 | + await page.waitForTimeout(6000); // Extra time for nested iframe content |
| 80 | + |
| 81 | + // Take screenshot |
| 82 | + await expect(page).toHaveScreenshot(`${server.key}.png`, { |
| 83 | + maxDiffPixelRatio: 0.1, // 10% tolerance for dynamic content |
| 84 | + timeout: 10000, |
| 85 | + }); |
| 86 | + }); |
| 87 | + }); |
| 88 | +} |
0 commit comments