Skip to content
Development
Skill

/playground-msw-tests

REQUIRED and PRIMARY testing approach for packages/playground and packages/playground-ui. Triggers on: adding or modifying hooks, pages, route components, data-fetching code, React Query interactions, or any test work in these packages. Generates Vitest tests that drive the real

From plugin
mastra
27k30 skills14 commands
Install
$ npx -y skills add mastra-ai/mastra --skill playground-msw-tests --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/playground-msw-tests

Context preview

The summary Claude sees to decide when to auto-load this skill.

REQUIRED and PRIMARY testing approach for packages/playground and packages/playground-ui. Triggers on: adding or modifying hooks, pages, route components, data-fetching code, React Query interactions, or any test work in these packages. Generates Vitest tests that drive the real

SKILL.md

playground-msw-tests.SKILL.md
name: playground-msw-tests
description: >
  REQUIRED and PRIMARY testing approach for packages/playground and packages/playground-ui.
  Triggers on: adding or modifying hooks, pages, route components, data-fetching code,
  React Query interactions, or any test work in these packages. Generates Vitest tests
  that drive the real @mastra/client-js + React Query stack through MSW handlers and
  typed fixtures derived from @mastra/client-js response types. This is the #1 way to
  test the playground packages — ABOVE Playwright E2E. Use Playwright only for
  cross-page user journeys that MSW cannot model.

MSW + client-js Fixtures: Primary Testing Strategy

Core Principle

**Drive the real transport, mock the network.**

Tests in `packages/playground` and `packages/playground-ui` MUST be written as Vitest tests that exercise the real `@mastra/client-js` SDK, the real React Query cache, and the real component/hook code paths. The only seam we mock is the network boundary, via [MSW](https://mswjs.io/).

This catches contract drift between the playground and `@mastra/client-js` at typecheck time and at test time — something `vi.mock('@/hooks/...')` style tests cannot do.

Priority Order

When you write or refactor a test for these packages, choose in this order:

1. **MSW + typed client-js fixtures (THIS SKILL)** — for hooks, pages, routes, data-fetching, gating, redirect logic, query/mutation flows, error paths. 2. **Playwright E2E** (`e2e-tests-studio` skill) — only for genuine cross-page user journeys, real browser concerns (focus/keyboard/viewport), or anything that requires a real running Mastra server. 3. **Pure unit tests** — only for self-contained utilities/services with no network, no React Query, no router involvement.

If the same behavior can be covered by both #1 and #2, **prefer #1**. MSW tests are faster, deterministic, run in CI without browsers, and assert the real wire contract.

What NOT to do

  • ❌ `vi.mock('@/domains/.../hooks/use-agents')` — mocking our own hooks hides

cache, gating and transport bugs.

  • ❌ Implementation-mirror tests — do not duplicate source class strings,

branch logic, generated shapes, or calculations without asserting a real behavior or regression.

  • ❌ Class-name-only visual tests — `expect(node.className).toContain(...)`

usually just tests that the implementation string exists. Prefer no test over a className duplication test; use computed style, user-visible behavior, or a browser/Storybook check unless the class string itself is the public API.

  • ❌ Inline TypeScript types in tests (`type AgentLite = { id: string }`) —

these drift silently from the real SDK.

  • ❌ `as any` / `as unknown as ListAgentsResponse` on fixture data, MSW

responses, request payloads, hook inputs, or component event inputs.

  • ❌ Returning bespoke shapes from MSW handlers that don't match the real

`@mastra/client-js` response. If a field is optional, include it as optional in the fixture, don't omit the type.

What TO do

  • ✅ Put fixtures in a `__tests__/fixtures/` folder next to the test file.
  • ✅ Type every fixture with a response type re-exported from `@mastra/client-js`

(e.g. `ListStoredAgentsResponse`, `GetAgentResponse`, `BuilderSettingsResponse`, `GetToolResponse`, `GetWorkflowResponse`, `ListStoredSkillsResponse`).

  • ✅ Use direct imports and inferred types from real SDK, hook, component, DOM,

or Testing Library APIs for MSW payloads, request payloads, hook inputs, and component events.

  • ✅ Register MSW handlers per test with `server.use(...)` so handlers reset

between tests via the global `afterEach`.

  • ✅ Render through `MastraReactProvider` + `QueryClientProvider` + `MemoryRouter`

so the real client SDK is the transport.

  • ✅ Use `vi.fn()` wrappers inside MSW handlers to assert which endpoints were

hit (great for testing `enabled: ...` gating without mocking hooks).

Standard Test Skeleton

// @vitest-environment jsdom
import { MastraReactProvider } from '@mastra/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { cleanup, render, screen } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { MemoryRouter } from 'react-router';
import { afterEach, describe, expect, it } from 'vitest';

import { server } from '@/test/msw-server';
import { Subject } from '../subject';
import { happyPathResponse } from './fixtures/subject';

const BASE_URL = 'http://localhost:4111';

const renderSubject = () => {
  const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return render(
    <MastraReactProvider baseUrl={BASE_URL}>
      <QueryClientProvider client={queryClient}>
        <MemoryRouter>
          <Subject />
        </MemoryRouter>
      </QueryClientProvider>
    </MastraReactProvider>,
  );
};

afterEach(() => cleanup());

describe('Subject', () => {
  it('renders the happy path', async () => {
    server.use(http.get(`${BASE_URL}/api/agents`, () => HttpResponse.json(happyPathResponse)));

    renderSubject();

    expect(await screen.findByText('Expected behavior')).not.toBeNull();
  });
});

Standard Fixture File

// packages/playground/src/.../__tests__/fixtures/subject.ts
import type { ListStoredAgentsResponse } from '@mastra/client-js';

export const emptyStoredAgents: ListStoredAgentsResponse = {
  agents: [],
  total: 0,
  page: 1,
  perPage: 50,
  hasMore: false,
};

export const oneDraftAgent: ListStoredAgentsResponse = {
  ...emptyStoredAgents,
  agents: [
    {
      id: 'agent-1',
      name: 'Draft Agent',
      instructions: '',
      model: { provider: 'openai', name: 'gpt-4o-mini' },
      status: 'draft',
      // ...other required fields from StoredAgentResponse
    },
  ],
  total: 1,
};

**If a required field on the SDK response type is missing from your fixture, that's a real test failure — fix the fixture, never `as any` it.** The same applies to hook inputs, compo

Read more
Ships withmastra

Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack. It includes everything you need to go from early prototypes to production-ready applications.

Get the whole plugin

Other skills on mastra.