AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Testing Library

skill-dallay-agents-skills-testing-library · by dallay

>-

No reviews yet
0 installs
33 views
0.0% view→install

Install

$ agentstack add skill-dallay-agents-skills-testing-library

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-dallay-agents-skills-testing-library)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Testing Library? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

When to Use

  • Testing UI components with @testing-library/react, @testing-library/vue, or

@testing-library/dom.

  • Choosing the right query method for elements.
  • Simulating user interactions (typing, clicking, selecting).
  • Testing async behavior (data fetching, loading states, debounced input).
  • Writing tests that enforce and verify accessibility.

Critical Patterns

  • Test User Behavior, Not Implementation: Your tests should resemble how users interact with

your app. Query by role, label, and text — not by class, test ID, or component internals.

  • Query Priority: Follow this order strictly — it maps to accessibility:
  1. getByRole — accessible roles (button, textbox, heading)
  2. getByLabelText — form fields with labels
  3. getByPlaceholderText — when no label exists
  4. getByText — visible text content
  5. getByDisplayValue — current input values
  6. getByAltText — images
  7. getByTitle — title attribute
  8. getByTestId — LAST RESORT only
  • userEvent Over fireEvent: Always use @testing-library/user-event for interactions. It

simulates real browser behavior (focus, keydown, keyup, input) while fireEvent dispatches a single synthetic event.

  • No Implementation Details: NEVER assert on component state, props, or internal methods. NEVER

query by class name or component structure.

  • Async by Default: Use findBy queries (which return Promises) or wrap assertions in waitFor

for any UI that updates asynchronously.

Code Examples

Query Priority in Practice

import { render, screen } from "@testing-library/react";
import { SignupForm } from "./SignupForm";

it("renders the signup form with accessible elements", () => {
  render();

  // BEST — getByRole with accessible name
  screen.getByRole("heading", { name: /create account/i });
  screen.getByRole("textbox", { name: /email/i });
  screen.getByRole("button", { name: /sign up/i });

  // GOOD — getByLabelText for form fields
  screen.getByLabelText(/password/i);

  // ACCEPTABLE — getByText for static content
  screen.getByText(/already have an account/i);

  // LAST RESORT — only when element has no accessible role or text
  screen.getByTestId("captcha-widget");
});

User Events — The Right Way

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { SearchBar } from "./SearchBar";

describe("SearchBar", () => {
  it("calls onSearch when user types and submits", async () => {
    const user = userEvent.setup(); // Always call setup() first
    const onSearch = jest.fn();
    render();

    const input = screen.getByRole("searchbox", { name: /search/i });

    await user.type(input, "testing library");
    await user.keyboard("{Enter}");

    expect(onSearch).toHaveBeenCalledWith("testing library");
  });

  it("clears input when clear button is clicked", async () => {
    const user = userEvent.setup();
    render();

    const input = screen.getByRole("searchbox", { name: /search/i });
    await user.type(input, "some query");
    expect(input).toHaveValue("some query");

    await user.click(screen.getByRole("button", { name: /clear/i }));
    expect(input).toHaveValue("");
  });
});

Async Testing Patterns

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { UserProfile } from "./UserProfile";

it("loads and displays user data", async () => {
  render();

  // findBy queries wait for the element to appear (default timeout: 1000ms)
  const heading = await screen.findByRole("heading", { name: /alice/i });
  expect(heading).toBeInTheDocument();
});

it("shows error state when fetch fails", async () => {
  server.use(
    rest.get("/api/users/1", (_req, res, ctx) =>
      res(ctx.status(500))
    )
  );

  render();

  // waitFor retries the assertion until it passes
  await waitFor(() => {
    expect(screen.getByRole("alert")).toHaveTextContent(/failed to load/i);
  });
});

it("shows loading then content", async () => {
  render();

  // Loading state appears first
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  // Content replaces loading
  await waitFor(() => {
    expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
  });
  expect(screen.getByRole("heading", { name: /alice/i })).toBeInTheDocument();
});

Testing Forms

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ContactForm } from "./ContactForm";

describe("ContactForm", () => {
  it("submits form with valid data", async () => {
    const user = userEvent.setup();
    const onSubmit = jest.fn();
    render();

    await user.type(screen.getByLabelText(/name/i), "Alice");
    await user.type(screen.getByLabelText(/email/i), "alice@example.com");

    // Select from dropdown — use selectOptions
    await user.selectOptions(
      screen.getByRole("combobox", { name: /topic/i }),
      "support"
    );

    // Checkbox
    await user.click(screen.getByRole("checkbox", { name: /agree to terms/i }));

    await user.click(screen.getByRole("button", { name: /submit/i }));

    expect(onSubmit).toHaveBeenCalledWith({
      name: "Alice",
      email: "alice@example.com",
      topic: "support",
      agreedToTerms: true,
    });
  });

  it("displays validation errors for empty required fields", async () => {
    const user = userEvent.setup();
    render();

    await user.click(screen.getByRole("button", { name: /submit/i }));

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

Testing Modals and Portals

import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ConfirmDialog } from "./ConfirmDialog";

it("opens and confirms deletion", async () => {
  const user = userEvent.setup();
  const onConfirm = jest.fn();
  render();

  // Open dialog
  await user.click(screen.getByRole("button", { name: /delete item/i }));

  // Scope assertions to the dialog
  const dialog = screen.getByRole("dialog");
  expect(within(dialog).getByText(/are you sure/i)).toBeInTheDocument();

  // Confirm
  await user.click(within(dialog).getByRole("button", { name: /confirm/i }));

  expect(onConfirm).toHaveBeenCalledTimes(1);
  expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});

Accessibility Assertions

import { render } from "@testing-library/react";
import { axe, toHaveNoViolations } from "jest-axe";
import { Navigation } from "./Navigation";

expect.extend(toHaveNoViolations);

it("has no accessibility violations", async () => {
  const { container } = render();
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Custom Render with Providers

// test-utils.tsx
import { render, RenderOptions } from "@testing-library/react";
import { ThemeProvider } from "./ThemeProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

function AllProviders({ children }: { children: React.ReactNode }) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  return (
    
      {children}
    
  );
}

export function renderWithProviders(
  ui: React.ReactElement,
  options?: Omit,
) {
  return render(ui, { wrapper: AllProviders, ...options });
}

// Re-export everything from testing-library
export * from "@testing-library/react";
export { renderWithProviders as render };

Best Practices

DO

  • Use screen for all queries — it's always scoped to document.body and requires no

destructuring.

  • Use within() to scope queries to a specific container (dialog, list item, section).
  • Use getByRole as your default query — it verifies accessibility and finds elements reliably.
  • Use regex with case-insensitive flag (/submit/i) for text matching — it's resilient to casing

changes.

  • Create a custom render function that includes your app's providers (theme, router, query

client).

  • Use MSW (Mock Service Worker) for API mocking — it works at the network level and shares mocks

between tests and development.

DON'T

  • DON'T use container.querySelector(".my-class") — it tests implementation details, not user

behavior.

  • DON'T use fireEvent when userEvent is available — fireEvent doesn't simulate real browser

behavior.

  • DON'T use getByTestId as your first choice — it means the element lacks proper accessible roles

or labels. Fix the component first.

  • DON'T assert on component state or hooks directly — test what the user sees and interacts with.
  • DON'T use act() manually in most cases — userEvent and findBy handle it internally.
  • DON'T use waitFor with getBy inside — use findBy instead, which is getBy + waitFor

combined.

  • DON'T wrap non-async operations in waitFor — it adds unnecessary complexity and hides

synchronous bugs.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.