Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
82 changes: 82 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: E2E Tests

on:
pull_request:
paths:
- 'packages/mcp-server/**'
- 'packages/mcp-cloudflare/**'
- 'packages/mcp-cloudflare-e2e/**'
push:
branches: [main]
paths:
- 'packages/mcp-server/**'
- 'packages/mcp-cloudflare/**'
- 'packages/mcp-cloudflare-e2e/**'

permissions:
contents: read

jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
name: Install pnpm
with:
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install --no-frozen-lockfile

- name: Install Playwright Browsers
run: pnpm --filter=@sentry/mcp-cloudflare-e2e install:browsers

- name: Build packages
run: pnpm build

- name: Run E2E tests with coverage
run: pnpm --filter=@sentry/mcp-cloudflare-e2e test:ci

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
directory: packages/mcp-cloudflare-e2e/coverage/
flags: e2e
name: codecov-e2e
fail_ci_if_error: false

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: packages/mcp-cloudflare-e2e/playwright-report/
retention-days: 30

- uses: actions/upload-artifact@v4
if: always()
with:
name: e2e-test-results
path: packages/mcp-cloudflare-e2e/test-results.xml
retention-days: 30
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ coverage

# Sentry Config File
.env.sentry-build-plugin

# Playwright
playwright-report/
test-results/
playwright/.cache/
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
},
"security": {
"noDangerouslySetInnerHtml": "off"
},
"a11y": {
"useSemanticElements": "off"
}
}
},
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"start:client": "pnpm run --filter ./packages/mcp-test-client start",
"start:stdio": "pnpm --stream run --filter ./packages/mcp-server start",
"test": "dotenv -e .env -e .env.local -- turbo test",
"test:ci": "CI=true dotenv -e .env -e .env.local -- pnpm --stream -r run test:ci"
"test:ci": "CI=true dotenv -e .env -e .env.local -- pnpm --stream -r run test:ci",
"test:e2e": "pnpm --filter @sentry/mcp-cloudflare-e2e exec playwright test",
"test:e2e:ui": "pnpm --filter @sentry/mcp-cloudflare-e2e test:ui"
},
"dependencies": {
"@biomejs/biome": "^1.9.4",
Expand Down
57 changes: 57 additions & 0 deletions packages/mcp-cloudflare-e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# @sentry/mcp-cloudflare-e2e

Simple end-to-end tests for the Sentry MCP Cloudflare client.

## Running Tests

```bash
# Run all tests
pnpm test

# Run tests in UI mode
pnpm test:ui

# Run tests in headed mode
pnpm test:headed

# Debug tests
pnpm test:debug
```

## Running from Root

From the project root:

```bash
# Run all e2e tests
pnpm test:e2e

# Open UI mode
pnpm test:e2e:ui
```

## Test Scope

These tests focus on high-level functionality without requiring authentication:

- **Page Loading** - Verifies the application loads without errors
- **Basic Structure** - Checks essential HTML elements exist
- **Responsiveness** - Tests mobile and desktop viewports
- **Meta Tags** - Validates proper HTML meta configuration
- **Title** - Confirms correct page title

## Why Simple Tests?

We intentionally keep these tests simple because:
- OAuth authentication is complex to mock in e2e tests
- Chat functionality requires real Sentry API integration
- Simple tests are more reliable and faster to run
- They catch major regressions without test flakiness

## Configuration

Tests run against:
- Desktop Chrome
- Mobile Safari

The dev server automatically starts on port 5173 before tests run.
22 changes: 22 additions & 0 deletions packages/mcp-cloudflare-e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@sentry/mcp-cloudflare-e2e",
"version": "0.12.0",
"private": true,
"description": "End-to-end tests for Sentry MCP",
"scripts": {
"test": "playwright test",
"test:coverage": "mkdir -p coverage && playwright test && nyc report --reporter=lcov --reporter=json --reporter=text",
"test:ci": "mkdir -p coverage && playwright test --reporter=junit",
"test:headed": "playwright test --headed",
"test:ui": "playwright test --ui",
"test:debug": "playwright test --debug",
"show-report": "playwright show-report",
"install:browsers": "playwright install --with-deps"
},
"devDependencies": {
"@playwright/test": "^1.43.0",
"typescript": "^5.4.5",
"nyc": "^17.1.0",
"@types/node": "^22.10.6"
}
}
51 changes: 51 additions & 0 deletions packages/mcp-cloudflare-e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { defineConfig, devices } from "@playwright/test";

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
/* 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
? [["junit", { outputFile: "test-results.xml" }], ["html"]]
: "html",
/* 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:5173",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
/* Screenshot on failure */
screenshot: "only-on-failure",
},

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

/* Test against mobile viewports. */
{
name: "Mobile Safari",
use: { ...devices["iPhone 12"] },
},
],

/* Run your local dev server before starting the tests */
webServer: {
command: "pnpm --filter @sentry/mcp-cloudflare dev",
url: "http://localhost:5173",
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
});
13 changes: 13 additions & 0 deletions packages/mcp-cloudflare-e2e/tests/coverage.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { test as setup } from "@playwright/test";

setup("collect coverage", async ({ page }) => {
// Enable coverage collection
await page.coverage.startJSCoverage({
resetOnNavigation: false,
});

// Enable CSS coverage as well
await page.coverage.startCSSCoverage({
resetOnNavigation: false,
});
});
95 changes: 95 additions & 0 deletions packages/mcp-cloudflare-e2e/tests/simple.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { test, expect } from "@playwright/test";
import fs from "node:fs";

test.describe("Simple Application Tests", () => {
const jsCoverage: any[] = [];
const cssCoverage: any[] = [];

test.beforeEach(async ({ page }) => {
// Start coverage collection for each test
await page.coverage.startJSCoverage();
await page.coverage.startCSSCoverage();
});

test.afterEach(async ({ page }) => {
// Collect coverage after each test
const jsResults = await page.coverage.stopJSCoverage();
const cssResults = await page.coverage.stopCSSCoverage();

jsCoverage.push(...jsResults);
cssCoverage.push(...cssResults);
});

test.afterAll(async () => {
// Write coverage data to files
if (jsCoverage.length > 0) {
fs.mkdirSync("coverage", { recursive: true });
fs.writeFileSync(
"coverage/js-coverage.json",
JSON.stringify(jsCoverage, null, 2),
);
}
if (cssCoverage.length > 0) {
fs.mkdirSync("coverage", { recursive: true });
fs.writeFileSync(
"coverage/css-coverage.json",
JSON.stringify(cssCoverage, null, 2),
);
}
});
test("should load without errors", async ({ page }) => {
await page.goto("/");

// Just check that the page loads without throwing errors
await page.waitForLoadState("networkidle");

// Verify we're on the right page by checking URL
expect(page.url()).toContain("/");
});

test("should have correct title", async ({ page }) => {
await page.goto("/");

// Check page title contains expected text
await expect(page).toHaveTitle(/Sentry MCP/);
});

test("should have basic HTML structure", async ({ page }) => {
await page.goto("/");

// Check basic HTML elements exist (using first() to avoid duplicates)
await expect(page.locator("html")).toBeVisible();
await expect(page.locator("body")).toBeVisible();
await expect(page.locator("#root")).toBeVisible();
});

test("should be responsive", async ({ page }) => {
// Test mobile size
await page.setViewportSize({ width: 375, height: 667 });
await page.goto("/");
await page.waitForLoadState("networkidle");

// Just verify page loads on mobile
await expect(page.locator("body")).toBeVisible();

// Test desktop size
await page.setViewportSize({ width: 1280, height: 720 });
await page.reload();
await page.waitForLoadState("networkidle");

// Just verify page loads on desktop
await expect(page.locator("body")).toBeVisible();
});

test("should have proper meta tags", async ({ page }) => {
await page.goto("/");

// Check viewport meta tag exists
const viewport = page.locator('meta[name="viewport"]');
await expect(viewport).toHaveCount(1);

// Check charset meta tag exists
const charset = page.locator("meta[charset]");
await expect(charset).toHaveCount(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export function SlidingPanel({
className={`fixed inset-y-0 right-0 w-full max-w-2xl bg-slate-950 border-l border-slate-800 z-50 shadow-2xl flex flex-col transition-transform duration-500 ease-in-out ${
isOpen ? "translate-x-0" : "translate-x-full"
} ${className}`}
role="dialog"
aria-modal="true"
aria-hidden={!isOpen}
>
{children}
</div>
Expand All @@ -61,6 +64,9 @@ export function SlidingPanel({
className={`${
isOpen ? "hidden md:flex" : "hidden"
} fixed top-0 right-0 h-screen w-1/2 bg-slate-950 flex-col border-l border-slate-800 transition-opacity duration-300 ${className}`}
role="dialog"
aria-modal="true"
aria-hidden={!isOpen}
>
{children}
</div>
Expand Down
Loading
Loading