Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
226ad25
Add a Playwright e2e test
msabramo May 28, 2025
69da70a
npm run prettier-fix
msabramo May 28, 2025
8a13526
Exclude e2e from Jest
msabramo May 28, 2025
8babc44
Make `npm run test:e2e` work
msabramo May 28, 2025
3a28efe
Mention `npm run test:e2e` in CONTRIBUTING.md
msabramo May 28, 2025
89e1efc
Add more Playwright tests
msabramo Jun 4, 2025
c803d93
Move test:e2e up with other two test scripts
msabramo Jun 4, 2025
4f6a4ce
Add .github/workflows/e2e_tests.yml
msabramo Jun 4, 2025
66ad85a
npm run prettier-fix
msabramo Jun 7, 2025
1df83d7
Do npm run start in background
msabramo Jun 7, 2025
826bc35
Upload Playwright Report and Screenshots
msabramo Jun 7, 2025
a421f32
git add playwright.config.ts
msabramo Jun 7, 2025
f06a6e1
npm run prettier-fix
msabramo Jun 7, 2025
a2a58c2
Install Playwright dependencies
msabramo Jun 7, 2025
705d7b4
e2e_tests.yml: playwright-report and test-result in client dir
msabramo Jun 7, 2025
cdb9180
Generate report with https://github.com/daun/playwright-report-summary
msabramo Jun 7, 2025
2c0af88
Take screenshots on failure
msabramo Jun 7, 2025
6e5ccf6
Remove commented out stuff in playwright.config.ts
msabramo Jun 17, 2025
8b1faf1
playwright.config.ts: Start dev server automatically
msabramo Jun 18, 2025
4ef6bdb
e2e_tests.yml: Remove start dev server
msabramo Jun 18, 2025
0f1cc19
prettier fix playwright.config.ts
msabramo Jun 18, 2025
0b31ce7
.gitignore: Add playwright artifacts
msabramo Jun 18, 2025
b783b50
Make "npm run clean" remove Playwright files
msabramo Jun 18, 2025
db9ca37
No playwright-report or results.json for non-CI
msabramo Jun 19, 2025
2ca63cb
Merge branch 'main' into playwright-test
msabramo Jun 19, 2025
08e4f6b
outputDir: "./e2e/test-results"
msabramo Jun 19, 2025
8b3613e
prettier fix
msabramo Jun 19, 2025
1807542
Cleanup client/e2e/test-results/ after test:e2e
msabramo Jun 19, 2025
b481ed0
Set `MCP_AUTO_OPEN_ENABLED=false` for `test:e2e` script
msabramo Jun 19, 2025
d5b4a6e
Update .github/workflows/e2e_tests.yml
msabramo Jun 20, 2025
eb89e01
Change retention-days from 30 to 2
msabramo Jun 20, 2025
1ff39f1
Replace pull_request with pull_request_target
msabramo Jun 20, 2025
4b14b96
Both pull_request and pull_request_target
msabramo Jun 20, 2025
9ffe63e
Revert "Both pull_request and pull_request_target"
msabramo Jun 20, 2025
3e28357
Revert "Replace pull_request with pull_request_target"
msabramo Jun 20, 2025
465ee9a
Revert "Update .github/workflows/e2e_tests.yml"
msabramo Jun 20, 2025
c747fe8
Only try to comment on PR if the PR is from the canonical repo and no…
msabramo Jun 21, 2025
94a35d0
Only try to comment on PR if the PR is from the canonical repo and no…
msabramo Jun 21, 2025
2cd7b74
Revert
msabramo Jun 21, 2025
ebc249b
Always gen test summary but only comment on same repo PRs
msabramo Jun 21, 2025
86eaabe
Merge branch 'main' into playwright-test
cliffhall Jun 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .github/workflows/e2e_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Playwright Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
timeout-minutes: 5
runs-on: ubuntu-latest

steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwoff1

- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 18

# Cache Playwright browsers
- name: Cache Playwright browsers
id: cache-playwright
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright # The default Playwright cache path
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json
restore-keys: |
${{ runner.os }}-playwright-

- name: Install dependencies
run: npm ci

- name: Install Playwright dependencies
run: npx playwright install-deps

- name: Install Playwright and browsers unless cached
run: npx playwright install --with-deps
if: steps.cache-playwright.outputs.cache-hit != 'true'

- name: Run Playwright tests
run: npm run test:e2e

- name: Upload Playwright Report and Screenshots
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: |
client/playwright-report/
client/test-results/
client/results.json
retention-days: 2

- name: Publish Playwright Test Summary
uses: daun/playwright-report-summary@v3
if: always()
with:
create-comment: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
report-file: client/results.json
comment-title: "🎭 Playwright E2E Test Results"
job-summary: true
icon-style: "emojis"
custom-info: |
**Test Environment:** Ubuntu Latest, Node.js 18
**Browsers:** Chromium, Firefox

📊 [View Detailed HTML Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) (download artifacts)
test-command: "npm run test:e2e"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ client/tsconfig.app.tsbuildinfo
client/tsconfig.node.tsbuildinfo
cli/build
test-output
client/playwright-report/
client/results.json
client/test-results/

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Thanks for your interest in contributing! This guide explains how to get involve

1. Create a new branch for your changes
2. Make your changes following existing code style and conventions. You can run `npm run prettier-check` and `npm run prettier-fix` as applicable.
3. Test changes locally by running `npm test`
3. Test changes locally by running `npm test` and `npm run test:e2e`
4. Update documentation as needed
5. Use clear commit messages explaining your changes
6. Verify all changes work as expected
Expand Down
18 changes: 18 additions & 0 deletions client/e2e/global-teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { rimraf } from "rimraf";

async function globalTeardown() {
if (!process.env.CI) {
console.log("Cleaning up test-results directory...");
// Add a small delay to ensure all Playwright files are written
await new Promise((resolve) => setTimeout(resolve, 100));
await rimraf("./e2e/test-results");
console.log("Test-results directory cleaned up.");
}
}

export default globalTeardown;

// Call the function when this script is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
globalTeardown().catch(console.error);
}
113 changes: 113 additions & 0 deletions client/e2e/transport-type-dropdown.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { test, expect } from "@playwright/test";

// Adjust the URL if your dev server runs on a different port
const APP_URL = "http://localhost:6274/";

test.describe("Transport Type Dropdown", () => {
test("should have options for STDIO, SSE, and Streamable HTTP", async ({
page,
}) => {
await page.goto(APP_URL);

// Wait for the Transport Type dropdown to be visible
const selectTrigger = page.getByLabel("Transport Type");
await expect(selectTrigger).toBeVisible();

// Open the dropdown
await selectTrigger.click();

// Check for the three options
await expect(page.getByRole("option", { name: "STDIO" })).toBeVisible();
await expect(page.getByRole("option", { name: "SSE" })).toBeVisible();
await expect(
page.getByRole("option", { name: "Streamable HTTP" }),
).toBeVisible();
});

test("should show Command and Arguments fields and hide URL field when Transport Type is STDIO", async ({
page,
}) => {
await page.goto(APP_URL);

// Wait for the Transport Type dropdown to be visible
const selectTrigger = page.getByLabel("Transport Type");
await expect(selectTrigger).toBeVisible();

// Open the dropdown and select STDIO
await selectTrigger.click();
await page.getByRole("option", { name: "STDIO" }).click();

// Wait for the form to update
await page.waitForTimeout(100);

// Check that Command and Arguments fields are visible
await expect(page.locator("#command-input")).toBeVisible();
await expect(page.locator("#arguments-input")).toBeVisible();

// Check that URL field is not visible
await expect(page.locator("#sse-url-input")).not.toBeVisible();

// Also verify the labels are present
await expect(page.getByText("Command")).toBeVisible();
await expect(page.getByText("Arguments")).toBeVisible();
await expect(page.getByText("URL")).not.toBeVisible();
});

test("should show URL field and hide Command and Arguments fields when Transport Type is SSE", async ({
page,
}) => {
await page.goto(APP_URL);

// Wait for the Transport Type dropdown to be visible
const selectTrigger = page.getByLabel("Transport Type");
await expect(selectTrigger).toBeVisible();

// Open the dropdown and select SSE
await selectTrigger.click();
await page.getByRole("option", { name: "SSE" }).click();

// Wait for the form to update
await page.waitForTimeout(100);

// Check that URL field is visible
await expect(page.locator("#sse-url-input")).toBeVisible();

// Check that Command and Arguments fields are not visible
await expect(page.locator("#command-input")).not.toBeVisible();
await expect(page.locator("#arguments-input")).not.toBeVisible();

// Also verify the labels are present/absent
await expect(page.getByText("URL")).toBeVisible();
await expect(page.getByText("Command")).not.toBeVisible();
await expect(page.getByText("Arguments")).not.toBeVisible();
});

test("should show URL field and hide Command and Arguments fields when Transport Type is Streamable HTTP", async ({
page,
}) => {
await page.goto(APP_URL);

// Wait for the Transport Type dropdown to be visible
const selectTrigger = page.getByLabel("Transport Type");
await expect(selectTrigger).toBeVisible();

// Open the dropdown and select Streamable HTTP
await selectTrigger.click();
await page.getByRole("option", { name: "Streamable HTTP" }).click();

// Wait for the form to update
await page.waitForTimeout(100);

// Check that URL field is visible
await expect(page.locator("#sse-url-input")).toBeVisible();

// Check that Command and Arguments fields are not visible
await expect(page.locator("#command-input")).not.toBeVisible();
await expect(page.locator("#arguments-input")).not.toBeVisible();

// Also verify the labels are present/absent
await expect(page.getByText("URL")).toBeVisible();
await expect(page.getByText("Command")).not.toBeVisible();
await expect(page.getByText("Arguments")).not.toBeVisible();
});
});
2 changes: 2 additions & 0 deletions client/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ module.exports = {
"/node_modules/",
"/dist/",
"/bin/",
"/e2e/",
"\\.config\\.(js|ts|cjs|mjs)$",
],
// Exclude the same patterns from coverage reports
coveragePathIgnorePatterns: [
"/node_modules/",
"/dist/",
"/bin/",
"/e2e/",
"\\.config\\.(js|ts|cjs|mjs)$",
],
};
4 changes: 3 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"lint": "eslint .",
"preview": "vite preview --port 6274",
"test": "jest --config jest.config.cjs",
"test:watch": "jest --config jest.config.cjs --watch"
"test:watch": "jest --config jest.config.cjs --watch",
"test:e2e": "playwright test e2e && npm run cleanup:e2e",
"cleanup:e2e": "node e2e/global-teardown.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.13.0",
Expand Down
70 changes: 70 additions & 0 deletions client/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { defineConfig, devices } from "@playwright/test";

/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
/* Run your local dev server before starting the tests */
webServer: {
cwd: "..",
command: "npm run dev",
url: "http://localhost:6274",
reuseExistingServer: !process.env.CI,
},

testDir: "./e2e",
outputDir: "./e2e/test-results",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI
? [
["html", { outputFolder: "playwright-report" }],
["json", { outputFile: "results.json" }],
["line"],
]
: [["line"]],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:6274",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",

/* Take screenshots on failure */
screenshot: "only-on-failure",

/* Record video on failure */
video: "retain-on-failure",
},

/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},

{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},

// Skip WebKit on macOS due to compatibility issues
...(process.platform !== "darwin"
? [
{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
]
: []),
],
});
Loading