Agent skill

frontend-testing

Use this skill when writing or running frontend unit and component tests with Vitest and Testing Library. Covers render/screen/fireEvent patterns, vi.mock for mocking, and the AAA (Arrange-Act-Assert) test structure. Apply when adding test coverage for Svelte components, debugging test failures, or setting up test utilities.

Stars 2,455
Forks 508

Install this agent skill to your Project

npx add-skill https://github.com/exceptionless/Exceptionless/tree/main/.agents/skills/frontend-testing

SKILL.md

Frontend Testing

Documentation: vitest.dev | testing-library.com

Running Tests

bash
npm run test:unit

Framework & Location

  • Framework: Vitest + @testing-library/svelte
  • Location: Co-locate with code as .test.ts or .spec.ts
  • TDD workflow: When fixing bugs or adding features, write a failing test first

AAA Pattern

Use explicit Arrange, Act, Assert regions:

typescript
import { describe, expect, it } from "vitest";

describe("Calculator", () => {
    it("should add two numbers correctly", () => {
        // Arrange
        const a = 5;
        const b = 3;

        // Act
        const result = add(a, b);

        // Assert
        expect(result).toBe(8);
    });

    it("should handle negative numbers", () => {
        // Arrange
        const a = -5;
        const b = 3;

        // Act
        const result = add(a, b);

        // Assert
        expect(result).toBe(-2);
    });
});

Test Patterns from Codebase

Unit Tests with AAA

From dates.test.ts:

typescript
import { describe, expect, it } from "vitest";
import { getDifferenceInSeconds, getRelativeTimeFormatUnit } from "./dates";

describe("getDifferenceInSeconds", () => {
    it("should calculate difference in seconds correctly", () => {
        // Arrange
        const now = new Date();
        const past = new Date(now.getTime() - 5000);

        // Act
        const result = getDifferenceInSeconds(past);

        // Assert
        expect(result).toBeCloseTo(5, 0);
    });
});

describe("getRelativeTimeFormatUnit", () => {
    it("should return correct unit for given seconds", () => {
        // Arrange & Act & Assert (simple value tests)
        expect(getRelativeTimeFormatUnit(30)).toBe("seconds");
        expect(getRelativeTimeFormatUnit(1800)).toBe("minutes");
        expect(getRelativeTimeFormatUnit(7200)).toBe("hours");
    });

    it("should handle boundary cases correctly", () => {
        // Arrange & Act & Assert
        expect(getRelativeTimeFormatUnit(59)).toBe("seconds");
        expect(getRelativeTimeFormatUnit(60)).toBe("minutes");
    });
});

Testing with Spies

From cached-persisted-state.svelte.test.ts:

typescript
import { beforeEach, describe, expect, it, vi } from "vitest";
import { CachedPersistedState } from "./cached-persisted-state.svelte";

describe("CachedPersistedState", () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    it("should initialize with default value when storage is empty", () => {
        // Arrange & Act
        const state = new CachedPersistedState("test-key", "default");

        // Assert
        expect(state.current).toBe("default");
    });

    it("should return cached value without reading storage repeatedly", () => {
        // Arrange
        const getItemSpy = vi.spyOn(Storage.prototype, "getItem");
        localStorage.setItem("test-key", "value1");
        const state = new CachedPersistedState("test-key", "default");
        getItemSpy.mockClear();

        // Act
        const val1 = state.current;
        const val2 = state.current;

        // Assert
        expect(val1).toBe("value1");
        expect(val2).toBe("value1");
        expect(getItemSpy).not.toHaveBeenCalled();
    });
});

Testing String Transformations

From helpers.svelte.test.ts:

typescript
import { describe, expect, it } from "vitest";
import { quoteIfSpecialCharacters } from "./helpers.svelte";

describe("helpers.svelte", () => {
    it("quoteIfSpecialCharacters handles tabs and newlines", () => {
        // Arrange & Act & Assert
        expect(quoteIfSpecialCharacters("foo\tbar")).toBe('"foo\tbar"');
        expect(quoteIfSpecialCharacters("foo\nbar")).toBe('"foo\nbar"');
    });

    it("quoteIfSpecialCharacters handles empty string and undefined/null", () => {
        // Arrange & Act & Assert
        expect(quoteIfSpecialCharacters("")).toBe("");
        expect(quoteIfSpecialCharacters(undefined)).toBeUndefined();
        expect(quoteIfSpecialCharacters(null)).toBeNull();
    });

    it("quoteIfSpecialCharacters quotes all Lucene special characters", () => {
        // Arrange
        const luceneSpecials = [
            "+",
            "-",
            "!",
            "(",
            ")",
            "{",
            "}",
            "[",
            "]",
            "^",
            '"',
            "~",
            "*",
            "?",
            ":",
            "\\",
            "/",
        ];

        // Act & Assert
        for (const char of luceneSpecials) {
            expect(quoteIfSpecialCharacters(char)).toBe(`"${char}"`);
        }
    });
});

Query Selection Priority

Use accessible queries (not implementation details):

typescript
// ✅ Role-based
screen.getByRole("button", { name: /submit/i });
screen.getByRole("textbox", { name: /email/i });

// ✅ Label-based
screen.getByLabelText("Email address");

// ✅ Text-based
screen.getByText("Welcome back");

// ⚠️ Fallback: Test ID
screen.getByTestId("complex-chart");

// ❌ Avoid: Implementation details
screen.getByClassName("btn-primary");

Mocking Modules

typescript
import { vi, describe, it, beforeEach, expect } from "vitest";
import { render, screen } from "@testing-library/svelte";

vi.mock("$lib/api/organizations", () => ({
    getOrganizations: vi.fn(),
}));

import { getOrganizations } from "$lib/api/organizations";
import OrganizationList from "./organization-list.svelte";

describe("OrganizationList", () => {
    beforeEach(() => {
        vi.clearAllMocks();
    });

    it("displays organizations from API", async () => {
        // Arrange
        const mockOrganizations = [{ id: "1", name: "Org One" }];
        vi.mocked(getOrganizations).mockResolvedValue(mockOrganizations);

        // Act
        render(OrganizationList);

        // Assert
        expect(await screen.findByText("Org One")).toBeInTheDocument();
    });
});

Snapshot Testing (Use Sparingly)

typescript
it("matches snapshot", () => {
    // Arrange & Act
    const { container } = render(StaticComponent);

    // Assert
    expect(container).toMatchSnapshot();
});

Use snapshots only for stable, static components. Prefer explicit assertions for dynamic content.

Expand your agent's capabilities with these related and highly-rated skills.

exceptionless/Exceptionless

foundatio-repositories

2,455 508
Explore
exceptionless/Exceptionless

releasenotes

Generate formatted changelogs from git history since the last release tag. Use when preparing release notes that categorize changes into breaking changes, features, fixes, and other sections.

2,455 508
Explore
exceptionless/Exceptionless

e2e-testing

Use this skill when writing or running end-to-end browser tests with Playwright. Covers Page Object Model patterns, selector strategies (data-testid, getByRole, getByLabel), fixtures, and accessibility audits with axe-playwright. Apply when adding E2E test coverage, debugging flaky tests, or testing user flows through the browser.

2,455 508
Explore
exceptionless/Exceptionless

tanstack-query

Use this skill when fetching data, managing server state, or handling API mutations in the Svelte frontend. Covers createQuery, createMutation, query keys, cache invalidation, optimistic updates, and WebSocket-driven refetching. Apply when adding API calls, managing loading/error states, or coordinating cache updates after mutations.

2,455 508
Explore
exceptionless/Exceptionless

dogfood

Systematically explore and test a web application to find bugs, UX issues, and other problems. Use when asked to "dogfood", "QA", "exploratory test", "find issues", "bug hunt", "test this app/site/platform", or review the quality of a web application. Produces a structured report with full reproduction evidence -- step-by-step screenshots, repro videos, and detailed repro steps for every issue -- so findings can be handed directly to the responsible teams.

2,455 508
Explore
exceptionless/Exceptionless

storybook

Use this skill when creating or updating Storybook stories for Svelte components. Covers Svelte CSF story format, defineMeta, argTypes, snippet-based customization, and autodocs. Apply when adding visual documentation for components, setting up story files, or running Storybook for development.

2,455 508
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results