Test Functions
Core functions for structuring and organizing your TWD tests.
describe(name, fn)
Groups related tests together for better organization and readability.
Syntax
describe(name: string, fn: () => void): voidParameters
- name (
string) - Descriptive name for the test group - fn (
function) - Function containing the test group's tests and setup
Examples
describe("User Authentication", () => {
it("should login with valid credentials", async () => {
// Test implementation
});
it("should reject invalid credentials", async () => {
// Test implementation
});
});Nested Groups
describe("Shopping Cart", () => {
describe("Adding Items", () => {
it("should add product to cart", async () => {
// Test implementation
});
});
describe("Removing Items", () => {
it("should remove product from cart", async () => {
// Test implementation
});
});
});describe.only(name, fn)
Run only this describe block (and its nested describes/tests). When used, other suites and tests that are not within the .only tree will be skipped — this is useful for focusing on a group of related tests while debugging.
Syntax
describe.only(name: string, fn: () => void): voidExample
describe.only("Payments", () => {
it("should process card payment", async () => {
// This test runs (others outside this describe are skipped)
});
});Tip: Remember to remove
describe.onlybefore merging or running full CI, as it will skip other tests.
describe.skip(name, fn)
Skips this describe block and all its descendant tests. Useful for temporarily disabling an entire suite while you work on other areas.
Syntax
describe.skip(name: string, fn: () => void): voidExample
describe.skip("Experimental feature", () => {
it("should do something", async () => {
// This won't run
});
});Use describe.skip for larger blocks that aren't ready or are flaky in certain environments.
it(name, fn)
Defines an individual test case.
Syntax
it(name: string, fn: () => Promise<void> | void): voidParameters
- name (
string) - Descriptive name for the test - fn (
function) - Test implementation (can be async)
Examples
it("should display welcome message", async () => {
await twd.visit("/");
const heading = await twd.get("h1");
heading.should("contain.text", "Welcome");
});
it("should handle button clicks", async () => {
const button = await twd.get("button");
const user = userEvent.setup();
await user.click(button.el);
// Assertions...
});Async Tests
it("should load user data", async () => {
await twd.mockRequest("getUser", {
method: "GET",
url: "/api/user",
response: { name: "John Doe" }
});
await twd.visit("/profile");
await twd.waitForRequest("getUser");
const userName = await twd.get(".user-name");
userName.should("have.text", "John Doe");
});itOnly(name, fn)
Runs only this test, skipping all others in the suite. Useful for debugging specific tests.
Syntax
itOnly(name: string, fn: () => Promise<void> | void): voidParameters
- name (
string) - Descriptive name for the test - fn (
function) - Test implementation (can be async)
Examples
describe("User Management", () => {
itOnly("should create new user", async () => {
// Only this test will run
await twd.visit("/users/new");
// Test implementation...
});
it("should delete user", async () => {
// This test will be skipped
});
it("should update user", async () => {
// This test will also be skipped
});
});Use Cases
- Debugging - Focus on a failing test
- Development - Work on one test at a time
- Troubleshooting - Isolate problematic tests
WARNING
Remember to remove itOnly before committing code, as it will skip other tests in CI/CD.
itSkip(name, fn)
Skips this test. Useful for temporarily disabling broken or incomplete tests.
Syntax
itSkip(name: string, fn: () => Promise<void> | void): voidParameters
- name (
string) - Descriptive name for the test - fn (
function) - Test implementation (will not be executed)
Examples
describe("Payment Processing", () => {
it("should process credit card payment", async () => {
// This test runs normally
});
itSkip("should process PayPal payment", async () => {
// This test is skipped - maybe PayPal integration isn't ready
throw new Error("This won't run");
});
it("should handle payment errors", async () => {
// This test runs normally
});
});Use Cases
- Incomplete features - Skip tests for unimplemented functionality
- Known issues - Temporarily skip failing tests while fixing
- Environment-specific - Skip tests that don't work in certain environments
beforeEach(fn)
Runs a setup function before each test in the current describe block.
Syntax
beforeEach(fn: () => Promise<void> | void): voidParameters
- fn (
function) - Setup function to run before each test (can be async)
Examples
describe("User Dashboard", () => {
beforeEach(() => {
// Reset state before each test
localStorage.clear();
twd.clearRequestMockRules();
});
it("should show user profile", async () => {
// Clean state guaranteed
});
it("should display recent orders", async () => {
// Clean state guaranteed
});
});Async Setup
describe("API Integration", () => {
beforeEach(async () => {
// Set up common mocks
await twd.mockRequest("getUser", {
method: "GET",
url: "/api/user",
response: { id: 1, name: "Test User" }
});
});
it("should load user data", async () => {
// Mocks are already set up
});
});Nested beforeEach
describe("E-commerce Tests", () => {
beforeEach(() => {
// Runs before all tests in this describe
localStorage.clear();
});
describe("Shopping Cart", () => {
beforeEach(() => {
// Runs before cart tests (after parent beforeEach)
await twd.visit("/cart");
});
it("should add items to cart", async () => {
// Both beforeEach functions have run
});
});
});Best Practices
1. Descriptive Test Names
// ✅ Good - Describes what and why
it("should show validation error when email is invalid", async () => {
// Test implementation
});
// ❌ Bad - Too vague
it("should validate email", async () => {
// Test implementation
});2. Logical Test Grouping
// ✅ Good - Logical grouping
describe("User Registration Form", () => {
describe("Validation", () => {
it("should require email field", async () => {});
it("should require password field", async () => {});
});
describe("Submission", () => {
it("should create user on valid input", async () => {});
it("should show success message", async () => {});
});
});3. Clean State Management
describe("Shopping Cart", () => {
beforeEach(() => {
// Ensure clean state
localStorage.removeItem("cart");
sessionStorage.clear();
twd.clearRequestMockRules();
});
it("should start with empty cart", async () => {
// Test with guaranteed clean state
});
});4. Avoid Test Dependencies
// ✅ Good - Each test is independent
describe("User Management", () => {
it("should create user", async () => {
// Creates user from scratch
});
it("should delete user", async () => {
// Creates user first, then deletes
});
});
// ❌ Bad - Tests depend on each other
describe("User Management", () => {
it("should create user", async () => {
// Creates user
});
it("should delete user", async () => {
// Assumes user from previous test exists
});
});5. Use beforeEach for Common Setup
describe("Authentication Flow", () => {
beforeEach(async () => {
// Common setup for all auth tests
await twd.visit("/login");
});
it("should login successfully", async () => {
// No need to repeat visit and mock setup
});
it("should handle login errors", async () => {
// No need to repeat visit and mock setup
});
});Common Patterns
Test Suite Organization
describe("E-commerce Application", () => {
describe("Authentication", () => {
beforeEach(() => {
await twd.visit("/login");
});
it("should login with valid credentials", async () => {});
it("should reject invalid credentials", async () => {});
it("should redirect after login", async () => {});
});
describe("Product Catalog", () => {
beforeEach(() => {
await twd.visit("/products");
});
it("should display product list", async () => {});
it("should filter products", async () => {});
it("should search products", async () => {});
});
describe("Shopping Cart", () => {
beforeEach(() => {
localStorage.setItem("user", JSON.stringify({ id: 1 }));
await twd.visit("/cart");
});
it("should add products to cart", async () => {});
it("should remove products from cart", async () => {});
it("should calculate total price", async () => {});
});
});Conditional Test Execution
describe("Feature Tests", () => {
const isFeatureEnabled = localStorage.getItem("newFeature") === "true";
if (isFeatureEnabled) {
it("should show new feature", async () => {
// Test new feature
});
} else {
itSkip("should show new feature", async () => {
// Feature not enabled, skip test
});
}
});Debug-Focused Testing
describe("Debug Session", () => {
// Focus on the failing test
itOnly("should handle complex user workflow", async () => {
await twd.visit("/complex-page");
// Add debug logging
console.log("Starting complex workflow test");
const user = userEvent.setup();
// ... complex test steps
console.log("Workflow completed");
});
// Skip other tests to focus
itSkip("should handle simple workflow", async () => {
// Skip during debugging
});
});Error Handling
Test Function Errors
describe("Error Handling", () => {
it("should handle test errors gracefully", async () => {
try {
const element = await twd.get(".non-existent");
element.should("be.visible");
} catch (error) {
// Test will fail with descriptive error
console.error("Element not found:", error.message);
throw error; // Re-throw to fail the test
}
});
});Next Steps
- Learn about TWD Commands for element interaction
- Explore Assertions for testing element states