@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
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Hybrid Test Patterns
|
|
2
|
+
|
|
3
|
+
Common patterns for hybrid testing (combining API and UI) with the Katalyst BDD framework.
|
|
4
|
+
|
|
5
|
+
## Core Principle
|
|
6
|
+
|
|
7
|
+
Hybrid tests use `@hybrid` tag to access both API and UI steps. The typical flow:
|
|
8
|
+
1. **Setup** - Create test data via API (fast, reliable)
|
|
9
|
+
2. **Test** - Verify behavior in UI (user-facing validation)
|
|
10
|
+
3. **Cleanup** - Remove test data via API (automatic)
|
|
11
|
+
|
|
12
|
+
## Basic Patterns
|
|
13
|
+
|
|
14
|
+
### Create via API, Verify in UI
|
|
15
|
+
|
|
16
|
+
```gherkin
|
|
17
|
+
@hybrid
|
|
18
|
+
Scenario: Create user via API, verify in admin panel
|
|
19
|
+
# API: Create test data
|
|
20
|
+
Given I am authenticated as an admin via API
|
|
21
|
+
Given I generate a UUID and store as "testId"
|
|
22
|
+
When I POST "/admin/users" with JSON body:
|
|
23
|
+
"""
|
|
24
|
+
{
|
|
25
|
+
"email": "test-{testId}@example.com",
|
|
26
|
+
"name": "Test User {testId}"
|
|
27
|
+
}
|
|
28
|
+
"""
|
|
29
|
+
Then the response status should be 201
|
|
30
|
+
And I store the value at "id" as "userId"
|
|
31
|
+
Given I register cleanup DELETE "/admin/users/{userId}"
|
|
32
|
+
|
|
33
|
+
# UI: Verify user appears
|
|
34
|
+
Given I navigate to "/admin/users"
|
|
35
|
+
Then I should see text "test-{testId}@example.com"
|
|
36
|
+
Then I should see text "Test User {testId}"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Setup Data, Test Workflow
|
|
40
|
+
|
|
41
|
+
```gherkin
|
|
42
|
+
@hybrid
|
|
43
|
+
Scenario: Test order workflow with pre-created product
|
|
44
|
+
# API: Create product
|
|
45
|
+
Given I am authenticated as an admin via API
|
|
46
|
+
Given I generate a UUID and store as "productId"
|
|
47
|
+
When I POST "/admin/products" with JSON body:
|
|
48
|
+
"""
|
|
49
|
+
{
|
|
50
|
+
"name": "Test Product {productId}",
|
|
51
|
+
"price": 99.99,
|
|
52
|
+
"stock": 100
|
|
53
|
+
}
|
|
54
|
+
"""
|
|
55
|
+
Then the response status should be 201
|
|
56
|
+
And I store the value at "id" as "prodId"
|
|
57
|
+
Given I register cleanup DELETE "/admin/products/{prodId}"
|
|
58
|
+
|
|
59
|
+
# UI: Test ordering flow
|
|
60
|
+
Given I am authenticated in UI as "customer"
|
|
61
|
+
Given I navigate to "/products/{prodId}"
|
|
62
|
+
Then I should see text "Test Product {productId}"
|
|
63
|
+
When I click the button "Add to Cart"
|
|
64
|
+
Then I should see text "Added to cart"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Verify API Changes Reflect in UI
|
|
68
|
+
|
|
69
|
+
```gherkin
|
|
70
|
+
@hybrid
|
|
71
|
+
Scenario: API update reflects in UI
|
|
72
|
+
# API: Create and update
|
|
73
|
+
Given I am authenticated as an admin via API
|
|
74
|
+
When I POST "/users" with JSON body:
|
|
75
|
+
"""
|
|
76
|
+
{ "name": "Original Name" }
|
|
77
|
+
"""
|
|
78
|
+
Then the response status should be 201
|
|
79
|
+
And I store the value at "id" as "userId"
|
|
80
|
+
Given I register cleanup DELETE "/users/{userId}"
|
|
81
|
+
|
|
82
|
+
When I PATCH "/users/{userId}" with JSON body:
|
|
83
|
+
"""
|
|
84
|
+
{ "name": "Updated Name" }
|
|
85
|
+
"""
|
|
86
|
+
Then the response status should be 200
|
|
87
|
+
|
|
88
|
+
# UI: Verify update is visible
|
|
89
|
+
Given I navigate to "/users/{userId}"
|
|
90
|
+
Then I should see text "Updated Name"
|
|
91
|
+
And I should not see text "Original Name"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Variable Sharing Patterns
|
|
95
|
+
|
|
96
|
+
### Share IDs Between Layers
|
|
97
|
+
|
|
98
|
+
```gherkin
|
|
99
|
+
@hybrid
|
|
100
|
+
Scenario: Use API-created ID in UI navigation
|
|
101
|
+
# API: Create resource
|
|
102
|
+
Given I am authenticated as an admin via API
|
|
103
|
+
When I POST "/projects" with JSON body:
|
|
104
|
+
"""
|
|
105
|
+
{ "name": "Test Project" }
|
|
106
|
+
"""
|
|
107
|
+
Then the response status should be 201
|
|
108
|
+
And I store the value at "id" as "projectId"
|
|
109
|
+
Given I register cleanup DELETE "/projects/{projectId}"
|
|
110
|
+
|
|
111
|
+
# UI: Navigate using ID
|
|
112
|
+
Given I navigate to "/projects/{projectId}"
|
|
113
|
+
Then I should see text "Test Project"
|
|
114
|
+
And the URL should contain "/projects/{projectId}"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Share Data Between Layers
|
|
118
|
+
|
|
119
|
+
```gherkin
|
|
120
|
+
@hybrid
|
|
121
|
+
Scenario: Verify API data in UI
|
|
122
|
+
# Setup variables
|
|
123
|
+
Given I generate a UUID and store as "runId"
|
|
124
|
+
Given I set variable "testEmail" to "hybrid-{runId}@test.com"
|
|
125
|
+
Given I set variable "testName" to "Hybrid User {runId}"
|
|
126
|
+
|
|
127
|
+
# API: Create with variables
|
|
128
|
+
Given I am authenticated as an admin via API
|
|
129
|
+
When I POST "/users" with JSON body:
|
|
130
|
+
"""
|
|
131
|
+
{
|
|
132
|
+
"email": "{testEmail}",
|
|
133
|
+
"name": "{testName}"
|
|
134
|
+
}
|
|
135
|
+
"""
|
|
136
|
+
Then the response status should be 201
|
|
137
|
+
And I store the value at "id" as "userId"
|
|
138
|
+
Given I register cleanup DELETE "/users/{userId}"
|
|
139
|
+
|
|
140
|
+
# UI: Verify same variables
|
|
141
|
+
Given I navigate to "/users/{userId}"
|
|
142
|
+
Then I should see text "{testEmail}"
|
|
143
|
+
Then I should see text "{testName}"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Authentication Patterns
|
|
147
|
+
|
|
148
|
+
### Separate API and UI Auth
|
|
149
|
+
|
|
150
|
+
```gherkin
|
|
151
|
+
@hybrid
|
|
152
|
+
Scenario: Different auth for API vs UI
|
|
153
|
+
# API: Admin creates data
|
|
154
|
+
Given I am authenticated as an admin via API
|
|
155
|
+
When I POST "/admin/announcements" with JSON body:
|
|
156
|
+
"""
|
|
157
|
+
{ "message": "Test announcement", "audience": "all" }
|
|
158
|
+
"""
|
|
159
|
+
Then the response status should be 201
|
|
160
|
+
And I store the value at "id" as "announcementId"
|
|
161
|
+
Given I register cleanup DELETE "/admin/announcements/{announcementId}"
|
|
162
|
+
|
|
163
|
+
# UI: Regular user sees announcement
|
|
164
|
+
Given I am authenticated in UI as "user"
|
|
165
|
+
Given I navigate to "/dashboard"
|
|
166
|
+
Then I should see text "Test announcement"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Use API Token in UI
|
|
170
|
+
|
|
171
|
+
```gherkin
|
|
172
|
+
@hybrid
|
|
173
|
+
Scenario: Get token from API, use in UI
|
|
174
|
+
# API: Login and get token
|
|
175
|
+
When I POST "/auth/login" with JSON body:
|
|
176
|
+
"""
|
|
177
|
+
{ "email": "user@example.com", "password": "password" }
|
|
178
|
+
"""
|
|
179
|
+
Then the response status should be 200
|
|
180
|
+
And I store the value at "token" as "authToken"
|
|
181
|
+
|
|
182
|
+
# UI: Use token for authentication
|
|
183
|
+
Given I am authenticated in UI with bearer token "{authToken}"
|
|
184
|
+
Given I navigate to "/profile"
|
|
185
|
+
Then I should see text "My Profile"
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Complex Workflow Patterns
|
|
189
|
+
|
|
190
|
+
### Multi-Step Business Process
|
|
191
|
+
|
|
192
|
+
```gherkin
|
|
193
|
+
@hybrid
|
|
194
|
+
Scenario: Complete order processing workflow
|
|
195
|
+
# API: Setup - Create customer and product
|
|
196
|
+
Given I am authenticated as an admin via API
|
|
197
|
+
Given I generate a UUID and store as "runId"
|
|
198
|
+
|
|
199
|
+
When I POST "/customers" with JSON body:
|
|
200
|
+
"""
|
|
201
|
+
{ "email": "customer-{runId}@test.com" }
|
|
202
|
+
"""
|
|
203
|
+
Then the response status should be 201
|
|
204
|
+
And I store the value at "id" as "customerId"
|
|
205
|
+
Given I register cleanup DELETE "/customers/{customerId}"
|
|
206
|
+
|
|
207
|
+
When I POST "/products" with JSON body:
|
|
208
|
+
"""
|
|
209
|
+
{ "name": "Product {runId}", "price": 50.00 }
|
|
210
|
+
"""
|
|
211
|
+
Then the response status should be 201
|
|
212
|
+
And I store the value at "id" as "productId"
|
|
213
|
+
Given I register cleanup DELETE "/products/{productId}"
|
|
214
|
+
|
|
215
|
+
# UI: Customer places order
|
|
216
|
+
Given I am authenticated in UI as "customer-{runId}@test.com"
|
|
217
|
+
Given I navigate to "/products/{productId}"
|
|
218
|
+
When I click the button "Buy Now"
|
|
219
|
+
Then I should see text "Order Confirmation"
|
|
220
|
+
And I store the current URL as "orderUrl"
|
|
221
|
+
|
|
222
|
+
# API: Verify order created
|
|
223
|
+
When I GET "/customers/{customerId}/orders"
|
|
224
|
+
Then the response status should be 200
|
|
225
|
+
And the response should be a JSON array
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Data Synchronization Test
|
|
229
|
+
|
|
230
|
+
```gherkin
|
|
231
|
+
@hybrid
|
|
232
|
+
Scenario: Real-time sync between API and UI
|
|
233
|
+
Given I am authenticated as an admin via API
|
|
234
|
+
Given I generate a UUID and store as "runId"
|
|
235
|
+
|
|
236
|
+
# API: Create initial data
|
|
237
|
+
When I POST "/messages" with JSON body:
|
|
238
|
+
"""
|
|
239
|
+
{ "content": "Message {runId}" }
|
|
240
|
+
"""
|
|
241
|
+
Then the response status should be 201
|
|
242
|
+
And I store the value at "id" as "messageId"
|
|
243
|
+
Given I register cleanup DELETE "/messages/{messageId}"
|
|
244
|
+
|
|
245
|
+
# UI: Open page to watch for updates
|
|
246
|
+
Given I am authenticated in UI as "user"
|
|
247
|
+
Given I navigate to "/messages"
|
|
248
|
+
Then I should see text "Message {runId}"
|
|
249
|
+
|
|
250
|
+
# API: Update data
|
|
251
|
+
When I PATCH "/messages/{messageId}" with JSON body:
|
|
252
|
+
"""
|
|
253
|
+
{ "content": "Updated Message {runId}" }
|
|
254
|
+
"""
|
|
255
|
+
Then the response status should be 200
|
|
256
|
+
|
|
257
|
+
# UI: Verify update (may need to refresh or wait for websocket)
|
|
258
|
+
When I reload the page
|
|
259
|
+
Then I should see text "Updated Message {runId}"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## State Management Patterns
|
|
263
|
+
|
|
264
|
+
### Clean State Between Tests
|
|
265
|
+
|
|
266
|
+
```gherkin
|
|
267
|
+
@hybrid
|
|
268
|
+
Feature: User Settings
|
|
269
|
+
|
|
270
|
+
Background:
|
|
271
|
+
Given I am authenticated as an admin via API
|
|
272
|
+
Given I generate a UUID and store as "testId"
|
|
273
|
+
|
|
274
|
+
# Create fresh user for each test
|
|
275
|
+
When I POST "/users" with JSON body:
|
|
276
|
+
"""
|
|
277
|
+
{ "email": "settings-{testId}@test.com" }
|
|
278
|
+
"""
|
|
279
|
+
Then the response status should be 201
|
|
280
|
+
And I store the value at "id" as "userId"
|
|
281
|
+
Given I register cleanup DELETE "/users/{userId}"
|
|
282
|
+
|
|
283
|
+
Scenario: Update notification settings
|
|
284
|
+
Given I navigate to "/users/{userId}/settings"
|
|
285
|
+
When I click the "Notifications" tab
|
|
286
|
+
And I click the element "#email-notifications"
|
|
287
|
+
And I click the button "Save"
|
|
288
|
+
Then I should see text "Settings saved"
|
|
289
|
+
|
|
290
|
+
# Verify via API
|
|
291
|
+
When I GET "/users/{userId}/settings"
|
|
292
|
+
Then the value at "notifications.email" should equal "true"
|
|
293
|
+
|
|
294
|
+
Scenario: Update privacy settings
|
|
295
|
+
# Uses fresh user from Background
|
|
296
|
+
Given I navigate to "/users/{userId}/settings"
|
|
297
|
+
When I click the "Privacy" tab
|
|
298
|
+
# ... test privacy settings
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Seed Multiple Resources
|
|
302
|
+
|
|
303
|
+
```gherkin
|
|
304
|
+
@hybrid
|
|
305
|
+
Scenario: Dashboard with multiple data types
|
|
306
|
+
Given I am authenticated as an admin via API
|
|
307
|
+
Given I generate a UUID and store as "runId"
|
|
308
|
+
|
|
309
|
+
# Create multiple related resources
|
|
310
|
+
When I POST "/projects" with JSON body:
|
|
311
|
+
"""
|
|
312
|
+
{ "name": "Project {runId}" }
|
|
313
|
+
"""
|
|
314
|
+
Then the response status should be 201
|
|
315
|
+
And I store the value at "id" as "projectId"
|
|
316
|
+
Given I register cleanup DELETE "/projects/{projectId}"
|
|
317
|
+
|
|
318
|
+
When I POST "/projects/{projectId}/tasks" with JSON body:
|
|
319
|
+
"""
|
|
320
|
+
{ "title": "Task 1" }
|
|
321
|
+
"""
|
|
322
|
+
Then the response status should be 201
|
|
323
|
+
|
|
324
|
+
When I POST "/projects/{projectId}/tasks" with JSON body:
|
|
325
|
+
"""
|
|
326
|
+
{ "title": "Task 2" }
|
|
327
|
+
"""
|
|
328
|
+
Then the response status should be 201
|
|
329
|
+
|
|
330
|
+
# UI: Verify dashboard shows all data
|
|
331
|
+
Given I navigate to "/projects/{projectId}"
|
|
332
|
+
Then I should see text "Project {runId}"
|
|
333
|
+
And I should see text "Task 1"
|
|
334
|
+
And I should see text "Task 2"
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Error Testing Patterns
|
|
338
|
+
|
|
339
|
+
### Test Error States
|
|
340
|
+
|
|
341
|
+
```gherkin
|
|
342
|
+
@hybrid
|
|
343
|
+
Scenario: UI shows error when API resource deleted
|
|
344
|
+
# API: Create and immediately delete
|
|
345
|
+
Given I am authenticated as an admin via API
|
|
346
|
+
When I POST "/items" with JSON body:
|
|
347
|
+
"""
|
|
348
|
+
{ "name": "Temporary Item" }
|
|
349
|
+
"""
|
|
350
|
+
Then the response status should be 201
|
|
351
|
+
And I store the value at "id" as "itemId"
|
|
352
|
+
|
|
353
|
+
When I DELETE "/items/{itemId}"
|
|
354
|
+
Then the response status should be 204
|
|
355
|
+
|
|
356
|
+
# UI: Try to access deleted resource
|
|
357
|
+
Given I navigate to "/items/{itemId}"
|
|
358
|
+
Then I should see text "Item not found"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Complete Example: User Onboarding Flow
|
|
362
|
+
|
|
363
|
+
```gherkin
|
|
364
|
+
@hybrid
|
|
365
|
+
Feature: User Onboarding
|
|
366
|
+
As a product owner
|
|
367
|
+
I want to test the complete onboarding flow
|
|
368
|
+
So that I can ensure new users have a smooth experience
|
|
369
|
+
|
|
370
|
+
Scenario: Complete onboarding journey
|
|
371
|
+
Given I generate a UUID and store as "testId"
|
|
372
|
+
Given I set variable "userEmail" to "onboard-{testId}@test.com"
|
|
373
|
+
Given I set variable "userName" to "New User {testId}"
|
|
374
|
+
|
|
375
|
+
# Step 1: Admin creates user invitation via API
|
|
376
|
+
Given I am authenticated as an admin via API
|
|
377
|
+
When I POST "/admin/invitations" with JSON body:
|
|
378
|
+
"""
|
|
379
|
+
{
|
|
380
|
+
"email": "{userEmail}",
|
|
381
|
+
"role": "member"
|
|
382
|
+
}
|
|
383
|
+
"""
|
|
384
|
+
Then the response status should be 201
|
|
385
|
+
And I store the value at "token" as "inviteToken"
|
|
386
|
+
And I store the value at "id" as "inviteId"
|
|
387
|
+
Given I register cleanup DELETE "/admin/invitations/{inviteId}"
|
|
388
|
+
|
|
389
|
+
# Step 2: User accepts invitation via UI
|
|
390
|
+
Given I navigate to "/invite/{inviteToken}"
|
|
391
|
+
Then I should see text "Welcome! Complete your profile"
|
|
392
|
+
When I fill the form:
|
|
393
|
+
| Field | Value |
|
|
394
|
+
| Full Name | {userName} |
|
|
395
|
+
| Password | SecurePass123! |
|
|
396
|
+
| Confirm Password| SecurePass123! |
|
|
397
|
+
And I click the button "Complete Setup"
|
|
398
|
+
Then I should see text "Welcome, {userName}"
|
|
399
|
+
And the URL should contain "/dashboard"
|
|
400
|
+
|
|
401
|
+
# Step 3: Verify user created via API
|
|
402
|
+
Given I am authenticated as an admin via API
|
|
403
|
+
When I GET "/admin/users?email={userEmail}"
|
|
404
|
+
Then the response status should be 200
|
|
405
|
+
And the value at "[0].name" should equal "{userName}"
|
|
406
|
+
And I store the value at "[0].id" as "userId"
|
|
407
|
+
Given I register cleanup DELETE "/admin/users/{userId}"
|
|
408
|
+
|
|
409
|
+
# Step 4: User completes profile via UI
|
|
410
|
+
Given I am authenticated in UI as "member" with id "{userId}"
|
|
411
|
+
Given I navigate to "/profile/edit"
|
|
412
|
+
When I fill in "Bio" with "I'm a new team member!"
|
|
413
|
+
And I click the button "Save"
|
|
414
|
+
Then I should see text "Profile updated"
|
|
415
|
+
|
|
416
|
+
# Step 5: Verify profile update via API
|
|
417
|
+
When I GET "/users/{userId}"
|
|
418
|
+
Then the response status should be 200
|
|
419
|
+
And the value at "bio" should equal "I'm a new team member!"
|
|
420
|
+
```
|