Agent skill

Testing Frontend

by MadAppGang in MadAppGang/claude-code

A skill your agent uses when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

MITAuto-check passedTesting & QA

Install Testing Frontend

skills CLI
$ npx skills add MadAppGang/claude-code --skill testing-frontend -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install MadAppGang/claude-code testing-frontend --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/MadAppGang/claude-code.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/dev/skills/frontend/testing-frontend .claude/skills/testing-frontend && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
testing-frontend
GitHub stars
285
Token cost
~2.9k tokens
SKILL.md length
125 words
Files
1
Skills in repo
69
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

  • Works in 7 steps: getByRole - Accessible to everyone → getByLabelText - Form elements → getByPlaceholderText - Inputs → …
  • Writing component tests
  • SKILL.md covers Overview, Testing Philosophy, Component Testing (React) and Component Testing (Vue), plus 6 more sections
  • Reaches test-api.com

What it does

Testing Frontend is an agent skill from MadAppGang/claude-code. Use when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

Its SKILL.md is about 2.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Testing & QA, covering Unit testing. It works with Vue.js, Testing Library and Vitest. The repository describes itself as: claude code plugins marketplace. The licence is MIT.

When your agent uses it

  • Writing component tests
  • Testing user interactions
  • Setting up Vitest/React Testing Library/Vue Test Utils for frontend applications

Example prompts

  • “/testing-frontend”

Workflow steps

7 steps, taken from the first numbered list in SKILL.md.

  1. getByRole - Accessible to everyone
  2. getByLabelText - Form elements
  3. getByPlaceholderText - Inputs
  4. getByText - Non-interactive elements
  5. getByDisplayValue - Form current values
  6. getByAltText - Images
  7. getByTestId - Last resort

What it can do on your machine

Read from SKILL.md and the folder at commit 6097ad4. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • test-api.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Testing Frontend loads about 2.9k tokens when it runs. Until then it costs about 44 tokens; SKILL.md has 125 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~44
When it runs · the whole SKILL.md, loaded when a task matches
~2.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from MadAppGang/claude-code at commit 6097ad4, republished under its MIT licence (© MadAppGang). 125 words, ~2,880 tokens.

Download SKILL.mdSave it as .claude/skills/testing-frontend/SKILL.md (or your agent's skills folder).
name
testing-frontend
description
Use when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.
version
1.0.0
keywords
frontend testing, Vitest, React Testing Library, Vue Test Utils, component testing, user event testing, mocking, accessibility testing
plugin
dev
updated
2026-01-20

Frontend Testing Patterns

Overview

Testing patterns for frontend applications using Vitest and React Testing Library / Vue Test Utils.

Testing Philosophy

User-Centric Testing

Test behavior, not implementation. Query elements the way users would find them.

tsx
// BAD: Testing implementation
expect(wrapper.state('isOpen')).toBe(true);
expect(wrapper.find('.modal-class').exists()).toBe(true);

// GOOD: Testing behavior
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByText('Modal Title')).toBeVisible();
Query Priority

Use queries in this order (most to least preferred):

  1. getByRole - Accessible to everyone
  2. getByLabelText - Form elements
  3. getByPlaceholderText - Inputs
  4. getByText - Non-interactive elements
  5. getByDisplayValue - Form current values
  6. getByAltText - Images
  7. getByTestId - Last resort

Component Testing (React)

Basic Component Test
tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserCard } from './UserCard';

describe('UserCard', () => {
  const user = { id: '1', name: 'John Doe', email: 'john@example.com' };

  it('renders user information', () => {
    render(<UserCard user={user} />);

    expect(screen.getByText('John Doe')).toBeInTheDocument();
    expect(screen.getByText('john@example.com')).toBeInTheDocument();
  });

  it('calls onSelect when clicked', async () => {
    const onSelect = vi.fn();
    const userEvt = userEvent.setup();

    render(<UserCard user={user} onSelect={onSelect} />);

    await userEvt.click(screen.getByRole('button'));

    expect(onSelect).toHaveBeenCalledWith(user);
  });
});
Testing Async Components
tsx
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { UserList } from './UserList';

// Mock API
vi.mock('@/api', () => ({
  getUsers: vi.fn(),
}));

describe('UserList', () => {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  const wrapper = ({ children }) => (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );

  beforeEach(() => {
    queryClient.clear();
  });

  it('shows loading state', () => {
    api.getUsers.mockImplementation(() => new Promise(() => {}));

    render(<UserList />, { wrapper });

    expect(screen.getByText('Loading...')).toBeInTheDocument();
  });

  it('shows users when loaded', async () => {
    api.getUsers.mockResolvedValue([
      { id: '1', name: 'John' },
      { id: '2', name: 'Jane' },
    ]);

    render(<UserList />, { wrapper });

    await waitFor(() => {
      expect(screen.getByText('John')).toBeInTheDocument();
      expect(screen.getByText('Jane')).toBeInTheDocument();
    });
  });

  it('shows error message on failure', async () => {
    api.getUsers.mockRejectedValue(new Error('Network error'));

    render(<UserList />, { wrapper });

    await waitFor(() => {
      expect(screen.getByText(/error/i)).toBeInTheDocument();
    });
  });
});
Testing Forms
tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';

describe('LoginForm', () => {
  it('submits form with valid data', async () => {
    const onSubmit = vi.fn();
    const user = userEvent.setup();

    render(<LoginForm onSubmit={onSubmit} />);

    await user.type(screen.getByLabelText(/email/i), 'test@example.com');
    await user.type(screen.getByLabelText(/password/i), 'password123');
    await user.click(screen.getByRole('button', { name: /submit/i }));

    expect(onSubmit).toHaveBeenCalledWith({
      email: 'test@example.com',
      password: 'password123',
    });
  });

  it('shows validation errors', async () => {
    const user = userEvent.setup();

    render(<LoginForm onSubmit={vi.fn()} />);

    // Submit empty form
    await user.click(screen.getByRole('button', { name: /submit/i }));

    expect(screen.getByText(/email is required/i)).toBeInTheDocument();
    expect(screen.getByText(/password is required/i)).toBeInTheDocument();
  });

  it('disables submit button while submitting', async () => {
    const user = userEvent.setup();

    render(
      <LoginForm
        onSubmit={() => new Promise((resolve) => setTimeout(resolve, 100))}
      />
    );

    await user.type(screen.getByLabelText(/email/i), 'test@example.com');
    await user.type(screen.getByLabelText(/password/i), 'password123');
    await user.click(screen.getByRole('button', { name: /submit/i }));

    expect(screen.getByRole('button', { name: /submitting/i })).toBeDisabled();
  });
});

Component Testing (Vue)

Basic Component Test
ts
import { mount } from '@vue/test-utils';
import UserCard from './UserCard.vue';

describe('UserCard', () => {
  const user = { id: '1', name: 'John Doe', email: 'john@example.com' };

  it('renders user information', () => {
    const wrapper = mount(UserCard, {
      props: { user },
    });

    expect(wrapper.text()).toContain('John Doe');
    expect(wrapper.text()).toContain('john@example.com');
  });

  it('emits select event when clicked', async () => {
    const wrapper = mount(UserCard, {
      props: { user },
    });

    await wrapper.find('button').trigger('click');

    expect(wrapper.emitted('select')).toBeTruthy();
    expect(wrapper.emitted('select')[0]).toEqual([user]);
  });
});
Testing with Pinia
ts
import { mount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import UserList from './UserList.vue';
import { useUserStore } from '@/stores/userStore';

describe('UserList', () => {
  it('renders users from store', () => {
    const wrapper = mount(UserList, {
      global: {
        plugins: [
          createTestingPinia({
            initialState: {
              user: {
                users: [
                  { id: '1', name: 'John' },
                  { id: '2', name: 'Jane' },
                ],
              },
            },
          }),
        ],
      },
    });

    expect(wrapper.text()).toContain('John');
    expect(wrapper.text()).toContain('Jane');
  });

  it('calls fetchUsers on mount', () => {
    mount(UserList, {
      global: {
        plugins: [createTestingPinia()],
      },
    });

    const store = useUserStore();
    expect(store.fetchUsers).toHaveBeenCalled();
  });
});

Hook Testing

tsx
import { renderHook, act, waitFor } from '@testing-library/react';
import { useCounter } from './useCounter';

describe('useCounter', () => {
  it('initializes with default value', () => {
    const { result } = renderHook(() => useCounter());

    expect(result.current.count).toBe(0);
  });

  it('initializes with custom value', () => {
    const { result } = renderHook(() => useCounter({ initial: 10 }));

    expect(result.current.count).toBe(10);
  });

  it('increments counter', () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.increment();
    });

    expect(result.current.count).toBe(1);
  });

  it('respects max limit', () => {
    const { result } = renderHook(() => useCounter({ initial: 9, max: 10 }));

    act(() => {
      result.current.increment();
      result.current.increment();
    });

    expect(result.current.count).toBe(10);
  });
});

Mocking

API Mocking
tsx
// __mocks__/api.ts
export const api = {
  getUsers: vi.fn(),
  createUser: vi.fn(),
  updateUser: vi.fn(),
  deleteUser: vi.fn(),
};

// In test
vi.mock('@/api');

beforeEach(() => {
  vi.clearAllMocks();
});

it('fetches users', async () => {
  api.getUsers.mockResolvedValue([{ id: '1', name: 'John' }]);

  render(<UserList />);

  await waitFor(() => {
    expect(api.getUsers).toHaveBeenCalledTimes(1);
  });
});
Module Mocking
tsx
// Mock entire module
vi.mock('@/utils/date', () => ({
  formatDate: vi.fn(() => '2024-01-01'),
}));

// Mock specific export
vi.mock('@/config', async () => {
  const actual = await vi.importActual('@/config');
  return {
    ...actual,
    API_URL: 'http://test-api.com',
  };
});
Timer Mocking
tsx
describe('Debounced search', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('debounces search input', async () => {
    const onSearch = vi.fn();
    const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });

    render(<SearchInput onSearch={onSearch} debounceMs={300} />);

    await user.type(screen.getByRole('searchbox'), 'test');

    expect(onSearch).not.toHaveBeenCalled();

    vi.advanceTimersByTime(300);

    expect(onSearch).toHaveBeenCalledWith('test');
  });
});

Integration Testing

Testing Page Flow
tsx
describe('User Registration Flow', () => {
  it('completes registration successfully', async () => {
    const user = userEvent.setup();

    render(<App />);

    // Navigate to registration
    await user.click(screen.getByRole('link', { name: /register/i }));

    // Fill form
    await user.type(screen.getByLabelText(/name/i), 'John Doe');
    await user.type(screen.getByLabelText(/email/i), 'john@example.com');
    await user.type(screen.getByLabelText(/password/i), 'SecurePass123!');

    // Submit
    await user.click(screen.getByRole('button', { name: /register/i }));

    // Verify redirect to dashboard
    await waitFor(() => {
      expect(screen.getByText(/welcome, john/i)).toBeInTheDocument();
    });
  });
});

Test Organization

File Structure
src/
├── components/
│   └── UserCard/
│       ├── UserCard.tsx
│       ├── UserCard.test.tsx
│       └── index.ts
├── hooks/
│   └── useAuth/
│       ├── useAuth.ts
│       └── useAuth.test.ts
└── __tests__/
    └── integration/
        └── auth.test.tsx
Test Setup
ts
// vitest.setup.ts
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';

afterEach(() => {
  cleanup();
});

Performance Testing

tsx
import { render } from '@testing-library/react';
import { performance } from 'perf_hooks';

describe('UserList Performance', () => {
  it('renders 1000 items within 100ms', () => {
    const items = Array.from({ length: 1000 }, (_, i) => ({
      id: String(i),
      name: `User ${i}`,
    }));

    const start = performance.now();
    render(<UserList items={items} />);
    const duration = performance.now() - start;

    expect(duration).toBeLessThan(100);
  });
});

Accessibility Testing

tsx
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

describe('Accessibility', () => {
  it('has no accessibility violations', async () => {
    const { container } = render(<LoginForm />);

    const results = await axe(container);

    expect(results).toHaveNoViolations();
  });
});

Frontend testing patterns for React and Vue applications

© MadAppGang, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in plugins/dev/skills/frontend/testing-frontend of MadAppGang/claude-code.

Open the folder on GitHubat commit 6097ad4

Compare with similar skills

Testing Frontend next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Testing Frontend compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Testing Frontend this skillMadAppGang/claude-code285—~2.9kAutomated safety check: PassMIT
React Testingaffaan-m/ECC276k1 repos~3.3kAutomated safety check: PassMIT
React Testingcitypaul/.dotfiles740—~3.6kAutomated safety check: PassCustom licence
React TestingHoangNguyen0403/agent-skills-standard571—~1.3kAutomated safety check: PassMIT
Frontend TestingOhh-889/skyroc7951 repos~2.5kAutomated safety check: PassMIT
Playwright Testingchongdashu/vibejam-starter-pack149—~2.1kAutomated safety check: PassNone

Similar skills

  • React Testing

    affaan-m/ECC

    React component testing with React Testing Library, Vitest/Jest, MSW for network mocking, accessibility assertions with axe, and the decision boundary between component tests and Playwright/Cypress…

    276k GitHub starsUsed in 1 repo~3.3k tokens
    Testing & QAAuto-check passed
  • React Testing

    citypaul/.dotfiles

    React component testing patterns including components, hooks, context, and forms.

    740 GitHub stars~3.6k tokensUpdated today
    Testing & QAAuto-check passed
  • React Testing

    HoangNguyen0403/agent-skills-standard

    Test React components with RTL and Jest/Vitest. An agent skill from HoangNguyen0403/agent-skills-standard.

    571 GitHub stars~1.3k tokensUpdated today
    Testing & QAAuto-check passed
  • Frontend Testing

    Ohh-889/skyroc

    Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities.

    795 GitHub starsUsed in 1 repo~2.5k tokens
    Testing & QAAuto-check passed
  • Playwright Testing

    chongdashu/vibejam-starter-pack

    Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y.

    149 GitHub stars~2.1k tokensUpdated 5 mo ago
    Testing & QAAuto-check passed
  • Test Coverage

    scott-fryxell/brayness

    Write Vitest specs for Vue 3 JavaScript (Vite Plus, happy-dom, @vue/test-utils) and analyze V8 coverage + Fallow health to prioritize test-first refactors.

    125 GitHub stars~1.6k tokensUpdated 11 days ago
    Testing & QAAuto-check passed

More from MadAppGang/claude-code

All 69 skills in this repo
  • Content Brief

    MadAppGang/claude-code

    Content brief template and creation methodology for SEO-optimized content.

    285 GitHub starsUsed in 1 repo~959 tokens
    Auto-check passed
  • Context Detection

    MadAppGang/claude-code

    A skill your agent uses when detecting project technology stack from files/configs/directory structure, auto-loading framework-specific skills, or analyzing multi-stack fullstack projects (e.g…

    285 GitHub stars~5.4k tokensUpdated 6 mo ago
    Auto-check passed
  • Content Optimizer

    MadAppGang/claude-code

    On-page SEO optimization techniques including keyword density, meta tags, heading structure, and readability.

    285 GitHub starsUsed in 1 repo~694 tokens
    Auto-check passed
  • Keyword Cluster Builder

    MadAppGang/claude-code

    Techniques for expanding seed keywords and clustering by topic and intent.

    285 GitHub starsUsed in 1 repo~674 tokens
    Auto-check passed
  • Serp Analysis

    MadAppGang/claude-code

    SERP analysis techniques for intent classification, feature identification, and competitive intelligence.

    285 GitHub starsUsed in 1 repo~1k tokens
    Auto-check passed
  • Agent Coordination Discipline

    MadAppGang/claude-code

    A skill your agent uses when deciding whether to launch an agent, selecting which agent to use, or coordinating multiple agents.

    285 GitHub stars~3.8k tokensUpdated 6 mo ago
    Auto-check passed

Questions about Testing Frontend

What does Testing Frontend do?

A skill your agent uses when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications. Testing Frontend is an agent skill from MadAppGang/claude-code. Use when writing component tests, testing user interactions, mocking APIs, or setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

When should I use Testing Frontend?

Testing Frontend fits situations like: writing component tests; testing user interactions; setting up Vitest/React Testing Library/Vue Test Utils for frontend applications.

How do I install Testing Frontend in Claude Code?

Run `npx skills add MadAppGang/claude-code --skill testing-frontend -a claude-code`. Or copy the skill folder (plugins/dev/skills/frontend/testing-frontend in MadAppGang/claude-code) into .claude/skills/testing-frontend in your project. Claude Code loads it when a task matches its description.

How do I install Testing Frontend in Codex?

Run `npx skills add MadAppGang/claude-code --skill testing-frontend -a codex`. Or copy the skill folder (plugins/dev/skills/frontend/testing-frontend in MadAppGang/claude-code) into .agents/skills/testing-frontend in your project. Codex loads it when a task matches its description.

Can I use Testing Frontend in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add MadAppGang/claude-code --skill testing-frontend -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/testing-frontend, .gemini/skills/testing-frontend, .github/skills/testing-frontend and .opencode/skills/testing-frontend in your project.

What does Testing Frontend need to run?

SKILL.md names no scripts, command-line tools or credentials: Testing Frontend is instructions for the agent only.

Does Testing Frontend access the network?

SKILL.md names 1 domain. In commands or code: test-api.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Testing Frontend safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Testing Frontend use?

Testing Frontend is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Testing Frontend use?

About 2.9k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Testing Frontend?

Skills that share tags, products or a category with Testing Frontend: React Testing (affaan-m/ECC, 276k stars), React Testing (citypaul/.dotfiles, 740 stars), React Testing (HoangNguyen0403/agent-skills-standard, 571 stars) and Frontend Testing (Ohh-889/skyroc, 795 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Testing Frontend?

MadAppGang (a GitHub organization) maintains it in MadAppGang/claude-code, which has 285 GitHub stars. The repository holds 69 skills in this directory. The repository was last updated on March 15, 2026.

Source: MadAppGang/claude-code on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.