twd-js 1.7.1 → 1.7.3

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.
Files changed (59) hide show
  1. package/README.md +26 -4
  2. package/dist/asserts/index.d.ts +2 -0
  3. package/dist/bundled.d.ts +4 -1
  4. package/dist/bundled.es.js +9861 -602
  5. package/dist/cli.js +11 -11
  6. package/dist/commands/mockBridge.d.ts +117 -0
  7. package/dist/commands/url.d.ts +33 -0
  8. package/dist/commands/viewport.d.ts +2 -0
  9. package/dist/commands/visit.d.ts +1 -0
  10. package/dist/constants/version.d.ts +1 -0
  11. package/dist/global.d.ts +20 -0
  12. package/dist/index.cjs.js +33 -33
  13. package/dist/index.d.ts +8 -664
  14. package/dist/index.es.js +1695 -1657
  15. package/dist/initializers/initSidebar.d.ts +29 -0
  16. package/dist/initializers/initTests.d.ts +36 -0
  17. package/dist/mock-sw.js +1 -1
  18. package/dist/plugin/removeMockServiceWorker.d.ts +18 -0
  19. package/dist/plugin/twdHmr.d.ts +45 -0
  20. package/dist/proxies/domMessage.d.ts +1 -0
  21. package/dist/proxies/eventsMessage.d.ts +1 -0
  22. package/dist/proxies/screenDom.d.ts +45 -0
  23. package/dist/proxies/userEvent.d.ts +4 -0
  24. package/dist/runner-ci.d.ts +12 -28
  25. package/dist/runner.d.ts +52 -63
  26. package/dist/twd-types.d.ts +120 -0
  27. package/dist/twd.d.ts +291 -0
  28. package/dist/ui/ClosedSidebar.d.ts +6 -0
  29. package/dist/ui/Icons/BaseIcon.d.ts +7 -0
  30. package/dist/ui/Icons/ChevronDown.d.ts +2 -0
  31. package/dist/ui/Icons/ChevronRight.d.ts +2 -0
  32. package/dist/ui/Icons/Loader.d.ts +2 -0
  33. package/dist/ui/Icons/MockRequestIcon.d.ts +2 -0
  34. package/dist/ui/Icons/Play.d.ts +2 -0
  35. package/dist/ui/LogItem.d.ts +6 -0
  36. package/dist/ui/MockRulesButton.d.ts +1 -0
  37. package/dist/ui/MockedComponent.d.ts +6 -0
  38. package/dist/ui/SearchInput.d.ts +6 -0
  39. package/dist/ui/SkipOnlyName.d.ts +8 -0
  40. package/dist/ui/TWDSidebar.d.ts +20 -0
  41. package/dist/ui/TestList.d.ts +8 -0
  42. package/dist/ui/TestListItem.d.ts +19 -0
  43. package/dist/ui/componentMocks.d.ts +3 -0
  44. package/dist/ui/hooks/useLayout.d.ts +6 -0
  45. package/dist/ui/index.d.ts +1 -0
  46. package/dist/ui/utils/buildTreeFromHandlers.d.ts +16 -0
  47. package/dist/ui/utils/chaiErrorFormat.d.ts +22 -0
  48. package/dist/ui/utils/filterTree.d.ts +2 -0
  49. package/dist/ui/utils/formatLogs.d.ts +30 -0
  50. package/dist/ui/utils/screenReaderMessages.d.ts +9 -0
  51. package/dist/ui/utils/styles.d.ts +7 -0
  52. package/dist/ui/utils/theme.d.ts +74 -0
  53. package/dist/ui.d.ts +2 -10
  54. package/dist/utils/assertionMessage.d.ts +1 -0
  55. package/dist/utils/log.d.ts +1 -0
  56. package/dist/utils/wait.d.ts +3 -0
  57. package/dist/utils/waitFor.d.ts +2 -0
  58. package/dist/vite-plugin.d.ts +2 -67
  59. package/package.json +31 -7
package/dist/twd.d.ts ADDED
@@ -0,0 +1,291 @@
1
+ import { Options, Rule } from './commands/mockBridge';
2
+ import { AnyAssertion, ArgsFor, TWDElemAPI, WaitForOptions } from './twd-types';
3
+ import { URLCommandAPI } from './commands/url';
4
+ interface TWDAPI {
5
+ /**
6
+ * Finds an element by selector and returns the TWD API for it.
7
+ * @param selector CSS selector
8
+ * @returns {Promise<TWDElemAPI>} The TWD API for the element
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const btn = await twd.get("button");
13
+ *
14
+ * ```
15
+ *
16
+ */
17
+ get: (selector: string) => Promise<TWDElemAPI>;
18
+ /**
19
+ * Sets the value of an input element and dispatches an input event. We recommend using this only for range, color, time inputs.
20
+ * @param el The input element
21
+ * @param value The value to set
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const input = await twd.get("input[type='time']");
26
+ * twd.setInputValue(input.el, "13:30");
27
+ *
28
+ * ```
29
+ */
30
+ setInputValue: (el: Element, value: string) => void;
31
+ /**
32
+ * Finds multiple elements by selector and returns an array of TWD APIs for them.
33
+ * @param selector CSS selector
34
+ * @returns {Promise<TWDElemAPI[]>} Array of TWD APIs for the elements
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const items = await twd.getAll(".item");
39
+ * items.at(0).should("be.visible");
40
+ * items.at(1).should("contain.text", "Hello");
41
+ * expect(items).to.have.length(3);
42
+ * ```
43
+ */
44
+ getAll: (selector: string) => Promise<TWDElemAPI[]>;
45
+ /**
46
+ * Simulates visiting a URL (SPA navigation).
47
+ * @param url The URL to visit
48
+ * @param [reload] Whether to force a reload even if already on the URL (optional)
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * twd.visit("/contact");
53
+ * // visit with reload
54
+ * twd.visit("/contact", true);
55
+ * ```
56
+ */
57
+ visit: (url: string, reload?: boolean) => Promise<void>;
58
+ /**
59
+ * Mock a network request.
60
+ *
61
+ * @param alias Identifier for the mock rule. Useful for `waitForRequest()`.
62
+ * @param options Options to configure the mock:
63
+ * - `method`: HTTP method ("GET", "POST", …)
64
+ * - `url`: URL string or RegExp to match
65
+ * - `response`: Body of the mocked response
66
+ * - `status`: (optional) HTTP status code (default: 200)
67
+ * - `responseHeaders`: (optional) Response headers
68
+ * - `delay`: (optional) Delay in ms before returning the response
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * mockRequest("getUser", {
73
+ * method: "GET",
74
+ * url: /\/api\/user\/\d+/,
75
+ * response: { id: 1, name: "Kevin" },
76
+ * status: 200,
77
+ * responseHeaders: { "x-mock": "true" }
78
+ * });
79
+ * ```
80
+ */
81
+ mockRequest: (alias: string, options: Options) => Promise<void>;
82
+ /**
83
+ * Wait for a mocked request to be made.
84
+ * @param alias The alias of the mock rule to wait for
85
+ * @param retries The number of retries to make
86
+ * @param retryDelay The delay between retries
87
+ * @return The matched rule (with body if applicable)
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const rule = await twd.waitForRequest("aliasId");
92
+ * console.log(rule.body);
93
+ * const rule = await twd.waitForRequest("aliasId", 5, 100);
94
+ * console.log(rule.body);
95
+ *
96
+ * ```
97
+ */
98
+ waitForRequest: (alias: string, retries?: number, retryDelay?: number) => Promise<Rule>;
99
+ /**
100
+ * wait for a list of mocked requests to be made.
101
+ * @param aliases The aliases of the mock rules to wait for
102
+ * @returns The matched rules (with body if applicable)
103
+ * @example
104
+ * ```ts
105
+ * const rules = await waitForRequests(["getUser", "postComment"]);
106
+ * ```
107
+ */
108
+ waitForRequests: (aliases: string[]) => Promise<Rule[]>;
109
+ /**
110
+ * URL-related assertions.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * twd.url().should("eq", "http://localhost:3000/contact");
115
+ * twd.url().should("contain.url", "/contact");
116
+ *
117
+ * ```
118
+ */
119
+ url: () => URLCommandAPI;
120
+ /**
121
+ * Initializes request mocking (registers the service worker).
122
+ * @param [path] service worker absolute path (optional)
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * await twd.initRequestMocking();
127
+ * // init with custom service worker path
128
+ * await twd.initRequestMocking('/test-path/mock-sw.js');
129
+ * ```
130
+ */
131
+ initRequestMocking: (path?: string) => Promise<void>;
132
+ /**
133
+ * Clears all request mock rules.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * twd.clearRequestMockRules();
138
+ *
139
+ * ```
140
+ */
141
+ clearRequestMockRules: () => void;
142
+ /**
143
+ * Gets all current request mock rules.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const rules = twd.getRequestMockRules();
148
+ * console.log(rules);
149
+ * ```
150
+ */
151
+ getRequestMockRules: () => Rule[];
152
+ /**
153
+ * Gets the number of times a specific mock rule was hit.
154
+ * @param alias The alias of the mock rule
155
+ * @returns The number of times the rule was matched
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * const count = twd.getRequestCount("getUser");
160
+ * expect(count).to.equal(2);
161
+ * ```
162
+ */
163
+ getRequestCount: (alias: string) => number;
164
+ /**
165
+ * Gets a snapshot of all mock rule hit counts.
166
+ * @returns An object mapping rule aliases to their hit counts
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * const counts = twd.getRequestCounts();
171
+ * expect(counts).to.deep.equal({ getUser: 2, listPosts: 1 });
172
+ * ```
173
+ */
174
+ getRequestCounts: () => Record<string, number>;
175
+ /**
176
+ * Waits for a specified time.
177
+ * @param time Time in milliseconds to wait
178
+ * @returns A promise that resolves after the specified time
179
+ * @example
180
+ * ```ts
181
+ * await twd.wait(500); // wait for 500ms
182
+ * ```
183
+ */
184
+ wait: (time: number) => Promise<void>;
185
+ /**
186
+ * Retries a callback until it stops throwing or the timeout expires.
187
+ * Use this instead of `twd.wait(ms)` to wait for conditions rather than fixed delays.
188
+ *
189
+ * @param callback Function to retry — can be sync or async. Should throw if the condition is not yet met.
190
+ * @param options Optional timeout, interval, and message settings
191
+ * @returns A promise that resolves with the callback's return value when it succeeds
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * // Wait for an analytics event and return it
196
+ * const event = await twd.waitFor(() => {
197
+ * const ev = findEvent("purchase");
198
+ * expect(ev).to.exist;
199
+ * return ev;
200
+ * }, { message: "purchase event to fire" });
201
+ *
202
+ * // Wait for an element (single expression)
203
+ * const heading = await twd.waitFor(() => screenDom.getByRole("heading", { name: /checkout/i }));
204
+ *
205
+ * // Fire-and-forget (void) — still works as before
206
+ * await twd.waitFor(() => {
207
+ * expect(submitButton.disabled).to.be.false;
208
+ * });
209
+ * ```
210
+ */
211
+ waitFor: <T>(callback: () => T | Promise<T>, options?: WaitForOptions) => Promise<T>;
212
+ /**
213
+ * Asserts something about the element.
214
+ * @param el The element to assert on
215
+ * @param name The name of the assertion.
216
+ * @param args Arguments for the assertion.
217
+ * @returns The same API for chaining.
218
+ * @example
219
+ * ```ts
220
+ * const button = await twd.get("button");
221
+ * const text = screenDom.getByText("Hello");
222
+ * twd.should(button.el, "have.text", "Hello");
223
+ * twd.should(text, "be.empty");
224
+ * twd.should(button.el, "have.class", "active");
225
+ * ```
226
+ */
227
+ should: (el: Element, name: AnyAssertion, ...args: ArgsFor<AnyAssertion>) => void;
228
+ /**
229
+ * Mock a component.
230
+ * @param name The name of the component to mock
231
+ * @param component The component to mock
232
+ * @returns The mocked component
233
+ * @example
234
+ * ```ts
235
+ * twd.mockComponent("Button", Button);
236
+ * ```
237
+ */
238
+ mockComponent: (name: string, component: React.ComponentType<any>) => void;
239
+ /**
240
+ * Clears all component mocks.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * twd.clearComponentMocks();
245
+ * ```
246
+ */
247
+ clearComponentMocks: () => void;
248
+ /**
249
+ * Asserts that an element does not exist in the DOM.
250
+ * @param selector CSS selector of the element to check
251
+ * @returns A promise that resolves if the element does not exist, or rejects if it does
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * await twd.notExists(".non-existent");
256
+ * ```
257
+ */
258
+ notExists: (selector: string) => Promise<void>;
259
+ /**
260
+ * Simulates a viewport size by constraining body dimensions, overriding
261
+ * `window.innerWidth`/`window.innerHeight` and `window.matchMedia()`, and
262
+ * rewriting CSS `@media` rules to match the simulated dimensions.
263
+ * Call with no arguments to reset to the original viewport.
264
+ *
265
+ * @param width Viewport width in pixels
266
+ * @param height Viewport height in pixels (optional — omit to leave height unconstrained)
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * twd.viewport(375, 667); // mobile
271
+ * twd.viewport(768); // tablet width, height unconstrained
272
+ * twd.viewport(); // reset
273
+ * ```
274
+ */
275
+ viewport: (width?: number, height?: number) => void;
276
+ /**
277
+ * Resets the viewport to its original size (undoes a previous `twd.viewport()` call).
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * twd.resetViewport();
282
+ * ```
283
+ */
284
+ resetViewport: () => void;
285
+ }
286
+ /**
287
+ * Mini Cypress-style helpers for DOM testing.
288
+ * @namespace twd
289
+ */
290
+ export declare const twd: TWDAPI;
291
+ export {};
@@ -0,0 +1,6 @@
1
+ interface ClosedSidebarProps {
2
+ setOpen: (open: boolean) => void;
3
+ position: 'left' | 'right';
4
+ }
5
+ export declare const ClosedSidebar: ({ setOpen, position }: ClosedSidebarProps) => import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,7 @@
1
+ interface BaseIconProps {
2
+ className: string;
3
+ children: React.ReactNode;
4
+ dataTestId: string;
5
+ }
6
+ declare const BaseIcon: ({ className, children, dataTestId }: BaseIconProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default BaseIcon;
@@ -0,0 +1,2 @@
1
+ declare const ChevronDown: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ChevronDown;
@@ -0,0 +1,2 @@
1
+ declare const ChevronRight: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ChevronRight;
@@ -0,0 +1,2 @@
1
+ declare const Loader: () => import("react/jsx-runtime").JSX.Element;
2
+ export default Loader;
@@ -0,0 +1,2 @@
1
+ declare const MockRequestIcon: () => import("react/jsx-runtime").JSX.Element;
2
+ export default MockRequestIcon;
@@ -0,0 +1,2 @@
1
+ declare const Play: () => import("react/jsx-runtime").JSX.Element;
2
+ export default Play;
@@ -0,0 +1,6 @@
1
+ interface LogItemProps {
2
+ log: string;
3
+ index: number;
4
+ }
5
+ export declare const LogItem: ({ log, index }: LogItemProps) => import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1 @@
1
+ export declare const MockRulesButton: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ interface MockedComponentProps<TProps = any> {
2
+ name: string;
3
+ children: React.ReactElement<TProps>;
4
+ }
5
+ export declare function MockedComponent<TProps extends Record<string, any>>({ name, children, }: MockedComponentProps<TProps>): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,6 @@
1
+ interface SearchInputProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ }
5
+ export declare const SearchInput: ({ value, onChange }: SearchInputProps) => import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,8 @@
1
+ interface SkipOnlyNameProps {
2
+ id: string;
3
+ name: string;
4
+ skip?: boolean;
5
+ only?: boolean;
6
+ }
7
+ declare const SkipOnlyName: ({ id, name, skip, only }: SkipOnlyNameProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default SkipOnlyName;
@@ -0,0 +1,20 @@
1
+ interface TWDSidebarProps {
2
+ /**
3
+ * Whether the sidebar is open by default
4
+ */
5
+ open: boolean;
6
+ /**
7
+ * Sidebar position
8
+ * - left: Sidebar on the left side (default)
9
+ * - right: Sidebar on the right side
10
+ *
11
+ * @default "left"
12
+ */
13
+ position?: 'left' | 'right';
14
+ /**
15
+ * Whether to show the search/filter input
16
+ */
17
+ search?: boolean;
18
+ }
19
+ export declare const TWDSidebar: ({ open, position, search }: TWDSidebarProps) => import("react/jsx-runtime").JSX.Element;
20
+ export {};
@@ -0,0 +1,8 @@
1
+ import { Node } from './utils/buildTreeFromHandlers';
2
+ interface TestListProps {
3
+ roots: Node[];
4
+ runTest: (id: string) => Promise<void>;
5
+ searchQuery?: string;
6
+ }
7
+ export declare const TestList: ({ roots, runTest, searchQuery }: TestListProps) => import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,19 @@
1
+ interface Test {
2
+ name: string;
3
+ depth: number;
4
+ status?: 'idle' | 'pass' | 'fail' | 'skip' | 'running';
5
+ logs?: string[];
6
+ id: string;
7
+ parent?: string;
8
+ type: 'test' | 'suite';
9
+ only?: boolean;
10
+ skip?: boolean;
11
+ }
12
+ interface TestListItemProps {
13
+ node: Test;
14
+ depth: number;
15
+ id: string;
16
+ runTest: (i: string) => void;
17
+ }
18
+ export declare const TestListItem: ({ node, depth, id, runTest }: TestListItemProps) => import("react/jsx-runtime").JSX.Element;
19
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const mockComponent: <TProps extends Record<string, any>>(id: string, component: React.ComponentType<TProps>) => void;
2
+ export declare function clearComponentMocks(): void;
3
+ export declare const getMockForComponent: <TProps = any>(id: string) => React.ComponentType<TProps> | undefined;
@@ -0,0 +1,6 @@
1
+ interface UseLayoutProps {
2
+ isOpen: boolean;
3
+ position: 'left' | 'right';
4
+ }
5
+ export declare const useLayout: ({ isOpen, position }: UseLayoutProps) => void;
6
+ export {};
@@ -0,0 +1 @@
1
+ export { MockedComponent } from './MockedComponent';
@@ -0,0 +1,16 @@
1
+ interface Test {
2
+ name: string;
3
+ depth: number;
4
+ status?: 'idle' | 'pass' | 'fail' | 'skip' | 'running';
5
+ logs?: string[];
6
+ id: string;
7
+ parent?: string;
8
+ type: 'test' | 'suite';
9
+ only?: boolean;
10
+ skip?: boolean;
11
+ }
12
+ export type Node = Test & {
13
+ childrenNodes?: Node[];
14
+ };
15
+ export declare const buildTreeFromHandlers: (handlers: Test[]) => Node[];
16
+ export {};
@@ -0,0 +1,22 @@
1
+ interface ChaiAssertionError extends Error {
2
+ actual?: unknown;
3
+ expected?: unknown;
4
+ operator?: string;
5
+ showDiff?: boolean;
6
+ }
7
+ declare const isChaiAssertionError: (error: unknown) => error is ChaiAssertionError;
8
+ declare const formatChaiError: (error: ChaiAssertionError) => {
9
+ type: string;
10
+ actual: {} | null;
11
+ expected: {} | null;
12
+ operator: string | undefined;
13
+ message?: undefined;
14
+ } | {
15
+ type: string;
16
+ message: string;
17
+ actual?: undefined;
18
+ expected?: undefined;
19
+ operator?: undefined;
20
+ };
21
+ declare const printChaiError: (error: ChaiAssertionError) => void;
22
+ export { isChaiAssertionError, formatChaiError, printChaiError };
@@ -0,0 +1,2 @@
1
+ import { Node } from './buildTreeFromHandlers';
2
+ export declare const filterTree: (roots: Node[], query: string) => Node[];
@@ -0,0 +1,30 @@
1
+ export declare enum LogType {
2
+ CHAI_DIFF = "chai-diff",
3
+ CHAI_MESSAGE = "chai-message",
4
+ ERROR = "error"
5
+ }
6
+ interface ChaiDiffLog {
7
+ type: LogType.CHAI_DIFF;
8
+ expected: unknown;
9
+ actual: unknown;
10
+ }
11
+ interface ChaiMessageLog {
12
+ type: LogType.CHAI_MESSAGE;
13
+ message: string;
14
+ }
15
+ interface ErrorLog {
16
+ type: LogType.ERROR;
17
+ message: string;
18
+ }
19
+ export type ParsedLog = ChaiDiffLog | ChaiMessageLog | ErrorLog;
20
+ export declare const parseLogEntry: (log: string) => ParsedLog | null;
21
+ export declare const formatValue: (value: unknown) => string;
22
+ export declare const assertStyles: (text: string) => {
23
+ color: string;
24
+ fontWeight: string;
25
+ } | {
26
+ color?: undefined;
27
+ fontWeight?: undefined;
28
+ };
29
+ export declare const getDisplayText: (log: string, parsedLog: ParsedLog | null) => string;
30
+ export {};
@@ -0,0 +1,9 @@
1
+ import { Handler } from '../../runner';
2
+ /**
3
+ * Generate screen reader message for a specific test result
4
+ */
5
+ export declare const displaySRMessageSpecificTest: (test: Handler) => string;
6
+ /**
7
+ * Generate screen reader message for all tests result
8
+ */
9
+ export declare const displaySRMessageAllTests: (tests: Handler[]) => string;
@@ -0,0 +1,7 @@
1
+ export declare const CSS_STYLES = "\n/* ========================\n Animation keyframes\n ======================== */\n@keyframes twd-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n/* ========================\n Buttons \u2014 shared base\n ======================== */\n.twd-btn {\n cursor: pointer;\n border-radius: var(--twd-border-radius);\n transition: all var(--twd-animation-duration) ease;\n}\n.twd-btn:hover { filter: brightness(1.15); }\n.twd-btn:active { filter: brightness(0.9); }\n.twd-btn:focus-visible {\n outline: 2px solid var(--twd-primary);\n outline-offset: 2px;\n}\n.twd-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.twd-btn:disabled:hover { filter: none; }\n\n/* ========================\n Button variants\n ======================== */\n.twd-btn-primary {\n background: var(--twd-button-primary);\n color: var(--twd-button-primary-text);\n padding: var(--twd-spacing-xs) var(--twd-spacing-md);\n border: none;\n}\n.twd-btn-secondary {\n background: var(--twd-button-secondary);\n color: var(--twd-button-secondary-text);\n padding: var(--twd-spacing-xs) var(--twd-spacing-md);\n border: 1px solid var(--twd-button-border);\n}\n.twd-btn-icon {\n background: transparent;\n border: 1px solid var(--twd-border-light);\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n vertical-align: middle;\n font-size: var(--twd-font-size-sm);\n}\n.twd-btn-mock-rules {\n background: var(--twd-button-secondary);\n border: 1px solid var(--twd-button-border);\n border-radius: var(--twd-border-radius-lg);\n padding: var(--twd-spacing-md) var(--twd-spacing-lg);\n font-size: var(--twd-font-size-sm);\n color: var(--twd-button-secondary-text);\n display: flex;\n align-items: center;\n gap: var(--twd-spacing-md);\n margin-bottom: 10px;\n width: 100%;\n text-align: left;\n transition: all var(--twd-animation-duration) ease;\n box-shadow: var(--twd-shadow-sm);\n}\n\n/* ========================\n Sidebar layout\n ======================== */\n.twd-sidebar {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: fixed;\n top: 0;\n bottom: 0;\n width: var(--twd-sidebar-width);\n background: var(--twd-background);\n font-size: var(--twd-font-size-md);\n overflow-y: auto;\n box-shadow: var(--twd-shadow);\n text-align: left;\n z-index: var(--twd-z-index-sidebar);\n pointer-events: all;\n isolation: isolate;\n}\n.twd-sidebar-header {\n padding: var(--twd-spacing-md);\n background: var(--twd-background);\n position: sticky;\n top: 0;\n z-index: var(--twd-z-index-sticky);\n border-bottom: 1px solid var(--twd-border);\n}\n.twd-sidebar-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: var(--twd-spacing-xl);\n}\n.twd-sidebar-header-buttons {\n display: flex;\n gap: var(--twd-spacing-xs);\n}\n.twd-sidebar-stats {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: var(--twd-font-size-md);\n color: var(--twd-text-secondary);\n margin-bottom: 10px;\n}\n.twd-sidebar-stats-counts {\n display: flex;\n gap: var(--twd-spacing-xs);\n}\n.twd-sidebar-content {\n padding: var(--twd-spacing-md);\n}\n.twd-sidebar-closed {\n position: fixed;\n top: 50%;\n transform: translateY(-50%);\n z-index: var(--twd-z-index-sidebar);\n background: var(--twd-button-primary);\n color: var(--twd-button-primary-text);\n padding: var(--twd-spacing-sm) 10px;\n font-size: var(--twd-font-size-sm);\n}\n.twd-sidebar-version {\n color: var(--twd-text-secondary);\n font-size: var(--twd-font-size-sm);\n align-self: center;\n}\n\n/* ========================\n Test list\n ======================== */\n.twd-test-list {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n.twd-test-group {\n background: var(--twd-describe-bg);\n border-left: 3px solid var(--twd-describe-border);\n border-radius: var(--twd-border-radius);\n padding: var(--twd-spacing-xs) var(--twd-spacing-sm);\n margin-bottom: var(--twd-spacing-sm);\n}\n.twd-test-group-toggle {\n font-weight: var(--twd-font-weight-medium);\n font-size: var(--twd-font-size-sm);\n cursor: pointer;\n color: var(--twd-describe-text);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--twd-spacing-sm);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.twd-test-item {\n display: flex;\n align-items: left;\n justify-content: space-between;\n padding: var(--twd-spacing-sm) var(--twd-spacing-sm);\n border-radius: var(--twd-border-radius);\n}\n.twd-test-item-name {\n font-weight: var(--twd-font-weight-medium);\n font-size: var(--twd-font-size-md);\n color: var(--twd-text);\n max-width: 220px;\n}\n.twd-test-item-logs {\n border-radius: var(--twd-border-radius);\n max-height: 260px;\n overflow-y: auto;\n padding: 0;\n background: var(--twd-background-secondary);\n list-style: none;\n margin-top: var(--twd-spacing-xs);\n text-align: left;\n}\n\n/* ========================\n Status variants\n ======================== */\n.twd-status-pass { background: var(--twd-success-bg); }\n.twd-status-fail { background: var(--twd-error-bg); }\n.twd-status-skip { background: var(--twd-skip-bg); }\n.twd-status-running { background: var(--twd-warning-bg); }\n\n/* ========================\n Search\n ======================== */\n.twd-search-wrapper {\n position: relative;\n margin-bottom: var(--twd-spacing-md);\n}\n.twd-search-input {\n width: 100%;\n padding: var(--twd-spacing-md);\n background: var(--twd-background);\n color: var(--twd-text);\n border: 1px solid var(--twd-border);\n border-radius: var(--twd-border-radius);\n font-size: var(--twd-font-size-md);\n box-sizing: border-box;\n}\n.twd-search-input:focus-visible {\n outline: 2px solid var(--twd-primary);\n outline-offset: 2px;\n}\n\n/* ========================\n Loader\n ======================== */\n.twd-loader {\n animation: twd-spin 1s linear infinite;\n}\n";
2
+ /**
3
+ * Injects all sidebar CSS classes into the document.
4
+ * Guards against double-injection (same pattern as injectTheme).
5
+ * Called once from initSidebar.tsx after injectTheme().
6
+ */
7
+ export declare function injectStyles(): void;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * TWD Theme Configuration
3
+ *
4
+ * This file defines CSS variables that can be customized by users
5
+ * to personalize their TWD UI experience.
6
+ *
7
+ * Users can override these variables by setting them in their CSS:
8
+ *
9
+ * ```css
10
+ * :root {
11
+ * --twd-primary: #2563eb;
12
+ * --twd-background: #1e293b;
13
+ * ... other variables
14
+ * }
15
+ *
16
+ */
17
+ export interface TWDTheme {
18
+ primary: string;
19
+ background: string;
20
+ backgroundSecondary: string;
21
+ border: string;
22
+ borderLight: string;
23
+ text: string;
24
+ textSecondary: string;
25
+ textMuted: string;
26
+ describeBg: string;
27
+ describeText: string;
28
+ describeBorder: string;
29
+ success: string;
30
+ successBg: string;
31
+ error: string;
32
+ errorBg: string;
33
+ warning: string;
34
+ warningBg: string;
35
+ skip: string;
36
+ skipBg: string;
37
+ buttonPrimary: string;
38
+ buttonPrimaryText: string;
39
+ buttonSecondary: string;
40
+ buttonSecondaryText: string;
41
+ buttonBorder: string;
42
+ spacingXs: string;
43
+ spacingSm: string;
44
+ spacingMd: string;
45
+ spacingLg: string;
46
+ spacingXl: string;
47
+ fontSizeXs: string;
48
+ fontSizeSm: string;
49
+ fontSizeMd: string;
50
+ fontSizeLg: string;
51
+ fontWeightNormal: string;
52
+ fontWeightMedium: string;
53
+ fontWeightBold: string;
54
+ sidebarWidth: string;
55
+ borderRadius: string;
56
+ borderRadiusLg: string;
57
+ shadow: string;
58
+ shadowSm: string;
59
+ zIndexSidebar: string;
60
+ zIndexSticky: string;
61
+ animationDuration: string;
62
+ iconColor: string;
63
+ iconColorSecondary: string;
64
+ }
65
+ export declare const defaultTheme: TWDTheme;
66
+ /**
67
+ * Converts theme object to CSS variables string
68
+ */
69
+ export declare function themeToCSSVariables(theme?: Partial<TWDTheme>): string;
70
+ /**
71
+ * Injects theme CSS variables into the document
72
+ * This should be called once when the sidebar is initialized
73
+ */
74
+ export declare function injectTheme(theme?: Partial<TWDTheme>): void;
package/dist/ui.d.ts CHANGED
@@ -1,10 +1,2 @@
1
- import { JSX } from 'react/jsx-runtime';
2
-
3
- export declare function MockedComponent<TProps extends Record<string, any>>({ name, children, }: MockedComponentProps<TProps>): JSX.Element;
4
-
5
- declare interface MockedComponentProps<TProps = any> {
6
- name: string;
7
- children: React.ReactElement<TProps>;
8
- }
9
-
10
- export { }
1
+ export * from './ui/index'
2
+ export {}
@@ -0,0 +1 @@
1
+ export declare const assertionMessage: (valid: boolean, isNegated: boolean, correctMessage: string, errorMessage: string) => string;
@@ -0,0 +1 @@
1
+ export declare const log: (msg: string) => void;
@@ -0,0 +1,3 @@
1
+ export declare const waitForElement: (selector: string, fn: () => HTMLElement | null, timeout?: number, interval?: number) => Promise<HTMLElement>;
2
+ export declare const waitForElements: (selector: string, fn: () => NodeListOf<Element> | null, timeout?: number, interval?: number) => Promise<Element[]>;
3
+ export declare const wait: (time: number) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ import { WaitForOptions } from '../twd-types';
2
+ export declare const waitFor: <T>(callback: () => T | Promise<T>, options?: WaitForOptions) => Promise<T>;