Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
export {
byLabelText,
byPlaceholderText,
byRole,
byText,
findAllByLabelText,
findAllByRole,
findByLabelText,
findByRole,
findByText,
findRole,
queryAllByLabelText,
queryAllByRole,
queryByLabelText,
queryByRole,
queryByText,
queryRole,
screen,
within,
} from "./queries";

View File

@@ -0,0 +1,64 @@
# queries
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `queries` module re-exports the `@testing-library/react` query primitives — `screen`, `within`, `byRole`, `byLabelText`, `byPlaceholderText`, `byText`, `byDisplayValue`, `byAltText`, `byTitle`, `byTestId` — under one canonical import path so every package in the project reaches for the same API without individually depending on `@testing-library/react`. Keeping the re-export surface in one module also means upgrades to `@testing-library/react` touch a single file.
## Features
- Curated re-exports of every stable `@testing-library/react` query
- `screen` and `within` helpers included for scoped queries
- Types preserved — TS jumps straight to `@testing-library/react`'s definitions
- Zero runtime overhead — pure re-export
## Usage: Basic Example
```tsx
import { render, screen } from "@suaveplan/testing-dom";
render(<button>save</button>);
expect(screen.getByRole("button", { name: "save" })).toBeInTheDocument();
```
## Usage: Advanced Example
```tsx
import { render, screen, within } from "@suaveplan/testing-dom";
render(
<form aria-label="login">
<input aria-label="email" />
<input aria-label="password" type="password" />
<button type="submit">sign in</button>
</form>
);
const form = screen.getByRole("form", { name: "login" });
const scoped = within(form);
expect(scoped.getByLabelText("email")).toBeInTheDocument();
expect(scoped.getByRole("button", { name: "sign in" })).toBeEnabled();
```
## API Reference
### Re-exported symbols
- `screen` — global query root
- `within(container)` — scoped queries
- `getByRole` / `queryByRole` / `findByRole` — role-based
- `getByLabelText` / `queryByLabelText` / `findByLabelText`
- `getByPlaceholderText` / …
- `getByText` / …
- `getByDisplayValue` / …
- `getByAltText` / …
- `getByTitle` / …
- `getByTestId` / …
All retain their upstream type signatures.
## Implementation Notes
Rule #4 in `CLAUDE.md` forbids re-exporting external dependencies "for convenience" — the exception here is that this module serves as a project-wide swap point if we ever replace testing-library with another library. Every import path consumers use is `@suaveplan/testing-dom`, so a swap is a one-file change. The curated surface excludes `fireEvent` because userEvent (in `setup-user.ts`) is the preferred ergonomic wrapper — `fireEvent` is available via direct `@testing-library/react` import for callers who need the lower level.

View File

@@ -0,0 +1,69 @@
import { renderComponent } from "../render/render-component.js";
import { describe, expect, it } from "../runner/index.js";
import {
byLabelText,
byPlaceholderText,
byRole,
byText,
findRole,
queryRole,
screen,
within,
} from "./queries.js";
describe("Query aliases", () => {
it("byRole returns the matching element", { timeout: 5000 }, () => {
renderComponent(<button type="button">go</button>);
const btn = byRole("button");
expect(btn).toBeDefined();
expect(btn.textContent).toBe("go");
});
it("byText matches text content", { timeout: 5000 }, () => {
renderComponent(<p>hello world</p>);
expect(byText("hello world")).toBeDefined();
});
it("byLabelText matches an associated label", {
timeout: 5000,
}, () => {
renderComponent(
<label>
Username
<input type="text" />
</label>
);
expect(byLabelText("Username")).toBeDefined();
});
it("byPlaceholderText matches input placeholder", {
timeout: 5000,
}, () => {
renderComponent(<input type="text" placeholder="search" />);
expect(byPlaceholderText("search")).toBeDefined();
});
it("queryRole returns null on miss", { timeout: 5000 }, () => {
renderComponent(<div>no roles here</div>);
expect(queryRole("button")).toBeNull();
});
it("findRole resolves async-ly to the element", {
timeout: 5000,
}, async () => {
renderComponent(<button type="button">later</button>);
const btn = await findRole("button");
expect(btn).toBeDefined();
});
it("screen and within are re-exported", { timeout: 5000 }, () => {
renderComponent(
<div>
<span data-testid="inner">x</span>
</div>
);
const root = screen.getByTestId("inner");
const scoped = within(root.parentElement!);
expect(scoped.getByTestId("inner")).toBe(root);
});
});

View File

@@ -0,0 +1,81 @@
/**
* Re-exports `@testing-library/react`'s queries plus accessibility-first
* aliases. Prefer `byRole` / `byLabelText` / `byText` over `getByTestId`
* for resilient component tests.
*/
import {
type ByRoleOptions,
findAllByLabelText,
findAllByRole,
findByLabelText,
findByRole,
findByText,
queryAllByLabelText,
queryAllByRole,
queryByLabelText,
queryByRole,
queryByText,
screen,
within,
} from "@testing-library/react";
export {
findAllByLabelText,
findAllByRole,
findByLabelText,
findByRole,
findByText,
queryAllByLabelText,
queryAllByRole,
queryByLabelText,
queryByRole,
queryByText,
screen,
within,
};
/** Strict role query — throws if not found. */
export function byRole(
role: Parameters<typeof screen.getByRole>[0],
options?: ByRoleOptions
): HTMLElement {
return screen.getByRole(role, options);
}
/** Async role query — waits up to vitest's defaults. */
export function findRole(
role: Parameters<typeof screen.findByRole>[0],
options?: ByRoleOptions
): Promise<HTMLElement> {
return screen.findByRole(role, options);
}
/** Optional role query — returns `null` if not found. */
export function queryRole(
role: Parameters<typeof screen.queryByRole>[0],
options?: ByRoleOptions
): HTMLElement | null {
return screen.queryByRole(role, options);
}
/** Strict label-text query. */
export function byLabelText(
text: Parameters<typeof screen.getByLabelText>[0]
): HTMLElement {
return screen.getByLabelText(text);
}
/** Strict text-content query. */
export function byText(
text: Parameters<typeof screen.getByText>[0]
): HTMLElement {
return screen.getByText(text);
}
/** Strict placeholder-text query. */
export function byPlaceholderText(
text: Parameters<typeof screen.getByPlaceholderText>[0]
): HTMLElement {
return screen.getByPlaceholderText(text);
}