Skip to content

Commit 8e4a76a

Browse files
mfrachetclaude
andauthored
test(playground): add comprehensive E2E tests for all routes (#12353)
This adds E2E test coverage for all playground routes that were previously untested: - `/` - Root redirect to agents - `/scorers` and `/scorers/:scorerId` - Scorers list and detail pages - `/processors` and `/processors/:processorId` - Processors list and detail pages - `/mcps/:serverId` - MCP server detail page - `/observability` - Observability page - `/request-context` - Request context page - `/settings` - Settings page - `/templates` and `/templates/:templateSlug` - Templates list and detail pages Also added `responseQualityScorer`, `responseTimeScorer`, `loggingProcessor`, and `contentFilterProcessor` to the kitchen-sink test setup to enable testing these routes. All 52 tests pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added processors for logging and content filtering in the Mastra platform. * Introduced scorers for evaluating response quality and measuring response latency performance. * **Tests** * Added comprehensive end-to-end test coverage for processors, scorers, observability, request context, templates, settings, and MCP server pages. * Added UI accessibility snapshots for processors and scorers pages. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent eac936e commit 8e4a76a

File tree

16 files changed

+374
-0
lines changed

16 files changed

+374
-0
lines changed

packages/playground/e2e/kitchen-sink/src/mastra/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { weatherAgent } from './agents';
66
import { complexWorkflow, lessComplexWorkflow } from './workflows/complex-workflow';
77
import { simpleMcpServer } from './mcps';
88
import { registerApiRoute } from '@mastra/core/server';
9+
import { responseQualityScorer, responseTimeScorer } from './scorers';
10+
import { loggingProcessor, contentFilterProcessor } from './processors';
911

1012
export const mastra = new Mastra({
1113
workflows: { complexWorkflow, lessComplexWorkflow },
@@ -18,6 +20,14 @@ export const mastra = new Mastra({
1820
mcpServers: {
1921
simpleMcpServer,
2022
},
23+
scorers: {
24+
responseQualityScorer,
25+
responseTimeScorer,
26+
},
27+
processors: {
28+
loggingProcessor,
29+
contentFilterProcessor,
30+
},
2131
server: {
2232
apiRoutes: [
2333
registerApiRoute('/e2e/reset-storage', {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Processor } from '@mastra/core/processors';
2+
3+
export const loggingProcessor: Processor<'logging-processor'> = {
4+
id: 'logging-processor',
5+
name: 'Logging Processor',
6+
description: 'Logs all input messages for debugging',
7+
processInput: async args => args.messages,
8+
};
9+
10+
export const contentFilterProcessor: Processor<'content-filter'> = {
11+
id: 'content-filter',
12+
name: 'Content Filter Processor',
13+
description: 'Filters content based on rules',
14+
processInput: async args => args.messages,
15+
processOutputResult: async args => args.messages,
16+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createScorer } from '@mastra/core/evals';
2+
3+
export const responseQualityScorer = createScorer({
4+
id: 'response-quality',
5+
name: 'Response Quality Scorer',
6+
description: 'Evaluates the quality of agent responses',
7+
}).generateScore(async () => 0.85);
8+
9+
export const responseTimeScorer = createScorer({
10+
id: 'response-time',
11+
name: 'Response Time Scorer',
12+
description: 'Measures response latency performance',
13+
}).generateScore(async () => 0.92);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from '../../__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('has breadcrumb navigation', async ({ page }) => {
9+
await page.goto('/mcps/simple-mcp-server');
10+
11+
await expect(page).toHaveTitle(/Mastra Studio/);
12+
13+
const breadcrumb = page.locator('nav a:has-text("MCP Servers")').first();
14+
await expect(breadcrumb).toHaveAttribute('href', '/mcps');
15+
});
16+
17+
test('has documentation link', async ({ page }) => {
18+
await page.goto('/mcps/simple-mcp-server');
19+
20+
await expect(page.locator('text=MCP documentation')).toHaveAttribute(
21+
'href',
22+
'https://mastra.ai/en/docs/tools-mcp/mcp-overview',
23+
);
24+
});
25+
26+
test('has server combobox for navigation', async ({ page }) => {
27+
await page.goto('/mcps/simple-mcp-server');
28+
29+
// The MCP server combobox should be visible
30+
const combobox = page.locator('[role="combobox"]');
31+
await expect(combobox).toBeVisible();
32+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from '../__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('has overall information', async ({ page }) => {
9+
await page.goto('/observability');
10+
11+
await expect(page).toHaveTitle(/Mastra Studio/);
12+
await expect(page.locator('h1').first()).toHaveText('Observability');
13+
await expect(page.getByRole('link', { name: 'Observability documentation' })).toHaveAttribute(
14+
'href',
15+
'https://mastra.ai/en/docs/observability/tracing/overview',
16+
);
17+
});
18+
19+
test('has page header with description', async ({ page }) => {
20+
await page.goto('/observability');
21+
22+
// The page header with description should be visible
23+
await expect(page.locator('text=Explore observability traces for your entities')).toBeVisible();
24+
});
25+
26+
test('has entity filter dropdown', async ({ page }) => {
27+
await page.goto('/observability');
28+
29+
// The entity filter should be present with default "All" selection
30+
const entityFilter = page.locator('button:has-text("All")');
31+
await expect(entityFilter).toBeVisible();
32+
});
33+
34+
test('renders empty state or traces list', async ({ page }) => {
35+
await page.goto('/observability');
36+
37+
// Either shows empty state or traces list (depending on data)
38+
// We check that the page has loaded and the traces tools are visible
39+
await expect(page.locator('text=Reset')).toBeVisible();
40+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from '../../__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('has breadcrumb navigation', async ({ page }) => {
9+
await page.goto('/processors/logging-processor');
10+
11+
await expect(page).toHaveTitle(/Mastra Studio/);
12+
13+
const breadcrumb = page.locator('nav a:has-text("Processors")').first();
14+
await expect(breadcrumb).toHaveAttribute('href', '/processors');
15+
});
16+
17+
test('displays processor ID in header', async ({ page }) => {
18+
await page.goto('/processors/logging-processor');
19+
20+
// The processor ID should be displayed in the header group
21+
await expect(page.locator('header').getByText('logging-processor')).toBeVisible();
22+
});
23+
24+
test('has documentation link', async ({ page }) => {
25+
await page.goto('/processors/logging-processor');
26+
27+
await expect(page.locator('text=Processors documentation')).toHaveAttribute(
28+
'href',
29+
'https://mastra.ai/docs/agents/processors',
30+
);
31+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from '../__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('has overall information', async ({ page }) => {
9+
await page.goto('/processors');
10+
11+
await expect(page).toHaveTitle(/Mastra Studio/);
12+
await expect(page.locator('h1')).toHaveText('Processors');
13+
await expect(page.getByRole('link', { name: 'Processors documentation' })).toHaveAttribute(
14+
'href',
15+
'https://mastra.ai/docs/agents/processors',
16+
);
17+
});
18+
19+
test('clicking on the processor row redirects to detail page', async ({ page }) => {
20+
await page.goto('/processors');
21+
22+
const el = page.locator('tr:has-text("Logging Processor")');
23+
await el.click();
24+
25+
await expect(page).toHaveURL(/\/processors\/logging-processor$/);
26+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- table:
2+
- rowgroup:
3+
- row "Name Phases Used by":
4+
- columnheader "Name"
5+
- columnheader "Phases"
6+
- columnheader "Used by"
7+
- rowgroup:
8+
- row "Logging Processor Logs all input messages for debugging Input 0 agents":
9+
- cell "Logging Processor Logs all input messages for debugging":
10+
- link "Logging Processor":
11+
- /url: /processors/logging-processor
12+
- text: ''
13+
- cell "Input"
14+
- cell "0 agents":
15+
- img
16+
- text: ''
17+
- row "Content Filter Processor Filters content based on rules Input Output Result 0 agents":
18+
- cell "Content Filter Processor Filters content based on rules":
19+
- link "Content Filter Processor":
20+
- /url: /processors/content-filter
21+
- text: ''
22+
- cell "Input Output Result"
23+
- cell "0 agents":
24+
- img
25+
- text: ''
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from '../__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('has page title', async ({ page }) => {
9+
await page.goto('/request-context');
10+
11+
await expect(page).toHaveTitle(/Mastra Studio/);
12+
await expect(page.locator('h1')).toHaveText('Request Context');
13+
});
14+
15+
test('renders RequestContext component', async ({ page }) => {
16+
await page.goto('/request-context');
17+
18+
// The RequestContext component should be rendered within the page
19+
// Check for the main content area
20+
const mainContent = page.locator('main');
21+
await expect(mainContent).toBeVisible();
22+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test, expect } from '@playwright/test';
2+
import { resetStorage } from './__utils__/reset-storage';
3+
4+
test.afterEach(async () => {
5+
await resetStorage();
6+
});
7+
8+
test('root path redirects to agents', async ({ page }) => {
9+
await page.goto('/');
10+
await expect(page).toHaveURL(/\/agents$/);
11+
});

0 commit comments

Comments
 (0)