twd-js 0.1.0 → 0.1.2
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 +17 -11
- package/dist/asserts/index.d.ts +2 -0
- package/dist/commands/mockResponses.d.ts +46 -0
- package/dist/commands/type.d.ts +11 -0
- package/dist/commands/url.d.ts +33 -0
- package/dist/index.d.ts +2 -0
- package/dist/twd-types.d.ts +147 -0
- package/dist/twd.d.ts +116 -0
- package/dist/twd.es.js +632 -406
- package/dist/twd.umd.js +9 -4
- package/dist/twdRegistry.d.ts +18 -0
- package/dist/ui/ClosedSidebar.d.ts +5 -0
- package/dist/ui/Icons/ChevronDown.d.ts +2 -0
- package/dist/ui/Icons/ChevronRight.d.ts +2 -0
- package/dist/ui/Icons/Loader.d.ts +2 -0
- package/dist/ui/Icons/Play.d.ts +2 -0
- package/dist/ui/TWDSidebar.d.ts +1 -0
- package/dist/ui/TestList.d.ts +7 -0
- package/dist/ui/TestListItem.d.ts +9 -0
- package/dist/ui/groupTests.d.ts +7 -0
- package/dist/utils/assertionMessage.d.ts +1 -0
- package/dist/utils/log.d.ts +1 -0
- package/dist/utils/wait.d.ts +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# twd
|
|
2
2
|
|
|
3
3
|
[](https://github.com/BRIKEV/twd/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/twd-js)
|
|
4
5
|
[](./LICENSE)
|
|
5
6
|
|
|
6
7
|
> ⚠️ This is a **beta release** – expect frequent updates and possible breaking changes.
|
|
@@ -15,13 +16,13 @@ You can install TWD via npm:
|
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
18
|
# with npm
|
|
18
|
-
npm install twd
|
|
19
|
+
npm install twd-js
|
|
19
20
|
|
|
20
21
|
# with yarn
|
|
21
|
-
yarn add twd
|
|
22
|
+
yarn add twd-js
|
|
22
23
|
|
|
23
24
|
# with pnpm
|
|
24
|
-
pnpm add twd
|
|
25
|
+
pnpm add twd-js
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
## How to use
|
|
@@ -32,7 +33,7 @@ Add the our React Sidebar component to your application:
|
|
|
32
33
|
import { StrictMode } from 'react'
|
|
33
34
|
import { createRoot } from 'react-dom/client'
|
|
34
35
|
import './index.css'
|
|
35
|
-
import { TWDSidebar } from '
|
|
36
|
+
import { TWDSidebar } from 'twd-js'
|
|
36
37
|
import router from './routes.ts'
|
|
37
38
|
import { RouterProvider } from 'react-router'
|
|
38
39
|
|
|
@@ -44,11 +45,11 @@ createRoot(document.getElementById('root')!).render(
|
|
|
44
45
|
)
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
Then, create test files with the
|
|
48
|
+
Then, create test files with the `twd.test.ts` or any extension you want. For example:
|
|
48
49
|
|
|
49
|
-
```
|
|
50
|
-
// src/app.twd
|
|
51
|
-
import { describe, it, twd } from "
|
|
50
|
+
```ts
|
|
51
|
+
// src/app.twd.test.ts
|
|
52
|
+
import { describe, it, twd } from "twd-js";
|
|
52
53
|
|
|
53
54
|
beforeEach(() => {
|
|
54
55
|
console.log("Reset state before each test");
|
|
@@ -56,8 +57,13 @@ beforeEach(() => {
|
|
|
56
57
|
|
|
57
58
|
describe("App interactions", () => {
|
|
58
59
|
it("clicks the button", async () => {
|
|
60
|
+
twd.visit("/"); // Visit the root URL
|
|
59
61
|
const btn = await twd.get("button");
|
|
60
62
|
btn.click();
|
|
63
|
+
const message = await twd.get("#message");
|
|
64
|
+
// have.text
|
|
65
|
+
const haveText = await twd.get("#message");
|
|
66
|
+
haveText.should("have.text", "Hello");
|
|
61
67
|
});
|
|
62
68
|
});
|
|
63
69
|
```
|
|
@@ -66,8 +72,8 @@ After you create your test you need to load them in your application. You can do
|
|
|
66
72
|
|
|
67
73
|
```ts
|
|
68
74
|
// src/loadTests.ts
|
|
69
|
-
import "./app.twd
|
|
70
|
-
import "./another-test-file.twd
|
|
75
|
+
import "./app.twd.test";
|
|
76
|
+
import "./another-test-file.twd.test";
|
|
71
77
|
// Import other test files here
|
|
72
78
|
```
|
|
73
79
|
|
|
@@ -89,4 +95,4 @@ import './loadTests' // Import test files
|
|
|
89
95
|
|
|
90
96
|
Finally, run your application and open the TWD sidebar to see and run your tests.
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
You can check the [examples](https://github.com/BRIKEV/twd/tree/main/examples) directory for more usage scenarios.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type Rule = {
|
|
2
|
+
method: string;
|
|
3
|
+
url: string | RegExp;
|
|
4
|
+
response: unknown;
|
|
5
|
+
alias?: string;
|
|
6
|
+
executed?: boolean;
|
|
7
|
+
request?: unknown;
|
|
8
|
+
status?: number;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
export interface Options {
|
|
12
|
+
method: string;
|
|
13
|
+
url: string | RegExp;
|
|
14
|
+
response: unknown;
|
|
15
|
+
status?: number;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mock a network request.
|
|
20
|
+
*
|
|
21
|
+
* @param alias Identifier for the mock rule. Useful for `waitFor()`.
|
|
22
|
+
* @param options Options to configure the mock:
|
|
23
|
+
* - `method`: HTTP method ("GET", "POST", …)
|
|
24
|
+
* - `url`: URL string or RegExp to match
|
|
25
|
+
* - `response`: Body of the mocked response
|
|
26
|
+
* - `status`: (optional) HTTP status code (default: 200)
|
|
27
|
+
* - `headers`: (optional) Response headers
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* mockRequest("getUser", {
|
|
32
|
+
* method: "GET",
|
|
33
|
+
* url: /\/api\/user\/\d+/,
|
|
34
|
+
* response: { id: 1, name: "Kevin" },
|
|
35
|
+
* status: 200,
|
|
36
|
+
* headers: { "x-mock": "true" }
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const mockRequest: (alias: string, options: Options) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Wait for a mocked request to be made.
|
|
43
|
+
* @param alias The alias of the mock rule to wait for
|
|
44
|
+
* @returns The matched rule (with body if applicable)
|
|
45
|
+
*/
|
|
46
|
+
export declare const waitFor: (alias: string) => Promise<Rule>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simulates typing text into an input or textarea element, character by character,
|
|
3
|
+
* dispatching keydown, keypress, input, and keyup events for each character.
|
|
4
|
+
* This more closely mimics real user input and works with React's state updates.
|
|
5
|
+
*
|
|
6
|
+
* @param el The input or textarea element
|
|
7
|
+
* @param text The text to type
|
|
8
|
+
* @returns The input element after typing
|
|
9
|
+
*/
|
|
10
|
+
declare const simulateType: (el: HTMLInputElement | HTMLTextAreaElement, text: string) => HTMLInputElement | HTMLTextAreaElement;
|
|
11
|
+
export { simulateType };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All supported assertion names for the `should` function in url command
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* twd.url().should("contain.url", "/new-page");
|
|
6
|
+
* twd.url().should("eq", "http://localhost:3000/new-page");
|
|
7
|
+
*/
|
|
8
|
+
export type URLAssertionName = "eq" | "contain.url";
|
|
9
|
+
/**
|
|
10
|
+
* Negatable assertion names (e.g., 'not.have.text').
|
|
11
|
+
*/
|
|
12
|
+
export type Negatable<T extends string> = T | `not.${T}`;
|
|
13
|
+
/**
|
|
14
|
+
* All assertion names, including negated ones.
|
|
15
|
+
*/
|
|
16
|
+
export type AnyURLAssertion = Negatable<URLAssertionName>;
|
|
17
|
+
export type URLCommandAPI = {
|
|
18
|
+
location: Location;
|
|
19
|
+
should: (name: AnyURLAssertion, value: string) => URLCommandAPI | string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Argument types for each assertion.
|
|
23
|
+
*/
|
|
24
|
+
export type URLAssertionArgs = {
|
|
25
|
+
(name: "contain.url", urlPart: string): URLCommandAPI;
|
|
26
|
+
(name: "not.contain.url", urlPart: string): URLCommandAPI;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* URL command for assertions on the current URL.
|
|
30
|
+
* @returns URLCommandAPI
|
|
31
|
+
*/
|
|
32
|
+
declare const urlCommand: () => URLCommandAPI;
|
|
33
|
+
export default urlCommand;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and interfaces for the TWD testing library.
|
|
3
|
+
*
|
|
4
|
+
* @module twd-types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* All supported assertion names for the `should` function.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* api.should("have.text", "Hello");
|
|
11
|
+
* api.should("be.empty");
|
|
12
|
+
*/
|
|
13
|
+
export type AssertionName = "have.text" | "contain.text" | "be.empty" | "have.attr" | "have.value" | "be.disabled" | "be.enabled" | "be.checked" | "be.selected" | "be.focused" | "be.visible" | "have.class";
|
|
14
|
+
/**
|
|
15
|
+
* Negatable assertion names (e.g., 'not.have.text').
|
|
16
|
+
*/
|
|
17
|
+
export type Negatable<T extends string> = T | `not.${T}`;
|
|
18
|
+
/**
|
|
19
|
+
* All assertion names, including negated ones.
|
|
20
|
+
*/
|
|
21
|
+
export type AnyAssertion = Negatable<AssertionName>;
|
|
22
|
+
/**
|
|
23
|
+
* Argument types for each assertion.
|
|
24
|
+
*/
|
|
25
|
+
export type AssertionArgs = {
|
|
26
|
+
"have.text": [expected: string];
|
|
27
|
+
"contain.text": [expected: string];
|
|
28
|
+
"be.empty": [];
|
|
29
|
+
"have.attr": [attr: string, value: string];
|
|
30
|
+
"have.value": [value: string];
|
|
31
|
+
"be.disabled": [];
|
|
32
|
+
"be.enabled": [];
|
|
33
|
+
"be.checked": [];
|
|
34
|
+
"be.selected": [];
|
|
35
|
+
"be.focused": [];
|
|
36
|
+
"be.visible": [];
|
|
37
|
+
"have.class": [className: string];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Maps assertion name to its argument tuple.
|
|
41
|
+
*/
|
|
42
|
+
export type ArgsFor<A extends AnyAssertion> = A extends `not.${infer Base extends AssertionName}` ? AssertionArgs[Base] : A extends AssertionName ? AssertionArgs[A] : never;
|
|
43
|
+
/**
|
|
44
|
+
* Overloads for the `should` function, for best IntelliSense.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* twd.should("have.text", "Hello");
|
|
48
|
+
* twd.should("be.empty");
|
|
49
|
+
* twd.should("have.class", "active");
|
|
50
|
+
*/
|
|
51
|
+
export type ShouldFn = {
|
|
52
|
+
(name: "have.text", expected: string): TWDElemAPI;
|
|
53
|
+
(name: "not.have.text", expected: string): TWDElemAPI;
|
|
54
|
+
(name: "contain.text", expected: string): TWDElemAPI;
|
|
55
|
+
(name: "not.contain.text", expected: string): TWDElemAPI;
|
|
56
|
+
(name: "be.empty"): TWDElemAPI;
|
|
57
|
+
(name: "not.be.empty"): TWDElemAPI;
|
|
58
|
+
(name: "have.attr", attr: string, value: string): TWDElemAPI;
|
|
59
|
+
(name: "not.have.attr", attr: string, value: string): TWDElemAPI;
|
|
60
|
+
(name: "have.value", value: string): TWDElemAPI;
|
|
61
|
+
(name: "not.have.value", value: string): TWDElemAPI;
|
|
62
|
+
(name: "be.disabled"): TWDElemAPI;
|
|
63
|
+
(name: "not.be.disabled"): TWDElemAPI;
|
|
64
|
+
(name: "be.enabled"): TWDElemAPI;
|
|
65
|
+
(name: "not.be.enabled"): TWDElemAPI;
|
|
66
|
+
(name: "be.checked"): TWDElemAPI;
|
|
67
|
+
(name: "not.be.checked"): TWDElemAPI;
|
|
68
|
+
(name: "be.selected"): TWDElemAPI;
|
|
69
|
+
(name: "not.be.selected"): TWDElemAPI;
|
|
70
|
+
(name: "be.focused"): TWDElemAPI;
|
|
71
|
+
(name: "not.be.focused"): TWDElemAPI;
|
|
72
|
+
(name: "be.visible"): TWDElemAPI;
|
|
73
|
+
(name: "not.be.visible"): TWDElemAPI;
|
|
74
|
+
(name: "have.class", className: string): TWDElemAPI;
|
|
75
|
+
(name: "not.have.class", className: string): TWDElemAPI;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The main API returned by `twd.get()`.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const btn = await twd.get("button");
|
|
83
|
+
* btn.should("have.text", "Clicked").click();
|
|
84
|
+
*
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
*/
|
|
88
|
+
export interface TWDElemAPI {
|
|
89
|
+
/** The underlying DOM element. */
|
|
90
|
+
el: Element;
|
|
91
|
+
/**
|
|
92
|
+
* Simulates a user click on the element.
|
|
93
|
+
* Returns the same API so you can chain more actions.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const button = await twd.get("button");
|
|
98
|
+
* button.click();
|
|
99
|
+
*
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
click: () => void;
|
|
104
|
+
/**
|
|
105
|
+
* Types text into an input element.
|
|
106
|
+
* @param text The text to type.
|
|
107
|
+
* @returns The input element.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const email = await twd.get("input#email");
|
|
112
|
+
* email.type("test@example.com");
|
|
113
|
+
*
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
*/
|
|
117
|
+
type: (text: string) => HTMLInputElement | HTMLTextAreaElement;
|
|
118
|
+
/**
|
|
119
|
+
* Gets the text content of the element.
|
|
120
|
+
* @returns The text content.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* const para = await twd.get("p");
|
|
125
|
+
* const content = para.text();
|
|
126
|
+
* console.log(content);
|
|
127
|
+
*
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
*/
|
|
131
|
+
text: () => string;
|
|
132
|
+
/**
|
|
133
|
+
* Asserts something about the element.
|
|
134
|
+
* @param name The name of the assertion.
|
|
135
|
+
* @param args Arguments for the assertion.
|
|
136
|
+
* @returns The same API for chaining.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const btn = await twd.get("button");
|
|
141
|
+
* btn.should("have.text", "Click me").should("not.be.disabled");
|
|
142
|
+
*
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
*/
|
|
146
|
+
should: ShouldFn;
|
|
147
|
+
}
|
package/dist/twd.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Options, Rule } from './commands/mockResponses';
|
|
2
|
+
import { TWDElemAPI } from './twd-types';
|
|
3
|
+
import { URLCommandAPI } from './commands/url';
|
|
4
|
+
/**
|
|
5
|
+
* Stores the function to run before each test.
|
|
6
|
+
*/
|
|
7
|
+
declare let beforeEachFn: (() => void | Promise<void>) | null;
|
|
8
|
+
/**
|
|
9
|
+
* Registers a function to run before each test.
|
|
10
|
+
* @example
|
|
11
|
+
* beforeEach(() => { ... });
|
|
12
|
+
*/
|
|
13
|
+
export declare const beforeEach: (fn: typeof beforeEachFn) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Groups related tests together.
|
|
16
|
+
* @example
|
|
17
|
+
* describe("My group", () => { ... });
|
|
18
|
+
*/
|
|
19
|
+
export declare const describe: (name: string, fn: () => void) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Defines a test case.
|
|
22
|
+
* @example
|
|
23
|
+
* it("does something", async () => { ... });
|
|
24
|
+
*/
|
|
25
|
+
export declare const it: (name: string, fn: () => Promise<void> | void) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Defines an exclusive test case (only this runs).
|
|
28
|
+
* @example
|
|
29
|
+
* itOnly("runs only this", async () => { ... });
|
|
30
|
+
*/
|
|
31
|
+
export declare const itOnly: (name: string, fn: () => Promise<void> | void) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Skips a test case.
|
|
34
|
+
* @example
|
|
35
|
+
* itSkip("skipped test", () => { ... });
|
|
36
|
+
*/
|
|
37
|
+
export declare const itSkip: (name: string, _fn: () => Promise<void> | void) => void;
|
|
38
|
+
interface TWDAPI {
|
|
39
|
+
/**
|
|
40
|
+
* Finds an element by selector and returns the TWD API for it.
|
|
41
|
+
* @param selector CSS selector
|
|
42
|
+
* @returns {Promise<TWDElemAPI>} The TWD API for the element
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const btn = await twd.get("button");
|
|
47
|
+
*
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
get: (selector: string) => Promise<TWDElemAPI>;
|
|
52
|
+
/**
|
|
53
|
+
* Simulates visiting a URL (SPA navigation).
|
|
54
|
+
* @param url The URL to visit
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* twd.visit("/contact");
|
|
59
|
+
*
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
visit: (url: string) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Mock a network request.
|
|
65
|
+
*
|
|
66
|
+
* @param alias Identifier for the mock rule. Useful for `waitFor()`.
|
|
67
|
+
* @param options Options to configure the mock:
|
|
68
|
+
* - `method`: HTTP method ("GET", "POST", …)
|
|
69
|
+
* - `url`: URL string or RegExp to match
|
|
70
|
+
* - `response`: Body of the mocked response
|
|
71
|
+
* - `status`: (optional) HTTP status code (default: 200)
|
|
72
|
+
* - `headers`: (optional) Response headers
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* mockRequest("getUser", {
|
|
77
|
+
* method: "GET",
|
|
78
|
+
* url: /\/api\/user\/\d+/,
|
|
79
|
+
* response: { id: 1, name: "Kevin" },
|
|
80
|
+
* status: 200,
|
|
81
|
+
* headers: { "x-mock": "true" }
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
mockRequest: (alias: string, options: Options) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Wait for a mocked request to be made.
|
|
88
|
+
* @param alias The alias of the mock rule to wait for
|
|
89
|
+
* @return The matched rule (with body if applicable)
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const rule = await twd.waitFor("aliasId");
|
|
94
|
+
* console.log(rule.body);
|
|
95
|
+
*
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
waitFor: (alias: string) => Promise<Rule>;
|
|
99
|
+
/**
|
|
100
|
+
* URL-related assertions.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* twd.url().should("eq", "http://localhost:3000/contact");
|
|
105
|
+
* twd.url().should("contain.url", "/contact");
|
|
106
|
+
*
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
url: () => URLCommandAPI;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Mini Cypress-style helpers for DOM testing.
|
|
113
|
+
* @namespace twd
|
|
114
|
+
*/
|
|
115
|
+
export declare const twd: TWDAPI;
|
|
116
|
+
export {};
|