Loading...
Loading...
TWD test writing context — teaches AI agents how to write correct TWD (Test While Developing) in-browser tests. Use this when writing, reviewing, or modifying TWD test files (*.twd.test.ts).
npx skill4agent add brikev/twd twd-test-writer@testing-library/domimport { twd, userEvent, screenDom } from "twd-js";
import { describe, it, beforeEach, afterEach, expect } from "twd-js/runner";twd-jstwduserEventscreenDomscreenDomGlobaltwd-js/runnerdescribeitbeforeEachafterEachexpecttwd-js/uiMockedComponentdescribeitbeforeEachexpecttwd-js/runner*.twd.test.ts*.twd.test.tsx// twd.get() and twd.getAll() are async — ALWAYS await
const button = await twd.get("button");
const items = await twd.getAll(".item");
// userEvent methods are async — ALWAYS await
await userEvent.click(button.el);
await userEvent.type(input, "text");
// Test functions should be async
it("should do something", async () => { /* ... */ });screenDom// By role (most accessible — RECOMMENDED)
const button = screenDom.getByRole("button", { name: "Submit" });
const heading = screenDom.getByRole("heading", { name: "Welcome", level: 1 });
// By label (for form inputs)
const emailInput = screenDom.getByLabelText("Email Address");
// By text content
const message = screenDom.getByText("Success!");
const partial = screenDom.getByText(/welcome/i);
// By test ID
const card = screenDom.getByTestId("user-card");
// Query variants
screenDom.getByRole("button"); // Throws if not found
screenDom.queryByRole("button"); // Returns null if not found
await screenDom.findByRole("button"); // Waits for element (async)
screenDom.getAllByRole("button"); // Returns arrayscreenDomGlobalimport { screenDomGlobal } from "twd-js";
const modal = screenDomGlobal.getByRole("dialog");twd.get()const button = await twd.get("button");
const byId = await twd.get("#email");
const byClass = await twd.get(".error-message");
const multiple = await twd.getAll(".item");const user = userEvent.setup();
// With screenDom elements (direct)
await user.click(screenDom.getByRole("button", { name: "Save" }));
await user.type(screenDom.getByLabelText("Email"), "hello@example.com");
// With twd.get() elements (use .el for raw DOM)
const twdButton = await twd.get(".save-btn");
await user.click(twdButton.el);
// Other interactions
await user.dblClick(element);
await user.clear(input);
await user.selectOptions(select, "option-value");
await user.keyboard("{Enter}");const element = await twd.get("h1");
element.should("have.text", "Welcome");
element.should("contain.text", "come");
element.should("be.visible");
element.should("not.be.visible");
element.should("have.class", "header");
element.should("have.value", "test@example.com");
element.should("have.attr", "type", "submit");
element.should("be.disabled");
element.should("be.enabled");
element.should("be.checked");
element.should("be.focused");
element.should("be.empty");twd.should(screenDom.getByRole("button"), "be.visible");
twd.should(screenDom.getByRole("button"), "have.text", "Submit");await twd.url().should("eq", "http://localhost:3000/dashboard");
await twd.url().should("contain.url", "/dashboard");expect(array).to.have.length(3);
expect(value).to.equal("expected");
expect(obj).to.deep.equal({ key: "value" });await twd.visit("/");
await twd.visit("/login");
await twd.wait(1000); // Wait for time (ms)
await screenDom.findByText("Success!"); // Wait for element
await twd.notExists(".loading-spinner"); // Wait for element to NOT exist// Mock GET request
await twd.mockRequest("getUser", {
method: "GET",
url: "/api/user/123",
response: { id: 123, name: "John Doe" },
status: 200,
});
// Mock POST request
await twd.mockRequest("createUser", {
method: "POST",
url: "/api/users",
response: { id: 456, created: true },
status: 201,
});
// URL patterns with regex
await twd.mockRequest("getUserById", {
method: "GET",
url: /\/api\/users\/\d+/,
response: { id: 999, name: "Dynamic User" },
urlRegex: true,
});
// Error responses
await twd.mockRequest("serverError", {
method: "GET",
url: "/api/data",
response: { error: "Server error" },
status: 500,
});
// Wait for request and inspect body
const rule = await twd.waitForRequest("submitForm");
expect(rule.request).to.deep.equal({ email: "test@example.com" });
// Wait for multiple requests
await twd.waitForRequests(["getUser", "getPosts"]);// In your component — wrap with MockedComponent
import { MockedComponent } from "twd-js/ui";
function Dashboard() {
return (
<MockedComponent name="ExpensiveChart">
<ExpensiveChart data={data} />
</MockedComponent>
);
}// In your test
twd.mockComponent("ExpensiveChart", () => (
<div data-testid="mock-chart">Mocked Chart</div>
));// hooks/useAuth.ts — CORRECT: stubbable
const useAuth = () => useAuth0();
export default { useAuth };
// hooks/useAuth.ts — WRONG: cannot be stubbed
export const useAuth = () => useAuth0();// In test:
import Sinon from "sinon";
import authModule from "../hooks/useAuth";
Sinon.stub(authModule, "useAuth").returns({
isAuthenticated: true,
user: { name: "John" },
});
// Always Sinon.restore() in beforeEachimport { twd, userEvent, screenDom } from "twd-js";
import { describe, it, beforeEach, expect } from "twd-js/runner";
describe("Feature Name", () => {
beforeEach(() => {
twd.clearRequestMockRules();
twd.clearComponentMocks();
});
it("should display the page correctly", async () => {
// 1. Setup mocks BEFORE visiting
await twd.mockRequest("getData", {
method: "GET",
url: "/api/data",
response: { items: [] },
status: 200,
});
// 2. Navigate
await twd.visit("/page");
// 3. Wait for async operations
await twd.waitForRequest("getData");
// 4. Interact
const user = userEvent.setup();
const button = screenDom.getByRole("button", { name: "Load" });
await user.click(button);
// 5. Assert
const message = await twd.get(".message");
message.should("have.text", "No items found");
});
it.only("debug this test", async () => { /* Only this test runs */ });
it.skip("skip this test", async () => { /* This test won't run */ });
});awaittwd.get()userEvent.*twd.visit()screenDom.findBy*twd.visit()twd.clearRequestMockRules()twd.clearComponentMocks()beforeEachfspathdescribeitbeforeEachtwd-js/runnercy.get()cy.visit()twd.get()twd.visit()twd-js/runner