Initial Commit
This commit is contained in:
20
packages/testkit/testing/testing-dom/src/queries/index.ts
Normal file
20
packages/testkit/testing/testing-dom/src/queries/index.ts
Normal 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";
|
||||
64
packages/testkit/testing/testing-dom/src/queries/queries.md
Normal file
64
packages/testkit/testing/testing-dom/src/queries/queries.md
Normal 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.
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
81
packages/testkit/testing/testing-dom/src/queries/queries.ts
Normal file
81
packages/testkit/testing/testing-dom/src/queries/queries.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user