Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/CodySwannGT/lisa --skill testing-libraryコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
SOC 職業分類に基づく
SKILL.md を表示中
| name | testing-library |
| description | Enforces best practices for… |
This skill enforces best practices for unit testing in Expo applications using Jest, @testing-library/react-native, and jest-expo. Tests should be user-centric, accessible, and behavior-focused rather than implementation-focused.
Focus on what the component does from a user's perspective, not how it achieves it internally.
// Correct - tests visible behavior
expect(screen.getByRole("button", { name: "Submit" })).toBeEnabled();
// Incorrect - tests implementation details
expect(component.state.isSubmitting).toBe(false);
Queries should reflect how users and assistive technologies interact with the UI.
// Correct - uses accessible role and name
screen.getByRole("button", { name: /save changes/i });
// Incorrect - relies on implementation detail
screen.getByTestId("save-btn");
Each test should verify one behavior. Multiple assertions are acceptable when verifying different aspects of the same behavior.
// Correct - focused test
test("displays error message when submission fails", async () => {
render(<Form />);
await userEvent.press(screen.getByRole("button", { name: "Submit" }));
expect(await screen.findByRole("alert")).toHaveTextContent("Failed");
});
// Incorrect - testing multiple behaviors
test("form works correctly", async () => {
// Tests validation, submission, success, and error handling...
});
userEvent simulates realistic user interactions including the full event sequence.
// Correct - realistic interaction
const user = userEvent.setup();
await user.press(screen.getByRole("button", { name: "Submit" }));
// Less ideal - simplified event
fireEvent.press(screen.getByRole("button", { name: "Submit" }));
Choose queries based on accessibility, following this priority order:
| Priority | Query | When to Use |
|---|---|---|
| 1 | getByRole | Interactive elements, headings, buttons |
| 2 | getByLabelText | Form fields with labels |
| 3 | getByText | Non-interactive content, static text |
| 4 | getByTestId | Last resort when semantic queries fail |
For detailed query patterns, see references/query-priority.md.
// Correct - waits for element to appear
expect(await screen.findByRole("alert")).toBeOnTheScreen();
// Incorrect - may fail if element appears async
expect(screen.getByRole("alert")).toBeOnTheScreen();
// Correct - assertion inside waitFor
await waitFor(() => {
expect(mockCallback).toHaveBeenCalledWith("success");
});
// Incorrect - side effect inside waitFor
await waitFor(() => {
fireEvent.press(button); // Never do this
});
For comprehensive async patterns, see references/async-patterns.md.
These must be configured in jest/setup-jest.ts:
// AsyncStorage
jest.mock("@react-native-async-storage/async-storage", () =>
require("@react-native-async-storage/async-storage/jest/async-storage-mock")
);
// Expo Fonts (to avoid async icon assertions)
jest.mock("expo-font", () => ({
...jest.requireActual("expo-font"),
isLoaded: jest.fn(() => true),
}));
For complete mocking patterns, see references/mocking-patterns.md.
Use renderRouter from expo-router/testing-library instead of render when testing components that use Expo Router.
import { renderRouter, screen } from "expo-router/testing-library";
test("navigates to player detail", async () => {
renderRouter({
index: () => <PlayerList />,
"players/[id]": () => <PlayerDetail />,
});
await userEvent.press(screen.getByRole("button", { name: "View Player" }));
expect(screen).toHavePathname("/players/123");
});
For Expo Router testing details, see references/expo-router-testing.md.
__tests__/ directories, not alongside source filesapp/ directory (Expo Router constraint).test.ts or .test.tsx extensionsStructure every test with Arrange-Act-Assert:
test("increments counter when button pressed", async () => {
// Arrange
const user = userEvent.setup();
render(<Counter initialCount={0} />);
// Act
await user.press(screen.getByRole("button", { name: "Increment" }));
// Assert
expect(screen.getByRole("text", { name: "Count: 1" })).toBeOnTheScreen();
});
Use descriptive names that explain the expected behavior:
// Correct - describes behavior
test("displays validation error when email format is invalid", () => {});
test("disables submit button while form is submitting", () => {});
// Incorrect - vague or implementation-focused
test("email validation works", () => {});
test("sets isSubmitting to true", () => {});
Lisa configures Jest manually instead of using the jest-expo preset to avoid
jsdom incompatibility with react-native/jest/setup.js. The configuration in
jest.expo.ts provides haste, resolver, transform, and setupFiles that match
the preset's behavior without redefining window.
jest.useFakeTimers();
test("handles debounced input", async () => {
const user = userEvent.setup();
render(<SearchInput />);
await user.type(screen.getByRole("textbox"), "query");
jest.runAllTimers();
expect(await screen.findByText("Results")).toBeOnTheScreen();
});
// Wrong - testing internal state
expect(wrapper.state().isLoading).toBe(true);
// Wrong - testing component methods
expect(wrapper.instance().handleSubmit).toHaveBeenCalled();
// Correct - testing visible behavior
expect(screen.getByRole("progressbar")).toBeOnTheScreen();
// Wrong - using testID when semantic query exists
screen.getByTestId("submit-button");
// Correct - using accessible query
screen.getByRole("button", { name: "Submit" });
// Wrong - unnecessary act wrapper
await act(async () => {
render(<Component />);
});
// Correct - render already wraps in act
render(<Component />);
// Wrong - side effect in waitFor
await waitFor(() => {
fireEvent.press(button);
expect(result).toBeOnTheScreen();
});
// Correct - side effect before waitFor
fireEvent.press(button);
await waitFor(() => {
expect(result).toBeOnTheScreen();
});
// Wrong - multiple assertions
await waitFor(() => {
expect(title).toBeOnTheScreen();
expect(subtitle).toBeOnTheScreen();
expect(button).toBeEnabled();
});
// Correct - single assertion, chain with findBy
expect(await screen.findByRole("heading")).toBeOnTheScreen();
expect(screen.getByText("Subtitle")).toBeOnTheScreen();
expect(screen.getByRole("button")).toBeEnabled();
| Matcher | Purpose |
|---|---|
toBeOnTheScreen() | Element is currently rendered |
toBeEnabled() | Interactive element is enabled |
toBeDisabled() | Interactive element is disabled |
toHaveTextContent() | Element contains text |
toBeVisible() | Element is visible to user |
toBeChecked() | Checkbox/radio is checked |
| Prefix | Returns | Throws on 0 | Throws on >1 | Async |
|---|---|---|---|---|
| getBy | Element | Yes | Yes | No |
| queryBy | Element | null | No | Yes | No |
| findBy | Promise<Element> | Yes | Yes | Yes |
| getAllBy | Element[] | Yes | No | No |
| queryAllBy | Element[] | No | No | No |
| findAllBy | Promise<Element[]> | Yes | No | Yes |