@esimplicitylabs/katalyst-xspec 0.6.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/LICENSE +7 -0
- package/README.md +69 -0
- package/bin/katalyst-xspec.cjs +54 -0
- package/cli/init.cjs +679 -0
- package/cli/stubs.cjs +365 -0
- package/cli/upgrade.cjs +1014 -0
- package/dist/chunk-ACAXOGKZ.js +1611 -0
- package/dist/index.d.ts +881 -0
- package/dist/index.js +1091 -0
- package/dist/steps/index.d.ts +151 -0
- package/dist/steps/index.js +50 -0
- package/package.json +80 -0
- package/scripts/postinstall.cjs +85 -0
- package/skills/katalyst-bdd-architecture/SKILL.md +517 -0
- package/skills/katalyst-bdd-architecture/references/adapters.md +310 -0
- package/skills/katalyst-bdd-architecture/references/custom-steps.md +360 -0
- package/skills/katalyst-bdd-architecture/references/ports.md +256 -0
- package/skills/katalyst-bdd-create-test/SKILL.md +366 -0
- package/skills/katalyst-bdd-create-test/references/api-patterns.md +371 -0
- package/skills/katalyst-bdd-create-test/references/hybrid-patterns.md +420 -0
- package/skills/katalyst-bdd-create-test/references/tui-patterns.md +458 -0
- package/skills/katalyst-bdd-create-test/references/ui-patterns.md +415 -0
- package/skills/katalyst-bdd-quickstart/SKILL.md +292 -0
- package/skills/katalyst-bdd-step-reference/SKILL.md +147 -0
- package/skills/katalyst-bdd-step-reference/references/api-steps.md +247 -0
- package/skills/katalyst-bdd-step-reference/references/shared-steps.md +340 -0
- package/skills/katalyst-bdd-step-reference/references/tui-steps.md +483 -0
- package/skills/katalyst-bdd-step-reference/references/ui-steps.md +521 -0
- package/skills/katalyst-bdd-troubleshooting/SKILL.md +449 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,881 @@
|
|
|
1
|
+
import * as playwright_bdd from 'playwright-bdd';
|
|
2
|
+
export { test as baseTest } from 'playwright-bdd';
|
|
3
|
+
import * as _playwright_test from '@playwright/test';
|
|
4
|
+
import { APIResponse, PlaywrightTestArgs, PlaywrightWorkerArgs, APIRequestContext, Page } from '@playwright/test';
|
|
5
|
+
export { isFlagEnabled, registerAllSteps, registerApiAssertionSteps, registerApiAuthSteps, registerApiHttpSteps, registerApiSteps, registerDebugSteps, registerFlagSteps, registerFormSteps, registerHybridSteps, registerHybridSuite, registerLayoutSteps, registerSharedCleanupSteps, registerSharedSteps, registerSharedVarSteps, registerTuiBasicSteps, registerTuiSteps, registerTuiWizardSteps, registerUiAuthSteps, registerUiBasicSteps, registerUiSteps, registerWizardSteps, setFlag } from './steps/index.js';
|
|
6
|
+
|
|
7
|
+
type CleanupItem = {
|
|
8
|
+
method: 'DELETE' | 'POST' | 'PATCH' | 'PUT';
|
|
9
|
+
path: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
body?: unknown;
|
|
12
|
+
};
|
|
13
|
+
type World = {
|
|
14
|
+
vars: Record<string, string>;
|
|
15
|
+
headers: Record<string, string>;
|
|
16
|
+
cleanup: CleanupItem[];
|
|
17
|
+
skipCleanup?: boolean;
|
|
18
|
+
lastResponse?: APIResponse;
|
|
19
|
+
lastStatus?: number;
|
|
20
|
+
lastText?: string;
|
|
21
|
+
lastJson?: unknown;
|
|
22
|
+
lastHeaders?: Record<string, string>;
|
|
23
|
+
lastContentType?: string;
|
|
24
|
+
};
|
|
25
|
+
declare function initWorld(): World;
|
|
26
|
+
|
|
27
|
+
type ApiResult = {
|
|
28
|
+
status: number;
|
|
29
|
+
text: string;
|
|
30
|
+
json?: unknown;
|
|
31
|
+
headers: Record<string, string>;
|
|
32
|
+
contentType?: string;
|
|
33
|
+
response: APIResponse;
|
|
34
|
+
};
|
|
35
|
+
type ApiMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
36
|
+
interface ApiPort {
|
|
37
|
+
sendJson(method: ApiMethod, path: string, body?: unknown, headers?: Record<string, string>): Promise<ApiResult>;
|
|
38
|
+
sendForm(method: 'POST' | 'PUT' | 'PATCH', path: string, form: Record<string, string>, headers?: Record<string, string>): Promise<ApiResult>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type UiClickMode = 'click' | 'dispatch click' | 'force click' | 'force dispatch click';
|
|
42
|
+
type UiInputMode = 'type' | 'fill' | 'choose';
|
|
43
|
+
type UiUrlAssertMode = 'contains' | 'doesntContain' | 'equals';
|
|
44
|
+
type UiLocatorMethod = 'text' | 'label' | 'placeholder' | 'role' | 'test ID' | 'alternative text' | 'title' | 'locator';
|
|
45
|
+
type UiElementState = 'visible' | 'hidden' | 'editable' | 'disabled' | 'enabled' | 'read-only';
|
|
46
|
+
interface UiPort {
|
|
47
|
+
goto(path: string): Promise<void>;
|
|
48
|
+
clickButton(name: string): Promise<void>;
|
|
49
|
+
clickLink(name: string): Promise<void>;
|
|
50
|
+
fillPlaceholder(placeholder: string, value: string): Promise<void>;
|
|
51
|
+
fillLabel(label: string, value: string): Promise<void>;
|
|
52
|
+
expectText(text: string): Promise<void>;
|
|
53
|
+
expectUrlContains(part: string): Promise<void>;
|
|
54
|
+
goBack(): Promise<void>;
|
|
55
|
+
reload(): Promise<void>;
|
|
56
|
+
waitSeconds(seconds: number): Promise<void>;
|
|
57
|
+
waitForPageLoad(): Promise<void>;
|
|
58
|
+
getCurrentUrl(): Promise<string>;
|
|
59
|
+
zoomTo(scale: number): Promise<void>;
|
|
60
|
+
typeText(text: string): Promise<void>;
|
|
61
|
+
pressKey(key: string): Promise<void>;
|
|
62
|
+
clickElementThatContains(clickMode: UiClickMode, elementType: string, text: string): Promise<void>;
|
|
63
|
+
clickElementWith(clickMode: UiClickMode, ordinal: string, text: string, method: UiLocatorMethod): Promise<void>;
|
|
64
|
+
fillDropdown(value: string, dropdownLabel: string): Promise<void>;
|
|
65
|
+
inputInElement(action: UiInputMode, value: string, ordinal: string, text: string, method: UiLocatorMethod): Promise<void>;
|
|
66
|
+
expectUrl(mode: UiUrlAssertMode, expected: string): Promise<void>;
|
|
67
|
+
expectNewTabUrl(mode: UiUrlAssertMode, expected: string): Promise<void>;
|
|
68
|
+
expectElementWithTextVisible(elementType: string, text: string, shouldBeVisible: boolean): Promise<void>;
|
|
69
|
+
expectElementState(ordinal: string, text: string, method: UiLocatorMethod, state: UiElementState): Promise<void>;
|
|
70
|
+
expectElementStateWithin(ordinal: string, text: string, method: UiLocatorMethod, state: UiElementState, seconds: number): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface AuthPort {
|
|
74
|
+
apiLoginAsAdmin(world: World): Promise<void>;
|
|
75
|
+
apiLoginAsUser(world: World): Promise<void>;
|
|
76
|
+
apiSetBearer(world: World, token: string): void;
|
|
77
|
+
uiLoginAsAdmin(world: World): Promise<void>;
|
|
78
|
+
uiLoginAsUser(world: World): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface CleanupPort {
|
|
82
|
+
registerFromVar(world: World, varName: string, id: unknown, meta?: unknown): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* TUI Port Interface
|
|
87
|
+
*
|
|
88
|
+
* Defines the contract for terminal user interface testing operations.
|
|
89
|
+
* Aligned with UiPort patterns for consistency across testing approaches.
|
|
90
|
+
*/
|
|
91
|
+
/** Keyboard modifier keys for key combinations */
|
|
92
|
+
type TuiKeyModifiers = {
|
|
93
|
+
ctrl?: boolean;
|
|
94
|
+
alt?: boolean;
|
|
95
|
+
shift?: boolean;
|
|
96
|
+
meta?: boolean;
|
|
97
|
+
};
|
|
98
|
+
/** Options for wait operations */
|
|
99
|
+
type TuiWaitOptions = {
|
|
100
|
+
/** Maximum time to wait in milliseconds */
|
|
101
|
+
timeout?: number;
|
|
102
|
+
/** Polling interval in milliseconds */
|
|
103
|
+
interval?: number;
|
|
104
|
+
};
|
|
105
|
+
/** Captured screen state */
|
|
106
|
+
type TuiScreenCapture = {
|
|
107
|
+
/** Full screen text content */
|
|
108
|
+
text: string;
|
|
109
|
+
/** Screen content split by lines */
|
|
110
|
+
lines: string[];
|
|
111
|
+
/** Timestamp when capture was taken */
|
|
112
|
+
timestamp: number;
|
|
113
|
+
/** Terminal dimensions */
|
|
114
|
+
size: {
|
|
115
|
+
cols: number;
|
|
116
|
+
rows: number;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
/** Result of snapshot comparison */
|
|
120
|
+
type TuiSnapshotResult = {
|
|
121
|
+
/** Whether the snapshot matched */
|
|
122
|
+
pass: boolean;
|
|
123
|
+
/** Diff output if snapshot didn't match */
|
|
124
|
+
diff?: string;
|
|
125
|
+
/** Path to the snapshot file */
|
|
126
|
+
snapshotPath?: string;
|
|
127
|
+
};
|
|
128
|
+
/** Mouse button types */
|
|
129
|
+
type TuiMouseButton = 'left' | 'right' | 'middle';
|
|
130
|
+
/** Mouse event types */
|
|
131
|
+
type TuiMouseEventType = 'click' | 'down' | 'up' | 'drag' | 'scroll';
|
|
132
|
+
/** Mouse event configuration */
|
|
133
|
+
type TuiMouseEvent = {
|
|
134
|
+
type: TuiMouseEventType;
|
|
135
|
+
position: {
|
|
136
|
+
x: number;
|
|
137
|
+
y: number;
|
|
138
|
+
};
|
|
139
|
+
button?: TuiMouseButton;
|
|
140
|
+
};
|
|
141
|
+
/** Configuration for TUI adapter initialization */
|
|
142
|
+
type TuiConfig = {
|
|
143
|
+
/** Command to start the TUI application (e.g., ['node', 'cli.js']) */
|
|
144
|
+
command: string[];
|
|
145
|
+
/** Terminal size (defaults to 80x24) */
|
|
146
|
+
size?: {
|
|
147
|
+
cols: number;
|
|
148
|
+
rows: number;
|
|
149
|
+
};
|
|
150
|
+
/** Working directory for the command */
|
|
151
|
+
cwd?: string;
|
|
152
|
+
/** Environment variables */
|
|
153
|
+
env?: Record<string, string>;
|
|
154
|
+
/** Enable debug output */
|
|
155
|
+
debug?: boolean;
|
|
156
|
+
/** Directory for snapshot storage */
|
|
157
|
+
snapshotDir?: string;
|
|
158
|
+
/** Shell to use (defaults to system shell) */
|
|
159
|
+
shell?: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Port interface for terminal user interface testing.
|
|
163
|
+
*
|
|
164
|
+
* This interface follows the same patterns as UiPort to enable:
|
|
165
|
+
* - Consistent step definitions across UI and TUI tests
|
|
166
|
+
* - Easy mental model for test authors
|
|
167
|
+
* - Potential for shared/hybrid testing scenarios
|
|
168
|
+
*/
|
|
169
|
+
interface TuiPort {
|
|
170
|
+
/**
|
|
171
|
+
* Start the TUI application.
|
|
172
|
+
* Creates a new terminal session and launches the configured command.
|
|
173
|
+
*/
|
|
174
|
+
start(): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* Stop the TUI application.
|
|
177
|
+
* Terminates the terminal session and cleans up resources.
|
|
178
|
+
*/
|
|
179
|
+
stop(): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Restart the TUI application.
|
|
182
|
+
* Equivalent to stop() followed by start().
|
|
183
|
+
*/
|
|
184
|
+
restart(): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Check if the TUI application is currently running.
|
|
187
|
+
*/
|
|
188
|
+
isRunning(): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Type text into the terminal.
|
|
191
|
+
* Similar to UiPort.typeText - sends characters one by one.
|
|
192
|
+
* @param text - The text to type
|
|
193
|
+
* @param options - Optional typing options
|
|
194
|
+
*/
|
|
195
|
+
typeText(text: string, options?: {
|
|
196
|
+
delay?: number;
|
|
197
|
+
}): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Press a keyboard key, optionally with modifiers.
|
|
200
|
+
* Similar to UiPort.pressKey.
|
|
201
|
+
* @param key - Key name (e.g., 'enter', 'tab', 'up', 'down', 'f1')
|
|
202
|
+
* @param modifiers - Optional modifier keys
|
|
203
|
+
*/
|
|
204
|
+
pressKey(key: string, modifiers?: TuiKeyModifiers): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Send raw text without any interpretation.
|
|
207
|
+
* Useful for pasting content or sending special sequences.
|
|
208
|
+
* @param text - Raw text to send
|
|
209
|
+
*/
|
|
210
|
+
sendText(text: string): Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Fill a labeled field in the TUI.
|
|
213
|
+
* Navigates to the field and enters the value.
|
|
214
|
+
* Similar to UiPort.fillLabel.
|
|
215
|
+
* @param fieldLabel - The label or identifier of the field
|
|
216
|
+
* @param value - The value to enter
|
|
217
|
+
*/
|
|
218
|
+
fillField(fieldLabel: string, value: string): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* Select an option/menu item.
|
|
221
|
+
* Similar to UiPort.clickButton conceptually.
|
|
222
|
+
* @param option - The option text to select
|
|
223
|
+
*/
|
|
224
|
+
selectOption(option: string): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* Send a mouse event to the terminal.
|
|
227
|
+
* Only works if the TUI application supports mouse input.
|
|
228
|
+
* @param event - Mouse event configuration
|
|
229
|
+
*/
|
|
230
|
+
sendMouse(event: TuiMouseEvent): Promise<void>;
|
|
231
|
+
/**
|
|
232
|
+
* Click at a specific position.
|
|
233
|
+
* @param x - Column position (0-based)
|
|
234
|
+
* @param y - Row position (0-based)
|
|
235
|
+
* @param button - Mouse button (defaults to 'left')
|
|
236
|
+
*/
|
|
237
|
+
click(x: number, y: number, button?: TuiMouseButton): Promise<void>;
|
|
238
|
+
/**
|
|
239
|
+
* Click on text content in the terminal.
|
|
240
|
+
* Finds the text and clicks on its position.
|
|
241
|
+
* @param text - The text to find and click
|
|
242
|
+
*/
|
|
243
|
+
clickOnText(text: string): Promise<void>;
|
|
244
|
+
/**
|
|
245
|
+
* Assert that text is visible on the screen.
|
|
246
|
+
* Waits for the text to appear within timeout.
|
|
247
|
+
* Similar to UiPort.expectText.
|
|
248
|
+
* @param text - The expected text
|
|
249
|
+
* @param options - Wait options
|
|
250
|
+
*/
|
|
251
|
+
expectText(text: string, options?: TuiWaitOptions): Promise<void>;
|
|
252
|
+
/**
|
|
253
|
+
* Assert that text matching a pattern is visible.
|
|
254
|
+
* @param pattern - Regular expression to match
|
|
255
|
+
* @param options - Wait options
|
|
256
|
+
*/
|
|
257
|
+
expectPattern(pattern: RegExp, options?: TuiWaitOptions): Promise<void>;
|
|
258
|
+
/**
|
|
259
|
+
* Assert that text is NOT visible on the screen.
|
|
260
|
+
* @param text - The text that should not be present
|
|
261
|
+
*/
|
|
262
|
+
expectNotText(text: string): Promise<void>;
|
|
263
|
+
/**
|
|
264
|
+
* Assert the screen contains specific text (immediate check, no wait).
|
|
265
|
+
* @param text - The expected text
|
|
266
|
+
*/
|
|
267
|
+
assertScreenContains(text: string): Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Assert the screen matches a regular expression.
|
|
270
|
+
* @param pattern - Regular expression to match against screen content
|
|
271
|
+
*/
|
|
272
|
+
assertScreenMatches(pattern: RegExp): Promise<void>;
|
|
273
|
+
/**
|
|
274
|
+
* Wait for text to appear on screen.
|
|
275
|
+
* @param text - Text to wait for
|
|
276
|
+
* @param options - Wait options
|
|
277
|
+
*/
|
|
278
|
+
waitForText(text: string, options?: TuiWaitOptions): Promise<void>;
|
|
279
|
+
/**
|
|
280
|
+
* Wait for text matching pattern to appear.
|
|
281
|
+
* @param pattern - Regular expression to match
|
|
282
|
+
* @param options - Wait options
|
|
283
|
+
*/
|
|
284
|
+
waitForPattern(pattern: RegExp, options?: TuiWaitOptions): Promise<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Wait for the application to be ready.
|
|
287
|
+
* Implementation-specific (e.g., wait for prompt, initial screen).
|
|
288
|
+
*/
|
|
289
|
+
waitForReady(): Promise<void>;
|
|
290
|
+
/**
|
|
291
|
+
* Wait for a specific duration.
|
|
292
|
+
* Similar to UiPort.waitSeconds.
|
|
293
|
+
* @param seconds - Duration to wait in seconds
|
|
294
|
+
*/
|
|
295
|
+
waitSeconds(seconds: number): Promise<void>;
|
|
296
|
+
/**
|
|
297
|
+
* Capture the current screen state.
|
|
298
|
+
* @returns Screen capture with text content and metadata
|
|
299
|
+
*/
|
|
300
|
+
captureScreen(): Promise<TuiScreenCapture>;
|
|
301
|
+
/**
|
|
302
|
+
* Get the current screen text content.
|
|
303
|
+
* @returns Plain text representation of the screen
|
|
304
|
+
*/
|
|
305
|
+
getScreenText(): Promise<string>;
|
|
306
|
+
/**
|
|
307
|
+
* Get screen content as lines.
|
|
308
|
+
* @returns Array of screen lines
|
|
309
|
+
*/
|
|
310
|
+
getScreenLines(): Promise<string[]>;
|
|
311
|
+
/**
|
|
312
|
+
* Take a snapshot of the current screen state.
|
|
313
|
+
* Saves to the configured snapshot directory.
|
|
314
|
+
* @param name - Snapshot identifier
|
|
315
|
+
*/
|
|
316
|
+
takeSnapshot(name: string): Promise<void>;
|
|
317
|
+
/**
|
|
318
|
+
* Compare current screen against a saved snapshot.
|
|
319
|
+
* @param name - Snapshot identifier to compare against
|
|
320
|
+
* @returns Comparison result with pass/fail and diff
|
|
321
|
+
*/
|
|
322
|
+
matchSnapshot(name: string): Promise<TuiSnapshotResult>;
|
|
323
|
+
/**
|
|
324
|
+
* Clear the terminal screen.
|
|
325
|
+
*/
|
|
326
|
+
clear(): Promise<void>;
|
|
327
|
+
/**
|
|
328
|
+
* Resize the terminal.
|
|
329
|
+
* @param size - New dimensions
|
|
330
|
+
*/
|
|
331
|
+
resize(size: {
|
|
332
|
+
cols: number;
|
|
333
|
+
rows: number;
|
|
334
|
+
}): Promise<void>;
|
|
335
|
+
/**
|
|
336
|
+
* Get the current terminal size.
|
|
337
|
+
*/
|
|
338
|
+
getSize(): {
|
|
339
|
+
cols: number;
|
|
340
|
+
rows: number;
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Get the current configuration.
|
|
344
|
+
*/
|
|
345
|
+
getConfig(): TuiConfig;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Callback type for obtaining admin auth headers for cleanup operations.
|
|
350
|
+
* Consumers can provide their own implementation (e.g., Keycloak, Auth0, Okta)
|
|
351
|
+
* via the `getCleanupAuth` option in createBddTest().
|
|
352
|
+
*/
|
|
353
|
+
type CleanupAuthProvider = (request: APIRequestContext) => Promise<Record<string, string>>;
|
|
354
|
+
type CreateContext = PlaywrightTestArgs & PlaywrightWorkerArgs & {
|
|
355
|
+
apiRequest: APIRequestContext;
|
|
356
|
+
page: Page;
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Factory function type for creating a TUI adapter.
|
|
360
|
+
* Returns undefined if TUI testing is not configured.
|
|
361
|
+
*/
|
|
362
|
+
type TuiFactory = () => TuiPort | undefined;
|
|
363
|
+
type CreateBddTestOptions = {
|
|
364
|
+
createApi?: (ctx: CreateContext) => ApiPort;
|
|
365
|
+
createUi?: (ctx: CreateContext) => UiPort;
|
|
366
|
+
createAuth?: (ctx: CreateContext & {
|
|
367
|
+
api: ApiPort;
|
|
368
|
+
ui: UiPort;
|
|
369
|
+
}) => AuthPort;
|
|
370
|
+
createCleanup?: (ctx: CreateContext) => CleanupPort;
|
|
371
|
+
/**
|
|
372
|
+
* Custom auth provider for cleanup operations.
|
|
373
|
+
* Return a record of headers (e.g., { Authorization: 'Bearer ...' }) to
|
|
374
|
+
* authenticate cleanup API calls.
|
|
375
|
+
*
|
|
376
|
+
* Use this to integrate with any auth provider (Keycloak, Auth0, Okta, etc.)
|
|
377
|
+
* without coupling the framework to a specific identity provider.
|
|
378
|
+
*
|
|
379
|
+
* If not provided, the default provider attempts a form-based login using
|
|
380
|
+
* DEFAULT_ADMIN_USERNAME / DEFAULT_ADMIN_PASSWORD env vars, or uses
|
|
381
|
+
* CLEANUP_AUTH_TOKEN if set. If no credentials are configured, cleanup
|
|
382
|
+
* runs unauthenticated.
|
|
383
|
+
*/
|
|
384
|
+
getCleanupAuth?: CleanupAuthProvider;
|
|
385
|
+
/**
|
|
386
|
+
* Factory function for creating a TUI adapter.
|
|
387
|
+
* Unlike other adapters, this is a simple factory that doesn't receive context,
|
|
388
|
+
* as TUI testing operates independently of Playwright's browser context.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```typescript
|
|
392
|
+
* createTui: () => new TuiTesterAdapter({
|
|
393
|
+
* command: ['node', 'dist/cli.js'],
|
|
394
|
+
* size: { cols: 100, rows: 30 },
|
|
395
|
+
* }),
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
createTui?: TuiFactory;
|
|
399
|
+
worldFactory?: () => World;
|
|
400
|
+
};
|
|
401
|
+
declare function createBddTest(options?: CreateBddTestOptions): {
|
|
402
|
+
test: _playwright_test.TestType<PlaywrightTestArgs & _playwright_test.PlaywrightTestOptions & playwright_bdd.BddTestFixtures & {
|
|
403
|
+
world: World;
|
|
404
|
+
api: ApiPort;
|
|
405
|
+
ui: UiPort;
|
|
406
|
+
auth: AuthPort;
|
|
407
|
+
cleanup: CleanupPort;
|
|
408
|
+
tui: TuiPort | undefined;
|
|
409
|
+
apiRequest: APIRequestContext;
|
|
410
|
+
}, PlaywrightWorkerArgs & _playwright_test.PlaywrightWorkerOptions & playwright_bdd.BddWorkerFixtures>;
|
|
411
|
+
expect: _playwright_test.Expect<{}>;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
declare function interpolate(template: string, vars: Record<string, string>): string;
|
|
415
|
+
declare function tryParseJson(text: string): unknown;
|
|
416
|
+
declare function selectPath(root: unknown, path: string): unknown;
|
|
417
|
+
declare function parseExpected(input: string, world: World): unknown;
|
|
418
|
+
declare function assertMasked(val: unknown): void;
|
|
419
|
+
declare function registerCleanup(world: World, item: {
|
|
420
|
+
method?: 'DELETE' | 'POST' | 'PATCH' | 'PUT';
|
|
421
|
+
path: string;
|
|
422
|
+
body?: unknown;
|
|
423
|
+
}): void;
|
|
424
|
+
|
|
425
|
+
declare class PlaywrightApiAdapter implements ApiPort {
|
|
426
|
+
private readonly request;
|
|
427
|
+
constructor(request: APIRequestContext);
|
|
428
|
+
sendJson(method: ApiMethod, path: string, body?: unknown, headers?: Record<string, string>): Promise<ApiResult>;
|
|
429
|
+
sendForm(method: 'POST' | 'PUT' | 'PATCH', path: string, form: Record<string, string>, headers?: Record<string, string>): Promise<ApiResult>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
declare class PlaywrightUiAdapter implements UiPort {
|
|
433
|
+
private readonly page;
|
|
434
|
+
constructor(page: Page);
|
|
435
|
+
goto(path: string): Promise<void>;
|
|
436
|
+
clickButton(name: string): Promise<void>;
|
|
437
|
+
clickLink(name: string): Promise<void>;
|
|
438
|
+
fillPlaceholder(placeholder: string, value: string): Promise<void>;
|
|
439
|
+
fillLabel(label: string, value: string): Promise<void>;
|
|
440
|
+
expectText(text: string): Promise<void>;
|
|
441
|
+
expectUrlContains(part: string): Promise<void>;
|
|
442
|
+
goBack(): Promise<void>;
|
|
443
|
+
reload(): Promise<void>;
|
|
444
|
+
waitSeconds(seconds: number): Promise<void>;
|
|
445
|
+
waitForPageLoad(): Promise<void>;
|
|
446
|
+
getCurrentUrl(): Promise<string>;
|
|
447
|
+
zoomTo(scale: number): Promise<void>;
|
|
448
|
+
typeText(text: string): Promise<void>;
|
|
449
|
+
pressKey(key: string): Promise<void>;
|
|
450
|
+
clickElementThatContains(clickMode: UiClickMode, elementType: string, text: string): Promise<void>;
|
|
451
|
+
clickElementWith(clickMode: UiClickMode, ordinal: string, text: string, method: UiLocatorMethod): Promise<void>;
|
|
452
|
+
fillDropdown(value: string, dropdownLabel: string): Promise<void>;
|
|
453
|
+
inputInElement(action: UiInputMode, value: string, ordinal: string, text: string, method: UiLocatorMethod): Promise<void>;
|
|
454
|
+
expectUrl(mode: UiUrlAssertMode, expected: string): Promise<void>;
|
|
455
|
+
expectNewTabUrl(mode: UiUrlAssertMode, expected: string): Promise<void>;
|
|
456
|
+
expectElementWithTextVisible(elementType: string, text: string, shouldBeVisible: boolean): Promise<void>;
|
|
457
|
+
expectElementState(ordinal: string, text: string, method: UiLocatorMethod, state: UiElementState): Promise<void>;
|
|
458
|
+
expectElementStateWithin(ordinal: string, text: string, method: UiLocatorMethod, state: UiElementState, seconds: number): Promise<void>;
|
|
459
|
+
private parseOrdinal;
|
|
460
|
+
private locatorBy;
|
|
461
|
+
private performClick;
|
|
462
|
+
private expectState;
|
|
463
|
+
private assertUrlAgainst;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
declare class UniversalAuthAdapter implements AuthPort {
|
|
467
|
+
private readonly deps;
|
|
468
|
+
constructor(deps: {
|
|
469
|
+
api: ApiPort;
|
|
470
|
+
ui: UiPort;
|
|
471
|
+
});
|
|
472
|
+
apiSetBearer(world: World, token: string): void;
|
|
473
|
+
apiLoginAsAdmin(world: World): Promise<void>;
|
|
474
|
+
apiLoginAsUser(world: World): Promise<void>;
|
|
475
|
+
private apiLogin;
|
|
476
|
+
uiLoginAsAdmin(world: World): Promise<void>;
|
|
477
|
+
uiLoginAsUser(world: World): Promise<void>;
|
|
478
|
+
private uiLogin;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
type CleanupRule = {
|
|
482
|
+
varMatch: string;
|
|
483
|
+
method?: 'DELETE' | 'POST' | 'PATCH' | 'PUT';
|
|
484
|
+
path: string;
|
|
485
|
+
body?: unknown;
|
|
486
|
+
};
|
|
487
|
+
declare class DefaultCleanupAdapter implements CleanupPort {
|
|
488
|
+
private readonly rules;
|
|
489
|
+
private readonly allowHeuristic;
|
|
490
|
+
constructor(input?: {
|
|
491
|
+
rules?: CleanupRule[];
|
|
492
|
+
allowHeuristic?: boolean;
|
|
493
|
+
});
|
|
494
|
+
registerFromVar(world: World, varName: string, id: unknown, meta?: unknown): void;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* TUI Tester Adapter
|
|
499
|
+
*
|
|
500
|
+
* Implements TuiPort using the tui-tester library (https://github.com/luxquant/tui-tester).
|
|
501
|
+
* Provides end-to-end testing capabilities for terminal user interfaces via tmux.
|
|
502
|
+
*/
|
|
503
|
+
|
|
504
|
+
declare class TuiTesterAdapter implements TuiPort {
|
|
505
|
+
private tester;
|
|
506
|
+
private config;
|
|
507
|
+
private running;
|
|
508
|
+
private tuiTesterModule;
|
|
509
|
+
constructor(config: TuiConfig);
|
|
510
|
+
/**
|
|
511
|
+
* Lazily load the tui-tester module to support optional dependency
|
|
512
|
+
*/
|
|
513
|
+
private loadTuiTester;
|
|
514
|
+
/**
|
|
515
|
+
* Get the tester instance, throwing if not started
|
|
516
|
+
*/
|
|
517
|
+
private getTester;
|
|
518
|
+
start(): Promise<void>;
|
|
519
|
+
stop(): Promise<void>;
|
|
520
|
+
restart(): Promise<void>;
|
|
521
|
+
isRunning(): boolean;
|
|
522
|
+
typeText(text: string, options?: {
|
|
523
|
+
delay?: number;
|
|
524
|
+
}): Promise<void>;
|
|
525
|
+
pressKey(key: string, modifiers?: TuiKeyModifiers): Promise<void>;
|
|
526
|
+
sendText(text: string): Promise<void>;
|
|
527
|
+
fillField(fieldLabel: string, value: string): Promise<void>;
|
|
528
|
+
selectOption(option: string): Promise<void>;
|
|
529
|
+
sendMouse(event: TuiMouseEvent): Promise<void>;
|
|
530
|
+
click(x: number, y: number, button?: TuiMouseButton): Promise<void>;
|
|
531
|
+
clickOnText(text: string): Promise<void>;
|
|
532
|
+
expectText(text: string, options?: TuiWaitOptions): Promise<void>;
|
|
533
|
+
expectPattern(pattern: RegExp, options?: TuiWaitOptions): Promise<void>;
|
|
534
|
+
expectNotText(text: string): Promise<void>;
|
|
535
|
+
assertScreenContains(text: string): Promise<void>;
|
|
536
|
+
assertScreenMatches(pattern: RegExp): Promise<void>;
|
|
537
|
+
waitForText(text: string, options?: TuiWaitOptions): Promise<void>;
|
|
538
|
+
waitForPattern(pattern: RegExp, options?: TuiWaitOptions): Promise<void>;
|
|
539
|
+
waitForReady(): Promise<void>;
|
|
540
|
+
waitSeconds(seconds: number): Promise<void>;
|
|
541
|
+
captureScreen(): Promise<TuiScreenCapture>;
|
|
542
|
+
getScreenText(): Promise<string>;
|
|
543
|
+
getScreenLines(): Promise<string[]>;
|
|
544
|
+
takeSnapshot(name: string): Promise<void>;
|
|
545
|
+
matchSnapshot(name: string): Promise<TuiSnapshotResult>;
|
|
546
|
+
clear(): Promise<void>;
|
|
547
|
+
resize(size: {
|
|
548
|
+
cols: number;
|
|
549
|
+
rows: number;
|
|
550
|
+
}): Promise<void>;
|
|
551
|
+
getSize(): {
|
|
552
|
+
cols: number;
|
|
553
|
+
rows: number;
|
|
554
|
+
};
|
|
555
|
+
getConfig(): TuiConfig;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Fetch Intercept Auth Adapter
|
|
560
|
+
*
|
|
561
|
+
* Provides UI authentication by intercepting fetch requests and injecting headers.
|
|
562
|
+
* Useful for testing UIs that communicate with APIs using fetch.
|
|
563
|
+
*/
|
|
564
|
+
|
|
565
|
+
interface FetchInterceptAuthData {
|
|
566
|
+
userId: string;
|
|
567
|
+
tenantId?: string;
|
|
568
|
+
roles: string[];
|
|
569
|
+
token?: string;
|
|
570
|
+
}
|
|
571
|
+
interface FetchInterceptConfig {
|
|
572
|
+
/**
|
|
573
|
+
* URL pattern to match for header injection.
|
|
574
|
+
* If not provided, headers are added to all fetch requests.
|
|
575
|
+
*/
|
|
576
|
+
urlPattern?: string | RegExp;
|
|
577
|
+
/**
|
|
578
|
+
* Headers to inject. Can be static values or functions that receive auth data.
|
|
579
|
+
*/
|
|
580
|
+
headers: Record<string, string | ((data: FetchInterceptAuthData) => string)>;
|
|
581
|
+
/**
|
|
582
|
+
* Key in localStorage to store auth data for UI components to read.
|
|
583
|
+
*/
|
|
584
|
+
localStorageKey?: string;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Default configuration for bypass auth mode.
|
|
588
|
+
*/
|
|
589
|
+
declare const defaultBypassConfig: FetchInterceptConfig;
|
|
590
|
+
/**
|
|
591
|
+
* Default configuration for bearer token auth.
|
|
592
|
+
*/
|
|
593
|
+
declare const defaultBearerConfig: FetchInterceptConfig;
|
|
594
|
+
/**
|
|
595
|
+
* Set up fetch interception on a page.
|
|
596
|
+
*
|
|
597
|
+
* @param page - Playwright page
|
|
598
|
+
* @param authData - Authentication data to inject
|
|
599
|
+
* @param config - Configuration for interception
|
|
600
|
+
*/
|
|
601
|
+
declare function setupFetchIntercept(page: Page, authData: FetchInterceptAuthData, config?: FetchInterceptConfig): Promise<void>;
|
|
602
|
+
/**
|
|
603
|
+
* Clear fetch interception setup (by reloading the page).
|
|
604
|
+
* Note: There's no clean way to remove addInitScript, so we reload.
|
|
605
|
+
*/
|
|
606
|
+
declare function clearFetchIntercept(page: Page): Promise<void>;
|
|
607
|
+
/**
|
|
608
|
+
* Helper to set up bypass auth for a page.
|
|
609
|
+
*/
|
|
610
|
+
declare function setupBypassAuth(page: Page, userId: string, roles: string[], tenantId?: string): Promise<void>;
|
|
611
|
+
/**
|
|
612
|
+
* Helper to set up bearer token auth for a page.
|
|
613
|
+
*/
|
|
614
|
+
declare function setupBearerAuth(page: Page, token: string): Promise<void>;
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Configuration for OIDC-based cleanup authentication.
|
|
618
|
+
*
|
|
619
|
+
* Reads from env vars by default:
|
|
620
|
+
* - OIDC_TOKEN_URL: Full token endpoint URL (required)
|
|
621
|
+
* - OIDC_CLIENT_ID: OAuth2 client ID (required)
|
|
622
|
+
* - OIDC_CLIENT_SECRET: OAuth2 client secret (optional, for confidential clients)
|
|
623
|
+
* - OIDC_GRANT_TYPE: Grant type (default: 'client_credentials')
|
|
624
|
+
* - OIDC_SCOPE: Requested scopes (optional)
|
|
625
|
+
* - OIDC_USERNAME: Username for password grant (optional)
|
|
626
|
+
* - OIDC_PASSWORD: Password for password grant (optional)
|
|
627
|
+
* - OIDC_EXTRA_HEADERS: JSON object of additional headers to include (optional)
|
|
628
|
+
*/
|
|
629
|
+
interface OidcCleanupAuthConfig {
|
|
630
|
+
/** Full URL to the OIDC token endpoint. */
|
|
631
|
+
tokenUrl?: string;
|
|
632
|
+
/** OAuth2 client ID. */
|
|
633
|
+
clientId?: string;
|
|
634
|
+
/** OAuth2 client secret (for confidential clients). */
|
|
635
|
+
clientSecret?: string;
|
|
636
|
+
/** Grant type. Defaults to 'client_credentials'. */
|
|
637
|
+
grantType?: string;
|
|
638
|
+
/** Requested scopes (space-separated). */
|
|
639
|
+
scope?: string;
|
|
640
|
+
/** Username for 'password' grant type. */
|
|
641
|
+
username?: string;
|
|
642
|
+
/** Password for 'password' grant type. */
|
|
643
|
+
password?: string;
|
|
644
|
+
/**
|
|
645
|
+
* Additional headers to include in cleanup API requests
|
|
646
|
+
* (e.g., { 'x-user-roles': 'admin' }).
|
|
647
|
+
*/
|
|
648
|
+
extraHeaders?: Record<string, string>;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Creates an OIDC-based cleanup auth provider.
|
|
652
|
+
*
|
|
653
|
+
* Works with any OIDC-compliant provider (Keycloak, Auth0, Okta, Azure AD, etc.)
|
|
654
|
+
* by configuring the token endpoint URL and client credentials.
|
|
655
|
+
*
|
|
656
|
+
* @example Keycloak with password grant:
|
|
657
|
+
* ```typescript
|
|
658
|
+
* import { createBddTest, createOidcCleanupAuth } from '@esimplicitylabs/katalyst-xspec';
|
|
659
|
+
*
|
|
660
|
+
* // Set env vars: OIDC_TOKEN_URL, OIDC_CLIENT_ID, OIDC_USERNAME, OIDC_PASSWORD
|
|
661
|
+
* const test = createBddTest({
|
|
662
|
+
* getCleanupAuth: createOidcCleanupAuth({
|
|
663
|
+
* grantType: 'password',
|
|
664
|
+
* extraHeaders: { 'x-user-roles': 'admin' },
|
|
665
|
+
* }),
|
|
666
|
+
* });
|
|
667
|
+
* ```
|
|
668
|
+
*
|
|
669
|
+
* @example Auth0 with client_credentials grant:
|
|
670
|
+
* ```typescript
|
|
671
|
+
* const test = createBddTest({
|
|
672
|
+
* getCleanupAuth: createOidcCleanupAuth({
|
|
673
|
+
* tokenUrl: 'https://your-tenant.auth0.com/oauth/token',
|
|
674
|
+
* clientId: 'your-client-id',
|
|
675
|
+
* clientSecret: 'your-secret',
|
|
676
|
+
* scope: 'delete:resources',
|
|
677
|
+
* }),
|
|
678
|
+
* });
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
declare function createOidcCleanupAuth(config?: OidcCleanupAuthConfig): CleanupAuthProvider;
|
|
682
|
+
/**
|
|
683
|
+
* Reset the cached OIDC token (useful for testing or when tokens expire).
|
|
684
|
+
*/
|
|
685
|
+
declare function resetOidcCleanupAuth(): void;
|
|
686
|
+
|
|
687
|
+
type TagsForProjectInput = {
|
|
688
|
+
projectTag: string;
|
|
689
|
+
extraTags?: string;
|
|
690
|
+
defaultExcludes?: string;
|
|
691
|
+
};
|
|
692
|
+
declare function tagsForProject({ projectTag, extraTags, defaultExcludes }: TagsForProjectInput): string;
|
|
693
|
+
declare function resolveExtraTags(raw?: string | null): string | undefined;
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Options for configuring worker resolution.
|
|
697
|
+
*/
|
|
698
|
+
type ResolveWorkersOptions = {
|
|
699
|
+
/**
|
|
700
|
+
* The type of test being run.
|
|
701
|
+
* When set to `'tui'`, workers is always forced to `1` (TUI tests require sequential execution).
|
|
702
|
+
*/
|
|
703
|
+
testType?: 'api' | 'ui' | 'tui' | 'hybrid';
|
|
704
|
+
/**
|
|
705
|
+
* Number of workers to use in CI environments (when `CI` env var is truthy).
|
|
706
|
+
* @default 1
|
|
707
|
+
*/
|
|
708
|
+
ciWorkers?: number;
|
|
709
|
+
/**
|
|
710
|
+
* Default number of workers for local development.
|
|
711
|
+
* Set to `undefined` (default) to let Playwright decide (50% of CPU cores).
|
|
712
|
+
* Set to a number to override.
|
|
713
|
+
*/
|
|
714
|
+
defaultWorkers?: number;
|
|
715
|
+
};
|
|
716
|
+
/**
|
|
717
|
+
* Resolves the number of Playwright workers based on environment variables,
|
|
718
|
+
* test type, and CI detection.
|
|
719
|
+
*
|
|
720
|
+
* **Environment variable:** `WORKERS`
|
|
721
|
+
* - `'auto'` or unset — uses smart defaults (Playwright decides locally, 1 in CI)
|
|
722
|
+
* - A positive integer (e.g. `'4'`) — uses that exact worker count
|
|
723
|
+
* - `'50%'` style percentage strings are passed through as strings for Playwright
|
|
724
|
+
*
|
|
725
|
+
* **Precedence:**
|
|
726
|
+
* 1. `testType: 'tui'` always returns `1`
|
|
727
|
+
* 2. `WORKERS` env var with a valid number overrides everything else
|
|
728
|
+
* 3. CI environment defaults to `ciWorkers` (default: `1`)
|
|
729
|
+
* 4. Otherwise returns `defaultWorkers` (default: `undefined`, letting Playwright decide)
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```ts
|
|
733
|
+
* import { resolveWorkers } from '@esimplicitylabs/katalyst-xspec';
|
|
734
|
+
*
|
|
735
|
+
* export default defineConfig({
|
|
736
|
+
* workers: resolveWorkers(),
|
|
737
|
+
* projects: [
|
|
738
|
+
* { name: 'api', ...apiBdd },
|
|
739
|
+
* { name: 'tui', ...tuiBdd, workers: resolveWorkers({ testType: 'tui' }) },
|
|
740
|
+
* ],
|
|
741
|
+
* });
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
declare function resolveWorkers(options?: ResolveWorkersOptions): number | undefined;
|
|
745
|
+
/**
|
|
746
|
+
* Returns the number of available CPU cores on the current machine.
|
|
747
|
+
* Useful for logging or making informed decisions about worker counts.
|
|
748
|
+
*/
|
|
749
|
+
declare function getCpuCount(): number;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Options for resolving the features directory path.
|
|
753
|
+
*/
|
|
754
|
+
type ResolveFeaturesOptions = {
|
|
755
|
+
/**
|
|
756
|
+
* Default path to use when `FEATURES_DIR` env var is not set.
|
|
757
|
+
* @default 'features'
|
|
758
|
+
*/
|
|
759
|
+
defaultDir?: string;
|
|
760
|
+
};
|
|
761
|
+
/**
|
|
762
|
+
* Options for resolving the custom steps directory path.
|
|
763
|
+
*/
|
|
764
|
+
type ResolveStepsOptions = {
|
|
765
|
+
/**
|
|
766
|
+
* Default path to use when `CUSTOM_STEPS_DIR` env var is not set.
|
|
767
|
+
* @default 'features/steps'
|
|
768
|
+
*/
|
|
769
|
+
defaultDir?: string;
|
|
770
|
+
};
|
|
771
|
+
/**
|
|
772
|
+
* Resolves the path to the BDD features directory.
|
|
773
|
+
*
|
|
774
|
+
* **Environment variable:** `FEATURES_DIR`
|
|
775
|
+
* - When set, returns the value as-is (relative or absolute)
|
|
776
|
+
* - When unset, returns `defaultDir` (default: `'features'`)
|
|
777
|
+
*
|
|
778
|
+
* Use this in `playwright.config.ts` with `defineBddProject()` to make
|
|
779
|
+
* feature file locations configurable per-environment.
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```ts
|
|
783
|
+
* import { resolveFeatures } from '@esimplicitylabs/katalyst-xspec';
|
|
784
|
+
* import { defineBddProject } from 'playwright-bdd';
|
|
785
|
+
*
|
|
786
|
+
* const apiBdd = defineBddProject({
|
|
787
|
+
* name: 'api',
|
|
788
|
+
* features: `${resolveFeatures()}/api/**\/*.feature`,
|
|
789
|
+
* steps: `${resolveSteps()}/**\/*.ts`,
|
|
790
|
+
* });
|
|
791
|
+
* ```
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```bash
|
|
795
|
+
* # Run from the layer directory (default)
|
|
796
|
+
* just test-bdd-run dev
|
|
797
|
+
*
|
|
798
|
+
* # Override via env var (used by just recipes in multi-layer repos)
|
|
799
|
+
* FEATURES_DIR=../../claims-api/tests/features just test-bdd-run dev
|
|
800
|
+
* ```
|
|
801
|
+
*/
|
|
802
|
+
declare function resolveFeatures(options?: ResolveFeaturesOptions): string;
|
|
803
|
+
/**
|
|
804
|
+
* Resolves the path to the custom step definitions directory.
|
|
805
|
+
*
|
|
806
|
+
* **Environment variable:** `CUSTOM_STEPS_DIR`
|
|
807
|
+
* - When set, returns the value as-is (relative or absolute)
|
|
808
|
+
* - When unset, returns `defaultDir` (default: `'features/steps'`)
|
|
809
|
+
*
|
|
810
|
+
* Custom steps are layer-specific Given/When/Then definitions that extend
|
|
811
|
+
* the library's pre-built steps. They live alongside the feature files in
|
|
812
|
+
* each test layer.
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```ts
|
|
816
|
+
* import { resolveSteps } from '@esimplicitylabs/katalyst-xspec';
|
|
817
|
+
* import { defineBddProject } from 'playwright-bdd';
|
|
818
|
+
*
|
|
819
|
+
* const apiBdd = defineBddProject({
|
|
820
|
+
* name: 'api',
|
|
821
|
+
* features: `${resolveFeatures()}/api/**\/*.feature`,
|
|
822
|
+
* steps: `${resolveSteps()}/**\/*.ts`,
|
|
823
|
+
* });
|
|
824
|
+
* ```
|
|
825
|
+
*/
|
|
826
|
+
declare function resolveSteps(options?: ResolveStepsOptions): string;
|
|
827
|
+
/**
|
|
828
|
+
* Builds glob patterns for use with `defineBddProject()` based on resolved paths.
|
|
829
|
+
*
|
|
830
|
+
* A convenience wrapper combining `resolveFeatures()` and `resolveSteps()` into
|
|
831
|
+
* the shape expected by playwright-bdd's `defineBddProject()`.
|
|
832
|
+
*
|
|
833
|
+
* @example
|
|
834
|
+
* ```ts
|
|
835
|
+
* import { resolveBddPaths } from '@esimplicitylabs/katalyst-xspec';
|
|
836
|
+
* import { defineBddProject } from 'playwright-bdd';
|
|
837
|
+
*
|
|
838
|
+
* const { features, steps } = resolveBddPaths({ tag: 'api' });
|
|
839
|
+
* const apiBdd = defineBddProject({ name: 'api', features, steps });
|
|
840
|
+
* ```
|
|
841
|
+
*/
|
|
842
|
+
declare function resolveBddPaths(options?: {
|
|
843
|
+
/**
|
|
844
|
+
* Test tag to scope feature file glob (e.g., 'api', 'ui').
|
|
845
|
+
* Creates pattern: `{featuresDir}/{tag}/**\/*.feature`
|
|
846
|
+
* If omitted, uses `{featuresDir}/**\/*.feature`
|
|
847
|
+
*/
|
|
848
|
+
tag?: string;
|
|
849
|
+
/** Options for features resolution */
|
|
850
|
+
featuresOptions?: ResolveFeaturesOptions;
|
|
851
|
+
/** Options for steps resolution */
|
|
852
|
+
stepsOptions?: ResolveStepsOptions;
|
|
853
|
+
}): {
|
|
854
|
+
features: string;
|
|
855
|
+
steps: string;
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Network target resolution for the API request context.
|
|
860
|
+
*
|
|
861
|
+
* macOS resolves `*.localhost` to `::1` (IPv6) first, but a local kind cluster
|
|
862
|
+
* binds its ingress on `0.0.0.0` (IPv4 only), so requests to `::1` fail with
|
|
863
|
+
* ECONNRESET. For plain-http `*.localhost` targets we connect to `127.0.0.1`
|
|
864
|
+
* instead and send the original host as the `Host` header so host-based
|
|
865
|
+
* ingress routing (Istio Gateway/VirtualService, nginx, etc.) still matches.
|
|
866
|
+
*
|
|
867
|
+
* Deliberately NOT rewritten:
|
|
868
|
+
* - bare `localhost`: local dev servers (Vite, Node >=17) often bind `::1` only.
|
|
869
|
+
* - `https:`: TLS SNI and certificate checks use the URL host, so connecting to
|
|
870
|
+
* 127.0.0.1 would break verification regardless of the Host header.
|
|
871
|
+
*
|
|
872
|
+
* Disable entirely with `KATALYST_XSPEC_FORCE_IPV4=false` (or `0`). The
|
|
873
|
+
* pre-rename `STACK_TESTS_FORCE_IPV4` is still honoured.
|
|
874
|
+
*/
|
|
875
|
+
type ApiRequestTarget = {
|
|
876
|
+
baseURL: string;
|
|
877
|
+
extraHTTPHeaders?: Record<string, string>;
|
|
878
|
+
};
|
|
879
|
+
declare function resolveApiRequestTarget(baseURL: string, env?: Record<string, string | undefined>): ApiRequestTarget;
|
|
880
|
+
|
|
881
|
+
export { type ApiMethod, type ApiPort, type ApiRequestTarget, type ApiResult, type AuthPort, type CleanupAuthProvider, type CleanupItem, type CleanupPort, type CleanupRule, type CreateBddTestOptions, DefaultCleanupAdapter, type FetchInterceptAuthData, type FetchInterceptConfig, type OidcCleanupAuthConfig, PlaywrightApiAdapter, PlaywrightUiAdapter, type ResolveFeaturesOptions, type ResolveStepsOptions, type ResolveWorkersOptions, type TuiConfig, type TuiFactory, type TuiKeyModifiers, type TuiMouseButton, type TuiMouseEvent, type TuiMouseEventType, type TuiPort, type TuiScreenCapture, type TuiSnapshotResult, TuiTesterAdapter, type TuiWaitOptions, type UiClickMode, type UiElementState, type UiInputMode, type UiLocatorMethod, type UiPort, type UiUrlAssertMode, UniversalAuthAdapter, type World, assertMasked, clearFetchIntercept, createBddTest, createOidcCleanupAuth, defaultBearerConfig, defaultBypassConfig, getCpuCount, initWorld, interpolate, parseExpected, registerCleanup, resetOidcCleanupAuth, resolveApiRequestTarget, resolveBddPaths, resolveExtraTags, resolveFeatures, resolveSteps, resolveWorkers, selectPath, setupBearerAuth, setupBypassAuth, setupFetchIntercept, tagsForProject, tryParseJson };
|