twd-js 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # twd
1
+ # TWD
2
2
 
3
3
  [![CI](https://github.com/BRIKEV/twd/actions/workflows/ci.yml/badge.svg)](https://github.com/BRIKEV/twd/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/twd-js.svg)](https://www.npmjs.com/package/twd-js)
@@ -12,7 +12,27 @@ TWD (Testing Web Development) is a library designed to seamlessly integrate test
12
12
 
13
13
  Currently, TWD supports React, with plans to add more frameworks soon.
14
14
 
15
- ---
15
+ ## Table of Contents
16
+
17
+ - [Features](#features)
18
+ - [Installation](#installation)
19
+ - [Quick Start](#quick-start)
20
+ - [Writing Tests](#writing-tests)
21
+ - [Test Structure](#test-structure)
22
+ - [Element Selection](#element-selection)
23
+ - [Assertions](#assertions)
24
+ - [User Interactions](#user-interactions)
25
+ - [API Mocking](#api-mocking)
26
+ - [Setup](#setup)
27
+ - [Mock Requests](#mock-requests)
28
+ - [Wait for Requests](#wait-for-requests)
29
+ - [API Reference](#api-reference)
30
+ - [Test Functions](#test-functions)
31
+ - [TWD Commands](#twd-commands)
32
+ - [Assertions](#assertions-1)
33
+ - [Examples](#examples)
34
+ - [Contributing](#contributing)
35
+ - [License](#license)
16
36
 
17
37
  ## Features
18
38
 
@@ -47,16 +67,39 @@ pnpm add twd-js
47
67
  import { createRoot } from "react-dom/client";
48
68
  import App from "./App";
49
69
  import "./index.css";
50
- import { TWDSidebar } from "twd-js";
70
+
71
+ // Only load the test sidebar and tests in development mode
72
+ if (import.meta.env.DEV) {
73
+ // Use Vite's glob import to find all test files
74
+ const testModules = import.meta.glob("./**/*.twd.test.ts");
75
+ const { initViteLoadTests, twd } = await import('twd-js');
76
+ // Initialize the TWD sidebar and load tests
77
+ initViteLoadTests(testModules, { open: true, position: 'left' });
78
+ // Optionally initialize request mocking
79
+ twd.initRequestMocking()
80
+ .then(() => {
81
+ console.log("Request mocking initialized");
82
+ })
83
+ .catch((err) => {
84
+ console.error("Error initializing request mocking:", err);
85
+ });
86
+ }
51
87
 
52
88
  createRoot(document.getElementById("root")!).render(
53
89
  <StrictMode>
54
90
  <App />
55
- <TWDSidebar />
91
+ <TWDSidebar open={false} />
56
92
  </StrictMode>
57
93
  );
58
94
  ```
59
95
 
96
+ **TWDSidebar Props**
97
+
98
+ | Prop | Type | Default | Description |
99
+ |-----------|---------------------|---------|---------------------------------------------|
100
+ | open | boolean | true | Whether the sidebar is open by default |
101
+ | position | "left" \| "right" | "left" | Sidebar position (left or right side)
102
+
60
103
  2. **Write your tests:**
61
104
 
62
105
  Create files ending with `.twd.test.ts` (or any extension you prefer):
@@ -82,47 +125,159 @@ pnpm add twd-js
82
125
 
83
126
  3. **Auto-load your tests:**
84
127
 
85
- - With Vite:
128
+
129
+ - With Vite and the new TWD loader:
86
130
 
87
131
  ```ts
88
- import { twd } from "twd-js";
89
- // src/loadTests.ts
90
- import.meta.glob("./**/*.twd.test.ts", { eager: true });
91
- // Initialize request mocking once
92
- twd
93
- .initRequestMocking()
94
- .then(() => {
95
- console.log("Request mocking initialized");
96
- })
97
- .catch((err) => {
98
- console.error("Error initializing request mocking:", err);
99
- });
100
- // No need to export anything
132
+ // src/main.tsx (or your main entry file)
133
+ import { StrictMode } from 'react';
134
+ import { createRoot } from 'react-dom/client';
135
+ import './index.css';
136
+ import router from './routes.ts';
137
+ import { RouterProvider } from 'react-router';
138
+
139
+ // Only load the test sidebar and tests in development mode
140
+ if (import.meta.env.DEV) {
141
+ // Use Vite's glob import to find all test files
142
+ const testModules = import.meta.glob("./**/*.twd.test.ts");
143
+ const { initViteLoadTests, twd } = await import('twd-js');
144
+ // Initialize the TWD sidebar and load tests
145
+ initViteLoadTests(testModules, { open: true, position: 'left' });
146
+ // Optionally initialize request mocking
147
+ twd.initRequestMocking()
148
+ .then(() => {
149
+ console.log("Request mocking initialized");
150
+ })
151
+ .catch((err) => {
152
+ console.error("Error initializing request mocking:", err);
153
+ });
154
+ }
155
+ // ...rest of your app bootstrap
156
+ createRoot(document.getElementById('root')!).render(
157
+ <StrictMode>
158
+ <RouterProvider router={router} />
159
+ </StrictMode>,
160
+ );
101
161
  ```
102
162
 
103
- - Or manually:
163
+ - Or manually (not recommended):
104
164
 
105
165
  ```ts
106
- // src/loadTests.ts
107
- import "./app.twd.test";
108
- import "./another-test-file.twd.test";
166
+ // src/main.tsx
167
+ if (import.meta.env.DEV) {
168
+ const testModules = {
169
+ './app.twd.test.ts': () => import('./app.twd.test'),
170
+ './another-test-file.twd.test.ts': () => import('./another-test-file.twd.test'),
171
+ };
172
+ const { initViteLoadTests } = await import('twd-js');
173
+ initViteLoadTests(testModules);
174
+ }
109
175
  ```
110
176
 
111
- Import `loadTests.ts` in your main entry (e.g., `main.tsx`):
177
+ 4. **Run your app and open the TWD sidebar** to see and run your tests in the browser.
112
178
 
113
- ```tsx
114
- import "./loadTests";
115
- ```
179
+ ## Writing Tests
116
180
 
117
- 4. **Run your app and open the TWD sidebar** to see and run your tests in the browser.
181
+ ### Test Structure
118
182
 
119
- ---
183
+ TWD uses a familiar testing structure with `describe`, `it`, `beforeEach`, and other common testing functions:
120
184
 
121
- ## Mock Service Worker (API Mocking)
185
+ ```ts
186
+ import { describe, it, itSkip, itOnly, beforeEach, twd, userEvent } from "twd-js";
122
187
 
123
- TWD provides a CLI to easily set up a mock service worker for API/request mocking in your app. You do **not** need to manually register the service worker in your app—TWD handles this automatically when you use `twd.initRequestMocking()` in your tests.
124
188
 
125
- ### Install the mock service worker
189
+ describe("User authentication", () => {
190
+ beforeEach(() => {
191
+ // Reset state before each test
192
+ });
193
+ it("should login successfully", async () => {
194
+ twd.visit("/login");
195
+ // Your test logic here
196
+ });
197
+
198
+ itSkip("skipped test", () => {
199
+ // This test will be skipped
200
+ });
201
+
202
+ itOnly("only this test runs", () => {
203
+ // Only this test will run when .only is present
204
+ });
205
+ });
206
+ ```
207
+
208
+ ### Element Selection
209
+
210
+ TWD provides two main methods for selecting elements:
211
+
212
+ ```ts
213
+ // Select a single element
214
+ const button = await twd.get("button");
215
+ const input = await twd.get("input#email");
216
+
217
+ // Select multiple elements
218
+ const items = await twd.getAll(".item");
219
+ items[0].should("be.visible");
220
+ ```
221
+
222
+ ### Assertions
223
+
224
+ TWD includes a comprehensive set of assertions for testing element states:
225
+
226
+ ```ts
227
+ // Text content
228
+ element.should("have.text", "exact text");
229
+ element.should("contain.text", "partial text");
230
+ element.should("be.empty");
231
+
232
+ // Attributes and values
233
+ element.should("have.attr", "placeholder", "Type here");
234
+ element.should("have.value", "input value");
235
+ element.should("have.class", "active");
236
+
237
+ // Element state
238
+ element.should("be.disabled");
239
+ element.should("be.enabled");
240
+ element.should("be.checked");
241
+ element.should("be.selected");
242
+ element.should("be.focused");
243
+ element.should("be.visible");
244
+
245
+ // Negated assertions
246
+ element.should("not.be.disabled");
247
+ element.should("not.have.text", "wrong text");
248
+
249
+ // URL assertions
250
+ twd.url().should("eq", "http://localhost:3000/contact");
251
+ twd.url().should("contain.url", "/contact");
252
+ ```
253
+
254
+ ### User Interactions
255
+
256
+ TWD integrates with `@testing-library/user-event` for realistic user interactions:
257
+
258
+ ```ts
259
+ import { userEvent } from "twd-js";
260
+
261
+ const user = userEvent.setup();
262
+ const button = await twd.get("button");
263
+ const input = await twd.get("input");
264
+
265
+ // Click interactions
266
+ await user.click(button.el);
267
+ await user.dblClick(button.el);
268
+
269
+ // Typing
270
+ await user.type(input.el, "Hello World");
271
+
272
+ // Form interactions
273
+ await user.selectOptions(selectElement.el, "option-value");
274
+ ```
275
+
276
+ ## API Mocking
277
+
278
+ ### Setup
279
+
280
+ TWD provides a CLI to easily set up a mock service worker for API/request mocking in your app. You do **not** need to manually register the service worker in your app—TWD handles this automatically when you use `twd.initRequestMocking()` in your tests.
126
281
 
127
282
  Run the following command in your project root:
128
283
 
@@ -135,45 +290,170 @@ npx twd-js init <public-dir> [--save]
135
290
 
136
291
  This will copy `mock-sw.js` to your public directory.
137
292
 
138
- ### How to use request mocking in your tests
293
+ ### Mock Requests
139
294
 
140
- Just call `await twd.initRequestMocking()` at the start of your test, then use `twd.mockRequest` to define your mocks. Example:
295
+ Use `twd.mockRequest()` to define API mocks in your tests:
141
296
 
142
297
  ```ts
143
- import { describe, it, twd, userEvent } from "twd-js";
298
+ import { twd } from "twd-js";
144
299
 
145
- it("fetches a message", async () => {
146
- twd.visit("/");
147
- const user = userEvent.setup();
148
- await twd.mockRequest("message", {
300
+ // Initialize mocking when loading tests
301
+ // await twd.initRequestMocking();
302
+
303
+ it("fetches user data", async () => {
304
+ // Mock the API request
305
+ twd.mockRequest("getUser", {
149
306
  method: "GET",
150
- url: "https://api.example.com/message",
307
+ url: "https://api.example.com/user/123",
151
308
  response: {
152
- value: "Mocked message!",
309
+ id: 123,
310
+ name: "John Doe",
311
+ email: "john@example.com"
153
312
  },
313
+ status: 200,
314
+ headers: { "Content-Type": "application/json" }
315
+ });
316
+
317
+ // Trigger the request in your app
318
+ const button = await twd.get("button[data-testid='load-user']");
319
+ await userEvent.click(button.el);
320
+
321
+ // Wait for the mock to be called
322
+ const rule = await twd.waitForRequest("getUser");
323
+ console.log("Request body:", rule.request);
324
+
325
+ // Clean up mocks after test
326
+ twd.clearRequestMockRules();
327
+ });
328
+ ```
329
+
330
+ ### Wait for Requests
331
+
332
+ TWD provides utilities to wait for mocked requests:
333
+
334
+ ```ts
335
+ // Wait for a single request
336
+ const rule = await twd.waitForRequest("getUserData");
337
+
338
+ // Wait for multiple requests
339
+ const rules = await twd.waitForRequests(["getUser", "getPosts"]);
340
+
341
+ // Access request data
342
+ console.log("Request body:", rule.request);
343
+ console.log("Response:", rule.response);
344
+ ```
345
+
346
+ ## API Reference
347
+
348
+ ### Test Functions
349
+
350
+ | Function | Description | Example |
351
+ |----------|-------------|---------|
352
+ | `describe(name, fn)` | Groups related tests | `describe("User login", () => {...})` |
353
+ | `it(name, fn)` | Defines a test case | `it("should login", async () => {...})` |
354
+ | `itOnly(name, fn)` | Runs only this test | `itOnly("focused test", () => {...})` |
355
+ | `itSkip(name, fn)` | Skips this test | `itSkip("broken test", () => {...})` |
356
+ | `beforeEach(fn)` | Runs before each test | `beforeEach(() => {...})` |
357
+
358
+ ### TWD Commands
359
+
360
+ | Command | Description | Example |
361
+ |---------|-------------|---------|
362
+ | `twd.get(selector)` | Select single element | `await twd.get("button")` |
363
+ | `twd.getAll(selector)` | Select multiple elements | `await twd.getAll(".item")` |
364
+ | `twd.visit(url)` | Navigate to URL | `twd.visit("/contact")` |
365
+ | `twd.wait(ms)` | Wait for specified time | `await twd.wait(500)` |
366
+ | `twd.url()` | Get URL API for assertions | `twd.url().should("contain.url", "/home")` |
367
+
368
+ ### Assertions
369
+
370
+ #### Element Content
371
+ - `have.text` - Exact text match
372
+ - `contain.text` - Partial text match
373
+ - `be.empty` - Element has no text content
374
+
375
+ #### Element Attributes
376
+ - `have.attr` - Has specific attribute value
377
+ - `have.value` - Input/textarea value
378
+ - `have.class` - Has CSS class
379
+
380
+ #### Element State
381
+ - `be.disabled` / `be.enabled` - Form element state
382
+ - `be.checked` - Checkbox/radio state
383
+ - `be.selected` - Option element state
384
+ - `be.focused` - Element has focus
385
+ - `be.visible` - Element is visible
386
+
387
+ #### URL Assertions
388
+ - `eq` - Exact URL match
389
+ - `contain.url` - URL contains substring
390
+
391
+ All assertions can be negated with `not.` prefix (e.g., `not.be.disabled`).
392
+
393
+ ## Examples
394
+
395
+ ### Basic Form Testing
396
+
397
+ ```ts
398
+ import { describe, it, twd, userEvent } from "twd-js";
399
+
400
+ describe("Contact form", () => {
401
+ it("submits form data", async () => {
402
+ twd.visit("/contact");
403
+
404
+ const user = userEvent.setup();
405
+ const emailInput = await twd.get("input#email");
406
+ const messageInput = await twd.get("textarea#message");
407
+ const submitBtn = await twd.get("button[type='submit']");
408
+
409
+ await user.type(emailInput.el, "test@example.com");
410
+ await user.type(messageInput.el, "Hello world");
411
+
412
+ emailInput.should("have.value", "test@example.com");
413
+ messageInput.should("have.value", "Hello world");
414
+
415
+ await user.click(submitBtn.el);
154
416
  });
155
- const btn = await twd.get("button[data-twd='message-button']");
156
- await user.click(btn.el);
157
- await twd.waitForRequest("message");
158
- const messageText = await twd.get("p[data-twd='message-text']");
159
- messageText.should("have.text", "Mocked message!");
160
417
  });
161
418
  ```
162
419
 
163
- ---
420
+ ### API Mocking with Authentication
164
421
 
165
- ## More Usage Examples
422
+ ```ts
423
+ import { describe, it, twd, userEvent } from "twd-js";
166
424
 
167
- See the [examples](https://github.com/BRIKEV/twd/tree/main/examples) directory for more scenarios and advanced usage.
425
+ describe("Protected routes", () => {
426
+ it("redirects to login when unauthorized", async () => {
427
+ twd.visit("/dashboard");
428
+ await twd.wait(100);
429
+ twd.url().should("contain.url", "/login");
430
+ });
431
+
432
+ it("loads dashboard with valid session", async () => {
433
+ // Mock authentication check
434
+ twd.mockRequest("authCheck", {
435
+ method: "GET",
436
+ url: "/api/auth/me",
437
+ response: { id: 1, name: "John Doe" }
438
+ });
439
+
440
+ twd.visit("/dashboard");
441
+ await twd.waitForRequest("authCheck");
442
+
443
+ const welcome = await twd.get("h1");
444
+ welcome.should("contain.text", "Welcome, John");
445
+
446
+ twd.clearRequestMockRules();
447
+ });
448
+ });
449
+ ```
168
450
 
169
- ---
451
+ For more comprehensive examples, see the [examples](https://github.com/BRIKEV/twd/tree/main/examples) directory in the repository.
170
452
 
171
453
  ## Contributing
172
454
 
173
455
  Contributions are welcome! Please open issues or pull requests on [GitHub](https://github.com/BRIKEV/twd).
174
456
 
175
- ---
176
-
177
457
  ## License
178
458
 
179
459
  This project is licensed under the [MIT License](./LICENSE).