@esimplicitylabs/katalyst-xspec 0.6.0 → 0.7.1

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/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as playwright_bdd from 'playwright-bdd';
2
2
  export { test as baseTest } from 'playwright-bdd';
3
3
  import * as _playwright_test from '@playwright/test';
4
- import { APIResponse, PlaywrightTestArgs, PlaywrightWorkerArgs, APIRequestContext, Page } from '@playwright/test';
4
+ import { APIResponse, APIRequestContext, PlaywrightTestArgs, PlaywrightWorkerArgs, Page } from '@playwright/test';
5
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
6
 
7
7
  type CleanupItem = {
@@ -685,11 +685,14 @@ declare function createOidcCleanupAuth(config?: OidcCleanupAuthConfig): CleanupA
685
685
  declare function resetOidcCleanupAuth(): void;
686
686
 
687
687
  type TagsForProjectInput = {
688
- projectTag: string;
688
+ /** Optional tag a project is limited to, e.g. '@smoke'. Not needed for the built-in steps. */
689
+ projectTag?: string;
690
+ /** Extra tag expression, typically from `resolveExtraTags(process.env.TEST_TAGS)`. */
689
691
  extraTags?: string;
690
692
  defaultExcludes?: string;
691
693
  };
692
- declare function tagsForProject({ projectTag, extraTags, defaultExcludes }: TagsForProjectInput): string;
694
+ /** Build a playwright-bdd `tags` expression: default excludes, optional project tag, optional extra tags. */
695
+ declare function tagsForProject({ projectTag, extraTags, defaultExcludes }?: TagsForProjectInput): string;
693
696
  declare function resolveExtraTags(raw?: string | null): string | undefined;
694
697
 
695
698
  /**
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  setupBypassAuth,
35
35
  setupFetchIntercept,
36
36
  tryParseJson
37
- } from "./chunk-ACAXOGKZ.js";
37
+ } from "./chunk-UTTKW2SR.js";
38
38
 
39
39
  // src/fixtures.ts
40
40
  import { test as base } from "playwright-bdd";
@@ -972,9 +972,8 @@ function resetOidcCleanupAuth() {
972
972
  }
973
973
 
974
974
  // src/config.ts
975
- function tagsForProject({ projectTag, extraTags, defaultExcludes = "not @Skip and not @ignore" }) {
976
- if (extraTags) return `${defaultExcludes} and ${projectTag} and (${extraTags})`;
977
- return `${defaultExcludes} and ${projectTag}`;
975
+ function tagsForProject({ projectTag, extraTags, defaultExcludes = "not @Skip and not @ignore" } = {}) {
976
+ return [defaultExcludes, projectTag, extraTags && `(${extraTags})`].filter(Boolean).join(" and ");
978
977
  }
979
978
  function resolveExtraTags(raw) {
980
979
  const tagFilterRaw = raw?.trim();
@@ -36,7 +36,6 @@ declare function registerWizardSteps(test: any): void;
36
36
  * UI Form Step Definitions
37
37
  *
38
38
  * Steps for bulk form filling using Gherkin data tables.
39
- * Tagged with @ui or @hybrid for selective execution.
40
39
  */
41
40
  declare function registerFormSteps(test: any): void;
42
41
 
@@ -44,7 +43,6 @@ declare function registerFormSteps(test: any): void;
44
43
  * UI Debug Step Definitions
45
44
  *
46
45
  * Steps for debugging and troubleshooting UI tests.
47
- * Tagged with @ui or @hybrid for selective execution.
48
46
  */
49
47
  declare function registerDebugSteps(test: any): void;
50
48
 
@@ -52,7 +50,6 @@ declare function registerDebugSteps(test: any): void;
52
50
  * UI Layout Assertion Step Definitions
53
51
  *
54
52
  * Steps for asserting layout states like panels, split views, and responsive behavior.
55
- * Tagged with @ui or @hybrid for selective execution.
56
53
  */
57
54
  declare function registerLayoutSteps(test: any): void;
58
55
 
@@ -60,7 +57,6 @@ declare function registerLayoutSteps(test: any): void;
60
57
  * UI Auth Step Definitions
61
58
  *
62
59
  * Steps for authenticating in UI tests using fetch interception.
63
- * Tagged with @ui for selective execution.
64
60
  */
65
61
  declare function registerUiAuthSteps(test: any): void;
66
62
 
@@ -68,7 +64,6 @@ declare function registerUiAuthSteps(test: any): void;
68
64
  * TUI Basic Step Definitions
69
65
  *
70
66
  * Basic step definitions for terminal user interface testing.
71
- * Tagged with @tui for selective execution.
72
67
  * Aligned with ui.basic.ts patterns for consistency.
73
68
  */
74
69
  declare function registerTuiBasicSteps(test: any): void;
@@ -77,19 +72,16 @@ declare function registerTuiBasicSteps(test: any): void;
77
72
  * TUI Wizard Step Definitions
78
73
  *
79
74
  * Advanced step definitions for terminal user interface testing.
80
- * Tagged with @tui for selective execution.
81
75
  * Aligned with ui.wizard.ts patterns for consistency.
82
76
  */
83
77
  declare function registerTuiWizardSteps(test: any): void;
84
78
 
85
79
  /**
86
80
  * Register all API step definitions.
87
- * Steps are tagged with @api or @hybrid for selective execution.
88
81
  */
89
82
  declare function registerApiSteps(test: any): void;
90
83
  /**
91
84
  * Register all UI step definitions.
92
- * Steps are tagged with @ui or @hybrid for selective execution.
93
85
  *
94
86
  * Includes:
95
87
  * - Basic UI steps (navigation, clicks, fills)
@@ -116,7 +108,6 @@ declare function registerSharedSteps(test: any): void;
116
108
  declare function registerHybridSuite(test: any): void;
117
109
  /**
118
110
  * Register all TUI (Terminal User Interface) step definitions.
119
- * Steps are tagged with @tui for selective execution.
120
111
  *
121
112
  * @example
122
113
  * ```typescript
@@ -22,7 +22,7 @@ import {
22
22
  registerUiSteps,
23
23
  registerWizardSteps,
24
24
  setFlag
25
- } from "../chunk-ACAXOGKZ.js";
25
+ } from "../chunk-UTTKW2SR.js";
26
26
  export {
27
27
  isFlagEnabled,
28
28
  registerAllSteps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esimplicitylabs/katalyst-xspec",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -58,7 +58,7 @@
58
58
  "peerDependencies": {
59
59
  "@playwright/test": "^1.49.0",
60
60
  "playwright-bdd": "^9.1.0",
61
- "tui-tester": "^1.0.0",
61
+ "tui-tester": "^0.3.0",
62
62
  "typescript": "^5.6.0"
63
63
  },
64
64
  "peerDependenciesMeta": {
@@ -164,7 +164,7 @@ const test = createBddTest({
164
164
  ```typescript
165
165
  createApi: (ctx) => {
166
166
  ctx.apiRequest; // Playwright APIRequestContext
167
- ctx.page; // Playwright Page (for @ui)
167
+ ctx.page; // Playwright Page
168
168
  // Return your ApiPort implementation
169
169
  }
170
170
 
@@ -192,14 +192,14 @@ When('I do something with {string}', async ({ world }, param: string) => {
192
192
  world.vars['result'] = param;
193
193
  });
194
194
 
195
- // Step with tag restriction
196
- When('I make API call', { tags: '@api or @hybrid' }, async ({ api, world }) => {
195
+ // Step using the API fixture (no tags needed; works in any scenario)
196
+ When('I make API call', async ({ api, world }) => {
197
197
  const result = await api.sendJson('GET', '/endpoint');
198
198
  world.lastJson = result.json;
199
199
  });
200
200
 
201
201
  // Step with multiple fixtures
202
- When('I verify in both layers', { tags: '@hybrid' }, async ({ api, ui, world }) => {
202
+ When('I verify in both layers', async ({ api, ui, world }) => {
203
203
  await api.sendJson('POST', '/data', { value: 'test' });
204
204
  await ui.goto('/data');
205
205
  await ui.expectText('test');
@@ -481,13 +481,14 @@ selectPath(data, 'user.roles[0]'); // 'admin'
481
481
  ```typescript
482
482
  import { tagsForProject, resolveExtraTags } from '@esimplicitylabs/katalyst-xspec';
483
483
 
484
- // Build tag expression with defaults
485
- tagsForProject({ projectTag: '@api' });
486
- // Result: 'not @Skip and not @ignore and @api'
484
+ // Build tag expression with defaults (projects select features by folder;
485
+ // tags only skip @Skip/@ignore and apply optional TEST_TAGS)
486
+ tagsForProject();
487
+ // Result: 'not @Skip and not @ignore'
487
488
 
488
489
  // With extra tags
489
- tagsForProject({ projectTag: '@api', extraTags: '@smoke' });
490
- // Result: 'not @Skip and not @ignore and @api and (@smoke)'
490
+ tagsForProject({ extraTags: resolveExtraTags(process.env.TEST_TAGS) });
491
+ // e.g. TEST_TAGS=@smoke -> 'not @Skip and not @ignore and (@smoke)'
491
492
  ```
492
493
 
493
494
  ## File Organization
@@ -166,7 +166,7 @@ const test = createBddTest({
166
166
  ```bash
167
167
  # Admin credentials
168
168
  DEFAULT_ADMIN_USERNAME=admin@example.com
169
- DEFAULT_ADMIN_PASSWORD=admin123
169
+ DEFAULT_ADMIN_PASSWORD=changeme
170
170
 
171
171
  # User credentials
172
172
  DEFAULT_USER_USERNAME=user@example.com
@@ -82,7 +82,7 @@ When I POST "/users" with JSON body:
82
82
  ```typescript
83
83
  import { DataTable } from '@cucumber/cucumber';
84
84
 
85
- When('I fill the form:', async ({ ui }, dataTable: DataTable) => {
85
+ When('I fill the signup form:', async ({ ui }, dataTable: DataTable) => {
86
86
  const rows = dataTable.hashes();
87
87
  // rows = [{ Field: 'Email', Value: 'test@...' }, ...]
88
88
 
@@ -94,7 +94,7 @@ When('I fill the form:', async ({ ui }, dataTable: DataTable) => {
94
94
 
95
95
  **Usage:**
96
96
  ```gherkin
97
- When I fill the form:
97
+ When I fill the signup form:
98
98
  | Field | Value |
99
99
  | Email | test@example.com |
100
100
  | Password | secret123 |
@@ -120,30 +120,26 @@ const pairs = dataTable.rowsHash();
120
120
  // { Email: '...', Password: '...' }
121
121
  ```
122
122
 
123
- ## Tag-Restricted Steps
123
+ ## Step Scope
124
+
125
+ Custom steps don't need `{ tags: ... }`. Like the built-in steps, a step without tags works in any scenario, whichever fixtures it uses:
124
126
 
125
127
  ```typescript
126
- // Only available in @api scenarios
127
- When('I make an API call', { tags: '@api' }, async ({ api }) => {
128
+ When('I make an API call', async ({ api }) => {
128
129
  // ...
129
130
  });
130
131
 
131
- // Available in @api or @hybrid
132
- When('I GET {string}', { tags: '@api or @hybrid' }, async ({ api, world }, path) => {
132
+ When('I click something', async ({ ui }) => {
133
133
  // ...
134
134
  });
135
135
 
136
- // Only available in @ui
137
- When('I click something', { tags: '@ui' }, async ({ ui }) => {
136
+ When('I create via API and check in UI', async ({ api, ui, world }) => {
138
137
  // ...
139
138
  });
140
-
141
- // Available everywhere (no tag restriction)
142
- Given('I set variable {string} to {string}', async ({ world }, name, value) => {
143
- world.vars[name] = value;
144
- });
145
139
  ```
146
140
 
141
+ Don't reuse wording of a built-in step (e.g. `I GET {string}`, `I set variable {string} to {string}`); a step text that matches two definitions is ambiguous.
142
+
147
143
  ## Available Fixtures
148
144
 
149
145
  Steps receive these fixtures:
@@ -285,7 +281,7 @@ import { expect } from '@playwright/test';
285
281
  * Custom steps for report generation testing
286
282
  */
287
283
 
288
- When('I generate a {string} report', { tags: '@api or @hybrid' },
284
+ When('I generate a {string} report',
289
285
  async ({ api, world }, reportType: string) => {
290
286
  const result = await api.sendJson('POST', '/reports/generate', {
291
287
  type: reportType,
@@ -301,7 +297,7 @@ When('I generate a {string} report', { tags: '@api or @hybrid' },
301
297
  }
302
298
  );
303
299
 
304
- When('I wait for the report to complete', { tags: '@api or @hybrid' },
300
+ When('I wait for the report to complete',
305
301
  async ({ api, world }) => {
306
302
  const reportId = world.vars['reportId'];
307
303
  let attempts = 0;
@@ -323,7 +319,7 @@ When('I wait for the report to complete', { tags: '@api or @hybrid' },
323
319
  }
324
320
  );
325
321
 
326
- Then('the report should be downloadable', { tags: '@api or @hybrid' },
322
+ Then('the report should be downloadable',
327
323
  async ({ api, world }) => {
328
324
  const reportUrl = world.vars['reportUrl'];
329
325
  expect(reportUrl).toBeDefined();
@@ -334,14 +330,14 @@ Then('the report should be downloadable', { tags: '@api or @hybrid' },
334
330
  }
335
331
  );
336
332
 
337
- When('I view the report in the UI', { tags: '@ui or @hybrid' },
333
+ When('I view the report in the UI',
338
334
  async ({ ui, world }) => {
339
335
  const reportId = world.vars['reportId'];
340
336
  await ui.goto(`/reports/${reportId}`);
341
337
  }
342
338
  );
343
339
 
344
- Then('I should see the report preview', { tags: '@ui or @hybrid' },
340
+ Then('I should see the report preview',
345
341
  async ({ ui }) => {
346
342
  await ui.expectText('Report Preview');
347
343
  await ui.expectElementState('first', 'pdf-viewer', 'locator', 'visible');
@@ -1,39 +1,42 @@
1
1
  ---
2
2
  name: katalyst-bdd-create-test
3
- description: Create BDD tests for the Katalyst framework. Use when writing new feature files, creating test scenarios, choosing test types (@api, @ui, @tui, @hybrid), or implementing common testing patterns like CRUD operations, login flows, form handling, or API+UI verification workflows.
3
+ description: Create BDD tests for the Katalyst framework. Use when writing new feature files, creating test scenarios, choosing where to put a test (features/api, features/ui, features/tui) or mixing API and UI steps, or implementing common testing patterns like CRUD operations, login flows, form handling, or API+UI verification workflows.
4
4
  ---
5
5
 
6
6
  # Katalyst BDD Test Creation Guide
7
7
 
8
8
  This skill guides you through creating BDD tests with the Katalyst framework.
9
9
 
10
- ## Test Type Decision Tree
10
+ ## Where to Put a Test
11
11
 
12
- Choose the right tag based on what you're testing:
12
+ Steps are untagged: any step works in any scenario. Do NOT add `@api`/`@ui`/`@hybrid`/`@tui` tags or `{ tags: ... }` on steps. Each Playwright project picks feature files by folder, so just choose the folder:
13
13
 
14
14
  ```
15
15
  What are you testing?
16
16
  │
17
- ├─ HTTP API only → @api
17
+ ├─ HTTP API only → features/api/
18
18
  │ (REST endpoints, JSON responses, status codes)
19
19
  │
20
- ├─ Browser UI only → @ui
20
+ ├─ Browser UI only → features/ui/
21
21
  │ (Pages, forms, buttons, navigation)
22
22
  │
23
- ├─ Terminal UI only → @tui
23
+ ├─ Terminal UI only → features/tui/ (enable the tui project first)
24
24
  │ (CLI apps, interactive terminal programs)
25
25
  │
26
- └─ Multiple layers → @hybrid
26
+ └─ API + UI in one scenario → features/ui/ (it needs a browser)
27
27
  (Create via API, verify in UI)
28
28
  (Setup data, then test UI flows)
29
29
  ```
30
30
 
31
+ Tags are optional and only for the user's own grouping (`@smoke`, `@wip`, `@regression`).
32
+
31
33
  ## Feature File Structure
32
34
 
33
35
  Every feature file follows this structure:
34
36
 
35
37
  ```gherkin
36
- @tag
38
+ # Tags are optional; this one is your own grouping
39
+ @smoke
37
40
  Feature: Feature Name
38
41
  As a [role]
39
42
  I want [capability]
@@ -48,14 +51,13 @@ Feature: Feature Name
48
51
  Then [expected outcome]
49
52
  ```
50
53
 
51
- ## Creating API Tests (`@api`)
54
+ ## Creating API Tests
52
55
 
53
56
  ### Step 1: Create Feature File
54
57
 
55
58
  Location: `features/api/[resource].feature`
56
59
 
57
60
  ```gherkin
58
- @api
59
61
  Feature: [Resource] API
60
62
  As a developer
61
63
  I want to test the [Resource] API
@@ -126,14 +128,13 @@ Scenario: Full [resource] lifecycle
126
128
 
127
129
  See [API Patterns](references/api-patterns.md) for more examples.
128
130
 
129
- ## Creating UI Tests (`@ui`)
131
+ ## Creating UI Tests
130
132
 
131
133
  ### Step 1: Create Feature File
132
134
 
133
135
  Location: `features/ui/[page].feature`
134
136
 
135
137
  ```gherkin
136
- @ui
137
138
  Feature: [Page Name]
138
139
  As a user
139
140
  I want to [action on page]
@@ -176,14 +177,13 @@ Scenario: User login
176
177
 
177
178
  See [UI Patterns](references/ui-patterns.md) for more examples.
178
179
 
179
- ## Creating TUI Tests (`@tui`)
180
+ ## Creating TUI Tests
180
181
 
181
182
  ### Step 1: Create Feature File
182
183
 
183
184
  Location: `features/tui/[command].feature`
184
185
 
185
186
  ```gherkin
186
- @tui
187
187
  Feature: [CLI Command]
188
188
  As a user
189
189
  I want to use the [command] CLI
@@ -192,7 +192,7 @@ Feature: [CLI Command]
192
192
 
193
193
  ### Step 2: Configure TUI in Fixtures
194
194
 
195
- Ensure your `fixtures.ts` has TUI configured:
195
+ TUI is off in the scaffold. Uncomment `registerTuiSteps(test)` in `steps.ts`, the `tuiBdd` project in `playwright.config.ts`, and configure TUI in `fixtures.ts`:
196
196
 
197
197
  ```typescript
198
198
  createTui: () => new TuiTesterAdapter({
@@ -224,14 +224,15 @@ Scenario: Navigate menu
224
224
 
225
225
  See [TUI Patterns](references/tui-patterns.md) for more examples.
226
226
 
227
- ## Creating Hybrid Tests (`@hybrid`)
227
+ ## Creating Hybrid Tests (API + UI)
228
+
229
+ A hybrid test is just a scenario that mixes API and UI steps. No tag or special project is needed.
228
230
 
229
231
  ### Step 1: Create Feature File
230
232
 
231
- Location: `features/hybrid/[workflow].feature`
233
+ Location: `features/ui/[workflow].feature` (or any folder a project reads)
232
234
 
233
235
  ```gherkin
234
- @hybrid
235
236
  Feature: [Workflow Name]
236
237
  As a tester
237
238
  I want to combine API and UI testing
@@ -336,21 +337,23 @@ And I store the value at "token" as "t"
336
337
  After creating feature files:
337
338
 
338
339
  ```bash
339
- # 1. Generate Playwright tests (REQUIRED)
340
- npm run gen
340
+ # One-time: browser for UI tests
341
+ npx playwright install chromium
341
342
 
342
- # 2. Run all tests
343
+ # Generate specs and run all tests (npm test runs bddgen first)
343
344
  npm test
344
345
 
345
- # 3. Run specific project
346
- npx playwright test --project=api
347
- npx playwright test --project=ui
348
- npx playwright test --project=hybrid
346
+ # Run one project (folder)
347
+ npx playwright test --project api
348
+ npx playwright test --project ui
349
+
350
+ # Run only scenarios with your own tag
351
+ TEST_TAGS=@smoke npm test
349
352
 
350
- # 4. Run specific feature
353
+ # Run specific feature (after npm run gen)
351
354
  npx playwright test features/api/users.feature
352
355
 
353
- # 5. Debug mode
356
+ # Debug mode
354
357
  npx playwright test --debug
355
358
  ```
356
359
 
@@ -358,8 +361,10 @@ npx playwright test --debug
358
361
 
359
362
  | Mistake | Solution |
360
363
  |---------|----------|
361
- | Forgot to run `npm run gen` | Always run after creating/modifying features |
362
- | Steps not available | Check you have the correct tag (`@api`, `@ui`, etc.) |
364
+ | Forgot to run `npm run gen` | `npm test` does it; run it yourself before `npx playwright test` |
365
+ | Adding `@api`/`@ui`/`@hybrid` tags | Not needed; steps work everywhere, projects select by folder |
366
+ | Scenario never runs | Check the feature is in a folder a project reads |
367
+ | Step not found | Check exact wording in `katalyst-bdd-step-reference` (TUI: `I fill the TUI form:`) |
363
368
  | Hardcoded test data | Use UUID generation for unique data |
364
369
  | No cleanup registered | Always register cleanup for created resources |
365
370
  | Scenarios depend on each other | Make each scenario independent |
@@ -7,7 +7,6 @@ Common patterns for API testing with the Katalyst BDD framework.
7
7
  ### List Resources
8
8
 
9
9
  ```gherkin
10
- @api
11
10
  Scenario: List all [resources]
12
11
  Given I am authenticated as an admin via API
13
12
  When I GET "/[endpoint]"
@@ -18,7 +17,6 @@ Scenario: List all [resources]
18
17
  ### Get Single Resource
19
18
 
20
19
  ```gherkin
21
- @api
22
20
  Scenario: Get [resource] by ID
23
21
  Given I am authenticated as an admin via API
24
22
  When I GET "/[endpoint]/1"
@@ -30,7 +28,6 @@ Scenario: Get [resource] by ID
30
28
  ### Create Resource
31
29
 
32
30
  ```gherkin
33
- @api
34
31
  Scenario: Create [resource]
35
32
  Given I am authenticated as an admin via API
36
33
  Given I generate a UUID and store as "runId"
@@ -50,7 +47,6 @@ Scenario: Create [resource]
50
47
  ### Update Resource (Full)
51
48
 
52
49
  ```gherkin
53
- @api
54
50
  Scenario: Update [resource] with PUT
55
51
  Given I am authenticated as an admin via API
56
52
  # First create the resource
@@ -75,7 +71,6 @@ Scenario: Update [resource] with PUT
75
71
  ### Update Resource (Partial)
76
72
 
77
73
  ```gherkin
78
- @api
79
74
  Scenario: Partial update with PATCH
80
75
  Given I am authenticated as an admin via API
81
76
  When I PATCH "/[endpoint]/{resourceId}" with JSON body:
@@ -89,7 +84,6 @@ Scenario: Partial update with PATCH
89
84
  ### Delete Resource
90
85
 
91
86
  ```gherkin
92
- @api
93
87
  Scenario: Delete [resource]
94
88
  Given I am authenticated as an admin via API
95
89
  # Create resource to delete
@@ -155,7 +149,6 @@ Scenario: Login and use token
155
149
  ### Not Found
156
150
 
157
151
  ```gherkin
158
- @api
159
152
  Scenario: Resource not found
160
153
  Given I am authenticated as an admin via API
161
154
  When I GET "/[endpoint]/99999"
@@ -165,7 +158,6 @@ Scenario: Resource not found
165
158
  ### Validation Error
166
159
 
167
160
  ```gherkin
168
- @api
169
161
  Scenario: Invalid data returns 400
170
162
  Given I am authenticated as an admin via API
171
163
  When I POST "/[endpoint]" with JSON body:
@@ -179,7 +171,6 @@ Scenario: Invalid data returns 400
179
171
  ### Unauthorized
180
172
 
181
173
  ```gherkin
182
- @api
183
174
  Scenario: Unauthorized access
184
175
  # No authentication
185
176
  When I GET "/admin/users"
@@ -189,7 +180,6 @@ Scenario: Unauthorized access
189
180
  ### Forbidden
190
181
 
191
182
  ```gherkin
192
- @api
193
183
  Scenario: User cannot access admin endpoint
194
184
  Given I am authenticated as a user via API
195
185
  When I GET "/admin/settings"
@@ -246,7 +236,6 @@ Then the value at "created_at" should match "^\d{4}-\d{2}-\d{2}"
246
236
  ### Pagination
247
237
 
248
238
  ```gherkin
249
- @api
250
239
  Scenario: Paginated list
251
240
  Given I am authenticated as an admin via API
252
241
  When I GET "/[endpoint]?page=1&limit=10"
@@ -260,7 +249,6 @@ Scenario: Paginated list
260
249
  ### Search/Filter
261
250
 
262
251
  ```gherkin
263
- @api
264
252
  Scenario: Filter by status
265
253
  Given I am authenticated as an admin via API
266
254
  When I GET "/[endpoint]?status=active"
@@ -271,7 +259,6 @@ Scenario: Filter by status
271
259
  ### Bulk Operations
272
260
 
273
261
  ```gherkin
274
- @api
275
262
  Scenario: Bulk create
276
263
  Given I am authenticated as an admin via API
277
264
  When I POST "/[endpoint]/bulk" with JSON body:
@@ -291,7 +278,6 @@ Scenario: Bulk create
291
278
  ### File Upload (Form Data)
292
279
 
293
280
  ```gherkin
294
- @api
295
281
  Scenario: Upload file
296
282
  Given I am authenticated as an admin via API
297
283
  Given I set header "Content-Type" to "multipart/form-data"
@@ -301,7 +287,6 @@ Scenario: Upload file
301
287
  ## Complete Example: User Management API
302
288
 
303
289
  ```gherkin
304
- @api
305
290
  Feature: User Management API
306
291
  As an admin
307
292
  I want to manage users via API