cypress-conditional-tags 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-03-21
9
+
10
+ ### Added
11
+ - Initial release of cypress-conditional-tags
12
+ - Runtime tag access in Cypress tests
13
+ - Synchronous API (`Cypress.Tags.get()`, `has()`, `hasAny()`, `hasAll()`)
14
+ - Asynchronous API (`cy.getTags()`, `cy.hasTag()`, `cy.hasAnyTag()`, `cy.hasAllTags()`)
15
+ - Tag inheritance from parent `describe` blocks
16
+ - Support for tags with numbers and underscores
17
+ - Full TypeScript definitions
18
+ - Comprehensive documentation and examples
19
+ - Test suite for plugin functionality
20
+
21
+ ### Features
22
+ - Parse tags from test titles using `@tagname` syntax
23
+ - Store tags at runtime for conditional test logic
24
+ - Enable environment-specific test behavior
25
+ - Support for multiple tag checks (any/all)
26
+ - Clean test titles in output (tags removed)
27
+ - Preserve Cypress modifiers (`.only`, `.skip`)
28
+
29
+ ### Documentation
30
+ - README.md with comprehensive usage guide
31
+ - QUICK_START.md for rapid setup
32
+ - ARCHITECTURE.md explaining internal design
33
+ - TESTING.md for testing the plugin
34
+ - Multiple example files demonstrating real-world usage
35
+
36
+ ### Examples
37
+ - Basic usage examples
38
+ - Advanced usage scenarios (e-commerce, performance, security)
39
+ - Async command examples
40
+ - Custom command integration
41
+
42
+ ### Compatibility
43
+ - Cypress >= 10.0.0
44
+ - Tested with Cypress 15.12.0
45
+ - Node.js >= 14.0.0
46
+ - Full TypeScript support
47
+
48
+ ## [Unreleased]
49
+
50
+ ### Planned
51
+ - Tag metadata support (priority, owner, etc.)
52
+ - Tag validation and warnings
53
+ - Integration with test reporters
54
+ - Tag-based test grouping
55
+ - Performance optimizations
56
+ - Additional examples and use cases
57
+
58
+ ---
59
+
60
+ ## Version History
61
+
62
+ ### Version 1.0.0
63
+ First stable release with core functionality for runtime tag access in Cypress tests.
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jaydip Maniya (Personal)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,423 @@
1
+ # Cypress Conditional Tags
2
+
3
+ Access runtime tags (from CLI) and test tags (from config) inside Cypress test logic!
4
+
5
+ ## ⚠️ Important: What This Plugin Does
6
+
7
+ This plugin provides **conditional logic** based on tags - it does **NOT filter/skip tests**.
8
+
9
+ **Use this plugin when you need to:**
10
+ - Execute different code paths based on environment tags
11
+ - Access tag information inside test logic
12
+ - Make runtime decisions based on CLI tags
13
+
14
+
15
+ ## The Problem
16
+ As test suites grow, tests often need to behave differently depending on context:
17
+
18
+ - Different environments
19
+ - Feature flags
20
+ - Test types (smoke, regression, critical)
21
+ - Debug vs CI execution
22
+ - Data variations
23
+ - Region or tenant-specific logic
24
+
25
+ What usually happens?
26
+
27
+ Teams end up with:
28
+
29
+ - Hardcoded values inside tests
30
+ - Scattered conditional logic
31
+ - Multiple config switches
32
+ - Duplicate test cases for different scenarios
33
+
34
+ This makes test suites:
35
+
36
+ - Hard to maintain
37
+ - Difficult to scale
38
+ - Less readable over time
39
+
40
+ ## The Solution
41
+
42
+ This plugin makes both runtime tags (from CLI) and test tags (from config) accessible inside your tests:
43
+
44
+ ```javascript
45
+ it('Test Case 01', { tags: ['IS_QA', 'IS_GA'] }, () => {
46
+ // Access runtime tag from CLI (passed via --env conditionalTags=IS_QA)
47
+ if (Cypress.Tags.isRuntime('IS_QA')) {
48
+ cy.log('Running in QA mode')
49
+ cy.visit('https://qa.example.com')
50
+ } else if (Cypress.Tags.isRuntime('IS_GA')) {
51
+ cy.log('Running in GA mode')
52
+ cy.visit('https://ga.example.com')
53
+ }
54
+
55
+ // Access test's configured tags
56
+ const testTags = Cypress.Tags.getTest()
57
+ cy.log(`Test tags: ${testTags.join(', ')}`)
58
+ })
59
+ ```
60
+
61
+ ## Environment Variable
62
+
63
+ **Use `conditionalTags`** to pass runtime tags:
64
+
65
+ ```bash
66
+ npx cypress run --env conditionalTags=IS_QA
67
+ ```
68
+
69
+ ```bash
70
+ # Filter tests with tag2, use tag5+tag6 for conditional logic
71
+ npx cypress run --env conditionalTags=tag5+tag6
72
+ ```
73
+
74
+
75
+ ## Installation
76
+
77
+ ```bash
78
+ npm install cypress-conditional-tags --save-dev
79
+ ```
80
+
81
+ ## Setup
82
+
83
+ ### 1. Configure Cypress (cypress.config.js)
84
+
85
+ ```javascript
86
+ const { defineConfig } = require('cypress')
87
+ const { setupNodeEvents } = require('cypress-conditional-tags')
88
+
89
+ module.exports = defineConfig({
90
+ e2e: {
91
+ setupNodeEvents(on, config) {
92
+ return setupNodeEvents(on, config)
93
+ },
94
+ },
95
+ })
96
+ ```
97
+
98
+ ### 2. Setup Support File (cypress/support/e2e.js)
99
+
100
+ ```javascript
101
+ const { setupSupport } = require('cypress-conditional-tags')
102
+
103
+ setupSupport()
104
+ ```
105
+
106
+ That's it! You're ready to use runtime tags.
107
+
108
+ ## Usage
109
+
110
+ ### Adding Tags to Tests
111
+
112
+ Use Cypress's native `tags` option in test configuration:
113
+
114
+ ```javascript
115
+ describe('Login Tests', () => {
116
+ it('should login with valid credentials', { tags: ['IS_QA', 'SMOKE'] }, () => {
117
+ // Test code
118
+ })
119
+
120
+ it('should show error for invalid credentials', { tags: ['IS_QA', 'REGRESSION'] }, () => {
121
+ // Test code
122
+ })
123
+ })
124
+ ```
125
+
126
+ ### Running Tests with Tags
127
+
128
+
129
+ #### Option 1: Conditional Logic Only (This Plugin Standalone)
130
+ Use `conditionalTags` for conditional logic - **all tests will run**:
131
+
132
+ ```bash
133
+ # Pass runtime tag for conditional logic using conditionalTags
134
+ npx cypress run --env conditionalTags=IS_QA
135
+
136
+ # Result: All tests execute, but Cypress.Tags.isRuntime('IS_QA') returns true
137
+
138
+ # Multiple tags for conditional logic
139
+ npx cypress run --env conditionalTags=tag5+tag6
140
+ ```
141
+
142
+ #### Option 2: Test Filtering + Conditional Logic (Both Plugins)
143
+ Use **separate environment variables** for filtering vs conditional logic:
144
+
145
+ **Setup:**
146
+ ```bash
147
+ # 1. Install @cypress/grep
148
+ npm install --save-dev @cypress/grep
149
+
150
+ # 2. Update cypress.config.js
151
+ const { defineConfig } = require('cypress')
152
+ const { setupNodeEvents } = require('cypress-conditional-tags')
153
+ const registerCypressGrep = require('@cypress/grep/src/plugin')
154
+
155
+ module.exports = defineConfig({
156
+ e2e: {
157
+ setupNodeEvents(on, config) {
158
+ setupNodeEvents(on, config)
159
+ registerCypressGrep(config) // Add grep for filtering
160
+ return config
161
+ },
162
+ },
163
+ })
164
+
165
+ # 3. Update cypress/support/e2e.js
166
+ const { setupSupport } = require('cypress-conditional-tags')
167
+ require('@cypress/grep')() // Add grep support
168
+
169
+ setupSupport()
170
+ ```
171
+
172
+ **Usage - Independent Control:**
173
+ ```bash
174
+ # Filter tests with tag2, use tag5+tag6 for conditional logic
175
+ npx cypress run --env grepTags=tag2,conditionalTags=tag5+tag6
176
+
177
+ # What happens:
178
+ # 1. @cypress/grep filters: Only runs tests with 'tag2'
179
+ # 2. Inside those tests: Cypress.Tags.isRuntime('tag5') returns true
180
+ # Cypress.Tags.isRuntime('tag6') returns true
181
+
182
+ # Another example: Filter with multiple tags, different conditional tags
183
+ npx cypress run --env grepTags=SMOKE+REGRESSION,conditionalTags=IS_QA
184
+
185
+ # Result:
186
+ # - Runs tests tagged with SMOKE OR REGRESSION (filtering)
187
+ # - Inside tests: Cypress.Tags.isRuntime('IS_QA') returns true (conditional logic)
188
+ ```
189
+
190
+ **Key Benefits:**
191
+ - ✅ **Independent control**: Filter tests with one set of tags, use different tags for logic
192
+ - ✅ **No conflicts**: `grepTags` for filtering, `conditionalTags` for logic
193
+ - ✅ **Backward compatible**: Still supports `grepTags` for conditional logic if `conditionalTags` not provided
194
+
195
+ ### Accessing Tags at Runtime
196
+
197
+ #### Method 1: Synchronous Access (Recommended)
198
+
199
+ Use `Cypress.Tags` for immediate, synchronous access:
200
+
201
+ ```javascript
202
+ it('should test feature', { tags: ['IS_QA', 'SMOKE'] }, () => {
203
+ // Get runtime tag from CLI
204
+ const runtimeTag = Cypress.Tags.getRuntime()
205
+ console.log(runtimeTag) // 'IS_QA' (if run with --env conditionalTags=IS_QA)
206
+
207
+ // Check if running with specific tag
208
+ if (Cypress.Tags.isRuntime('IS_QA')) {
209
+ cy.log('Running in QA mode')
210
+ }
211
+
212
+ // Check for any of multiple tags
213
+ if (Cypress.Tags.isAnyRuntime('IS_QA', 'IS_STAGING')) {
214
+ cy.log('Running in non-production environment')
215
+ }
216
+
217
+ // Get test's configured tags
218
+ const testTags = Cypress.Tags.getTest()
219
+ console.log(testTags) // ['IS_QA', 'SMOKE']
220
+
221
+ // Check if test has specific tag
222
+ if (Cypress.Tags.hasTest('SMOKE')) {
223
+ cy.log('This is a smoke test')
224
+ }
225
+
226
+ // Check if test matches runtime tag
227
+ if (Cypress.Tags.matchesRuntime()) {
228
+ cy.log('Test has the runtime tag!')
229
+ }
230
+ })
231
+ ```
232
+
233
+ #### Method 2: Cypress Commands (Async)
234
+
235
+ Use custom Cypress commands for chainable operations:
236
+
237
+ ```javascript
238
+ it('should test feature', { tags: ['IS_QA', 'SMOKE'] }, () => {
239
+ cy.getRuntimeTag().then(tag => {
240
+ cy.log(`Runtime tag: ${tag}`)
241
+ })
242
+
243
+ cy.isRuntimeTag('IS_QA').then(isQA => {
244
+ if (isQA) {
245
+ cy.log('Running in QA mode')
246
+ }
247
+ })
248
+
249
+ cy.getTestTags().then(tags => {
250
+ expect(tags).to.include('SMOKE')
251
+ })
252
+
253
+ cy.hasTestTag('SMOKE').then(hasIt => {
254
+ if (hasIt) {
255
+ cy.log('This is a smoke test')
256
+ }
257
+ })
258
+ })
259
+ ```
260
+
261
+ ## Real-World Examples
262
+
263
+ ### Example 1: Environment-Specific URLs
264
+
265
+ ```javascript
266
+ it('should use correct environment', { tags: ['IS_QA', 'IS_PROD'] }, () => {
267
+ const baseUrl = Cypress.Tags.isRuntime('IS_QA')
268
+ ? 'https://qa-api.example.com'
269
+ : Cypress.Tags.isRuntime('IS_PROD')
270
+ ? 'https://api.example.com'
271
+ : 'https://dev-api.example.com'
272
+
273
+ cy.request(`${baseUrl}/users`)
274
+ })
275
+ ```
276
+
277
+ ### Example 2: Conditional Test Steps
278
+
279
+ ```javascript
280
+ it('should test checkout', { tags: ['FAST', 'FULL'] }, () => {
281
+ cy.visit('/cart')
282
+ cy.get('[data-cy=checkout]').click()
283
+
284
+ // Skip expensive operations for FAST tests
285
+ if (!Cypress.Tags.isRuntime('FAST')) {
286
+ cy.get('[data-cy=order-summary]').should('be.visible')
287
+ cy.get('[data-cy=shipping-info]').should('be.visible')
288
+ cy.get('[data-cy=payment-info]').should('be.visible')
289
+ }
290
+
291
+ cy.get('[data-cy=place-order]').click()
292
+ })
293
+ ```
294
+
295
+ ### Example 3: Environment-Specific Test Data
296
+
297
+ ```javascript
298
+ it('should process data', { tags: ['IS_QA', 'SMALL_DATA', 'LARGE_DATA'] }, () => {
299
+ const recordCount = Cypress.Tags.isRuntime('LARGE_DATA') ? 10000 : 100
300
+
301
+ cy.request('POST', '/api/generate-data', { count: recordCount })
302
+ cy.visit('/data-viewer')
303
+ cy.get('[data-cy=record-count]').should('contain', recordCount)
304
+ })
305
+ ```
306
+
307
+ ### Example 4: Multiple Tag Conditions
308
+
309
+ ```javascript
310
+ it('should validate production', { tags: ['IS_PROD', 'CRITICAL'] }, () => {
311
+ // Extra validations for critical production tests
312
+ if (Cypress.Tags.isAllRuntime('IS_PROD', 'CRITICAL')) {
313
+ cy.log('Running critical production validations')
314
+ cy.request('/health').its('status').should('eq', 200)
315
+ cy.request('/metrics').its('status').should('eq', 200)
316
+ }
317
+
318
+ // Run for any smoke or regression test
319
+ if (Cypress.Tags.hasAnyTest('SMOKE', 'REGRESSION')) {
320
+ cy.visit('/home')
321
+ cy.get('h1').should('be.visible')
322
+ }
323
+ })
324
+ ```
325
+
326
+ ### Example 5: Feature Flags
327
+
328
+ ```javascript
329
+ it('should test features', { tags: ['IS_QA', 'NEW_FEATURE'] }, () => {
330
+ const features = {
331
+ newCheckout: Cypress.Tags.isRuntime('IS_QA'),
332
+ betaFeatures: Cypress.Tags.hasTest('NEW_FEATURE')
333
+ }
334
+
335
+ if (features.newCheckout) {
336
+ cy.log('Testing new checkout flow')
337
+ cy.visit('/checkout-v2')
338
+ } else {
339
+ cy.visit('/checkout')
340
+ }
341
+ })
342
+ ```
343
+
344
+ ## API Reference
345
+
346
+ ### Synchronous API (Cypress.Tags)
347
+
348
+ | Method | Description | Returns | Example |
349
+ |--------|-------------|---------|---------|
350
+ | `getRuntime()` | Get runtime tag from CLI | `string \| null` | `Cypress.Tags.getRuntime()` |
351
+ | `getAllRuntime()` | Get all runtime tags | `string[]` | `Cypress.Tags.getAllRuntime()` |
352
+ | `isRuntime(tag)` | Check if runtime tag matches | `boolean` | `Cypress.Tags.isRuntime('IS_QA')` |
353
+ | `isAnyRuntime(...tags)` | Check if any runtime tag matches | `boolean` | `Cypress.Tags.isAnyRuntime('IS_QA', 'IS_STAGING')` |
354
+ | `isAllRuntime(...tags)` | Check if all runtime tags match | `boolean` | `Cypress.Tags.isAllRuntime('IS_QA', 'SMOKE')` |
355
+ | `getTest()` | Get test's configured tags | `string[]` | `Cypress.Tags.getTest()` |
356
+ | `hasTest(tag)` | Check if test has specific tag | `boolean` | `Cypress.Tags.hasTest('SMOKE')` |
357
+ | `hasAnyTest(...tags)` | Check if test has any tag | `boolean` | `Cypress.Tags.hasAnyTest('SMOKE', 'REGRESSION')` |
358
+ | `hasAllTest(...tags)` | Check if test has all tags | `boolean` | `Cypress.Tags.hasAllTest('SMOKE', 'CRITICAL')` |
359
+ | `matchesRuntime()` | Check if test has runtime tag | `boolean` | `Cypress.Tags.matchesRuntime()` |
360
+
361
+ ### Async API (Cypress Commands)
362
+
363
+ | Command | Description | Returns | Example |
364
+ |---------|-------------|---------|---------|
365
+ | `cy.getRuntimeTag()` | Get runtime tag from CLI | `Chainable<string \| null>` | `cy.getRuntimeTag().then(tag => ...)` |
366
+ | `cy.getAllRuntimeTags()` | Get all runtime tags | `Chainable<string[]>` | `cy.getAllRuntimeTags().then(tags => ...)` |
367
+ | `cy.isRuntimeTag(tag)` | Check if runtime tag matches | `Chainable<boolean>` | `cy.isRuntimeTag('IS_QA').then(is => ...)` |
368
+ | `cy.getTestTags()` | Get test's configured tags | `Chainable<string[]>` | `cy.getTestTags().then(tags => ...)` |
369
+ | `cy.hasTestTag(tag)` | Check if test has specific tag | `Chainable<boolean>` | `cy.hasTestTag('SMOKE').then(has => ...)` |
370
+ | `cy.matchesRuntimeTag()` | Check if test has runtime tag | `Chainable<boolean>` | `cy.matchesRuntimeTag().then(matches => ...)` |
371
+
372
+ ## TypeScript Support
373
+
374
+ Full TypeScript definitions are included. No additional setup required!
375
+
376
+ ```typescript
377
+ it('should test', { tags: ['IS_QA', 'SMOKE'] }, () => {
378
+ const runtimeTag: string | null = Cypress.Tags.getRuntime()
379
+ const testTags: string[] = Cypress.Tags.getTest()
380
+ const isQA: boolean = Cypress.Tags.isRuntime('IS_QA')
381
+
382
+ cy.getRuntimeTag().then((tag: string | null) => {
383
+ // TypeScript knows the type
384
+ })
385
+ })
386
+ ```
387
+
388
+ ## How It Works
389
+
390
+ 1. **Tag Storage**: Plugin wraps `it()` to extract tags from test config
391
+ 2. **Runtime Access**: Reads `Cypress.env('conditionalTags')` for CLI-passed tags
392
+ 3. **API Exposure**: Provides `Cypress.Tags` API and custom commands
393
+ 4. **Compatibility**: Works seamlessly with `@cypress/grep`
394
+
395
+ ## Compatibility
396
+
397
+ - **Cypress**: >= 10.0.0
398
+ - **Node.js**: >= 14.0.0
399
+ - **TypeScript**: Full support included
400
+ - **@cypress/grep**: Fully compatible
401
+
402
+ ## Best Practices
403
+
404
+ 1. **Use Descriptive Tag Names**: `IS_QA`, `IS_PROD`, `SMOKE`, `REGRESSION` are better than `Q`, `P`, `S`, `R`
405
+ 2. **Prefer Synchronous API**: Use `Cypress.Tags.isRuntime()` over `cy.isRuntimeTag()` for simpler code
406
+ 3. **Combine with @cypress/grep**: Use for filtering + runtime logic
407
+ 4. **Document Tags**: Maintain a list of available tags in your project
408
+ 5. **Environment Tags**: Use `IS_QA`, `IS_STAGING`, `IS_PROD` for environment-specific logic
409
+ 6. **Test Type Tags**: Use `SMOKE`, `REGRESSION`, `CRITICAL` for test categorization
410
+
411
+
412
+ ## Contributing
413
+
414
+ Contributions are welcome! Please feel free to submit a Pull Request.
415
+
416
+ ## License
417
+
418
+ MIT
419
+
420
+ ## Support
421
+
422
+ - 🐛 [Report Issues](https://github.com/JaydipManiya/cypress-conditional-tags/issues)
423
+ - 📖 [Documentation](https://github.com/JaydipManiya/cypress-conditional-tags#readme)
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "cypress-conditional-tags",
3
+ "version": "1.0.0",
4
+ "description": "Access runtime tags (from CLI) and test tags (from config) inside Cypress test logic",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "keywords": [
8
+ "cypress",
9
+ "cypress-plugin",
10
+ "tags",
11
+ "test-tags",
12
+ "runtime-tags",
13
+ "conditional-testing",
14
+ "test-filtering"
15
+ ],
16
+ "author": "Jaydip Maniya",
17
+ "license": "MIT",
18
+ "peerDependencies": {
19
+ "cypress": ">=10.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "cypress": "^15.12.0",
23
+ "mocha": "^10.2.0",
24
+ "chai": "^4.3.10"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/JaydipManiya/cypress-conditional-tags.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/JaydipManiya/cypress-conditional-tags/issues"
32
+ },
33
+ "homepage": "https://github.com/JaydipManiya/cypress-conditional-tags#readme",
34
+ "scripts": {
35
+ "test": "npm run test:unit && npm run test:e2e",
36
+ "test:unit": "mocha test/unit/**/*.test.js",
37
+ "test:e2e": "cypress run --config-file test/e2e/cypress.config.js",
38
+ "test:e2e:open": "cypress open --config-file test/e2e/cypress.config.js"
39
+ },
40
+ "files": [
41
+ "src/",
42
+ "README.md",
43
+ "LICENSE"
44
+ ]
45
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,263 @@
1
+ /**
2
+ * TypeScript definitions for cypress-conditional-tags
3
+ */
4
+
5
+ declare namespace Cypress {
6
+ interface Chainable {
7
+ /**
8
+ * Get the runtime tag passed via CLI (async)
9
+ * @example
10
+ * cy.getRuntimeTag().then(tag => {
11
+ * cy.log(`Runtime tag: ${tag}`)
12
+ * })
13
+ */
14
+ getRuntimeTag(): Chainable<string | null>;
15
+
16
+ /**
17
+ * Get all runtime tags passed via CLI (async)
18
+ * @example
19
+ * cy.getAllRuntimeTags().then(tags => {
20
+ * expect(tags).to.include('IS_QA')
21
+ * })
22
+ */
23
+ getAllRuntimeTags(): Chainable<string[]>;
24
+
25
+ /**
26
+ * Check if runtime tag matches the given tag (async)
27
+ * @paramtag - Tag to check
28
+ * @example
29
+ * cy.isRuntimeTag('IS_QA').then(matches => {
30
+ * if (matches) {
31
+ * cy.log('Running in QA mode')
32
+ * }
33
+ * })
34
+ */
35
+ isRuntimeTag(tag:string): Chainable<boolean>;
36
+
37
+ /**
38
+ * Get tags for current test from config (async)
39
+ * @example
40
+ * cy.getTestTags().then(tags => {
41
+ * expect(tags).to.include('IS_QA')
42
+ * })
43
+ */
44
+ getTestTags(): Chainable<string[]>;
45
+
46
+ /**
47
+ * Check if current test has a specific tag (async)
48
+ * @paramtag - Tag to check
49
+ * @example
50
+ * cy.hasTestTag('IS_QA').then(hasIt => {
51
+ * if (hasIt) {
52
+ * cy.log('Test is tagged with IS_QA')
53
+ * }
54
+ * })
55
+ */
56
+ hasTestTag(tag: string): Chainable<boolean>;
57
+
58
+ /**
59
+ * Check if current test matches the runtime tag (async)
60
+ * @example
61
+ * cy.matchesRuntimeTag().then(matches => {
62
+ * if (matches) {
63
+ * cy.log('Test matches runtime tag!')
64
+ * }
65
+ * })
66
+ */
67
+ matchesRuntimeTag(): Chainable<boolean>;
68
+ }
69
+
70
+ interface Tags {
71
+ /**
72
+ * Get the runtime tag passed via CLI (synchronous)
73
+ * @returns The runtime tag or null
74
+ * @example
75
+ * const tag = Cypress.Tags.getRuntime()
76
+ * if (tag === 'IS_QA') {
77
+ * cy.log('Running in QA mode')
78
+ * }
79
+ */
80
+ getRuntime(): string | null;
81
+
82
+ /**
83
+ * Get all runtime tags passed via CLI (synchronous)
84
+ * @returns Array of runtime tags
85
+ * @example
86
+ * const tags = Cypress.Tags.getAllRuntime()
87
+ * cy.log(`Runtime tags: ${tags.join(', ')}`)
88
+ */
89
+ getAllRuntime(): string[];
90
+
91
+ /**
92
+ * Check if runtime tag matches the given tag (synchronous)
93
+ * @paramtag - Tag to check
94
+ * @returns true if runtime tag matches
95
+ * @example
96
+ * if (Cypress.Tags.isRuntime('IS_QA')) {
97
+ * cy.log('Running in QA mode')
98
+ * }
99
+ */
100
+ isRuntime(tag:string): boolean;
101
+
102
+ /**
103
+ * Check if runtime has any of the given tags (synchronous)
104
+ * @paramtags - Tags to check
105
+ * @returns true if any tag matches
106
+ * @example
107
+ * if (Cypress.Tags.isAnyRuntime('IS_QA', 'IS_STAGING')) {
108
+ * cy.log('Running in QA or Staging')
109
+ * }
110
+ */
111
+ isAnyRuntime(...tags: string[]): boolean;
112
+
113
+ /**
114
+ * Check if runtime has all of the given tags (synchronous)
115
+ * @paramtags - Tags to check
116
+ * @returns true if all tags match
117
+ * @example
118
+ * if (Cypress.Tags.isAllRuntime('IS_QA', 'SMOKE')) {
119
+ * cy.log('Running QA smoke tests')
120
+ * }
121
+ */
122
+ isAllRuntime(...tags: string[]): boolean;
123
+
124
+ /**
125
+ * Get tags for current test from config (synchronous)
126
+ * @returns Array of test tags
127
+ * @example
128
+ * const tags = Cypress.Tags.getTest()
129
+ * cy.log(`Test tags: ${tags.join(', ')}`)
130
+ */
131
+ getTest(): string[];
132
+
133
+ /**
134
+ * Check if current test has a specific tag (synchronous)
135
+ * @paramtag - Tag to check
136
+ * @returns true if test has the tag
137
+ * @example
138
+ * if (Cypress.Tags.hasTest('IS_QA')) {
139
+ * cy.log('Test is tagged with IS_QA')
140
+ * }
141
+ */
142
+ hasTest(tag: string): boolean;
143
+
144
+ /**
145
+ * Check if current test has any of the given tags (synchronous)
146
+ * @paramtags - Tags to check
147
+ * @returns true if test has any tag
148
+ * @example
149
+ * if (Cypress.Tags.hasAnyTest('IS_QA', 'IS_GA')) {
150
+ * cy.log('Test has QA or GA tag')
151
+ * }
152
+ */
153
+ hasAnyTest(...tags: string[]): boolean;
154
+
155
+ /**
156
+ * Check if current test has all of the given tags (synchronous)
157
+ * @paramtags - Tags to check
158
+ * @returns true if test has all tags
159
+ * @example
160
+ * if (Cypress.Tags.hasAllTest('IS_QA', 'SMOKE')) {
161
+ * cy.log('Test has both QA and SMOKE tags')
162
+ * }
163
+ */
164
+ hasAllTest(...tags: string[]): boolean;
165
+
166
+ /**
167
+ * Check if current test matches the runtime tag (synchronous)
168
+ * @returns true if test has the runtime tag
169
+ * @example
170
+ * if (Cypress.Tags.matchesRuntime()) {
171
+ * cy.log('Test matches the runtime tag!')
172
+ * }
173
+ */
174
+ matchesRuntime(): boolean;
175
+ }
176
+
177
+ interface CypressStatic {
178
+ /**
179
+ * Tag utility functions for runtime and test tag access
180
+ */
181
+ Tags:Tags;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Plugin configuration for cypress.config.js
187
+ */
188
+ export function setupNodeEvents(
189
+ on: Cypress.PluginEvents,
190
+ config: Cypress.PluginConfigOptions
191
+ ):Cypress.PluginConfigOptions;
192
+
193
+ /**
194
+ * Support file setup for cypress/support/e2e.js
195
+ * Call this function to enable runtime tag access
196
+ */
197
+ export function setupSupport(): void;
198
+
199
+ /**
200
+ * Get the runtime tag passed via CLI
201
+ * @returns The runtime tag or null
202
+ */
203
+ export function getRuntimeTag(): string | null;
204
+
205
+ /**
206
+ * Get all runtime tags passed via CLI
207
+ * @returns Array of runtime tags
208
+ */
209
+ export function getAllRuntimeTags(): string[];
210
+
211
+ /**
212
+ * Check if runtime tag matches the given tag
213
+ * @paramtag - Tag to check
214
+ * @returns true if runtime tag matches
215
+ */
216
+ export function isRuntimeTag(tag: string): boolean;
217
+
218
+ /**
219
+ * Check if runtime has any of the given tags
220
+ * @paramtags - Tags to check
221
+ * @returns true if any tag matches
222
+ */
223
+ export function isAnyRuntimeTag(...tags: string[]): boolean;
224
+
225
+ /**
226
+ * Check if runtime has all of the given tags
227
+ * @paramtags - Tags to check
228
+ * @returns true if all tags match
229
+ */
230
+ export function isAllRuntimeTags(...tags: string[]): boolean;
231
+
232
+ /**
233
+ * Get tags for current test from config
234
+ * @returns Array of test tags
235
+ */
236
+ export function getTestTags(): string[];
237
+
238
+ /**
239
+ * Check if current test has a specific tag
240
+ * @paramtag - Tag to check
241
+ * @returns true if test has the tag
242
+ */
243
+ export function hasTestTag(tag: string): boolean;
244
+
245
+ /**
246
+ * Check if current test has any of the given tags
247
+ * @paramtags - Tags to check
248
+ * @returns true if test has any tag
249
+ */
250
+ export function hasAnyTestTag(...tags: string[]): boolean;
251
+
252
+ /**
253
+ * Check if current test has all of the given tags
254
+ * @paramtags - Tags to check
255
+ * @returns true if test has all tags
256
+ */
257
+ export function hasAllTestTags(...tags: string[]): boolean;
258
+
259
+ /**
260
+ * Check if current test matches the runtime tag
261
+ * @returns true if test has the runtime tag
262
+ */
263
+ export function matchesRuntimeTag(): boolean;
package/src/index.js ADDED
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Cypress Conditional Tags Plugin
3
+ * Access runtime tags (from CLI) and test tags (from config) inside test logic
4
+ */
5
+
6
+ const testTagStore = new Map();
7
+
8
+ /**
9
+ * Get the runtime tag passed via CLI
10
+ * Supports both 'conditionalTags' (preferred) and 'grepTags' (fallback for backward compatibility)
11
+ * @returns{string|null} - The runtime tag or null
12
+ */
13
+ function getRuntimeTag() {
14
+ // Prefer conditionalTags, fallback to grepTags for backward compatibility
15
+ const conditionalTags = Cypress.env('conditionalTags');
16
+ const grepTags = Cypress.env('grepTags');
17
+ const tags = conditionalTags || grepTags;
18
+
19
+ if (!tags) return null;
20
+
21
+ // Handle multiple tags separated by + or ,
22
+ if (typeof tags === 'string') {
23
+ // Return first tag if multiple
24
+ return tags.split(/[+,]/)[0].trim();
25
+ }
26
+
27
+ return tags;
28
+ }
29
+
30
+ /**
31
+ * Get all runtime tags passed via CLI
32
+ * Supports both 'conditionalTags' (preferred) and 'grepTags' (fallback for backward compatibility)
33
+ * @returns{string[]} - Array of runtime tags
34
+ */
35
+ function getAllRuntimeTags() {
36
+ // Prefer conditionalTags, fallback to grepTags for backward compatibility
37
+ const conditionalTags = Cypress.env('conditionalTags');
38
+ const grepTags = Cypress.env('grepTags');
39
+ const tags = conditionalTags || grepTags;
40
+
41
+ if (!tags) return [];
42
+
43
+ if (typeoftags === 'string') {
44
+ return tags.split(/[+,]/).map(tag => tag.trim()).filter(Boolean);
45
+ }
46
+
47
+ if (Array.isArray(tags)) {
48
+ return tags;
49
+ }
50
+
51
+ return [tags];
52
+ }
53
+
54
+ /**
55
+ * Check if runtime tag matches the given tag
56
+ * @param{string}tag - Tag to check
57
+ * @returns{boolean}
58
+ */
59
+ function isRuntimeTag(tag) {
60
+ const runtimeTags = getAllRuntimeTags();
61
+ return runtimeTags.includes(tag);
62
+ }
63
+
64
+ /**
65
+ * Check if runtime has any of the given tags
66
+ * @param{...string}tags - Tags to check
67
+ * @returns{boolean}
68
+ */
69
+ function isAnyRuntimeTag(...tags) {
70
+ const runtimeTags = getAllRuntimeTags();
71
+ return tags.some(tag => runtimeTags.includes(tag));
72
+ }
73
+
74
+ /**
75
+ * Check if runtime has all of the given tags
76
+ * @param{...string}tags - Tags to check
77
+ * @returns{boolean}
78
+ */
79
+ function isAllRuntimeTags(...tags) {
80
+ const runtimeTags = getAllRuntimeTags();
81
+ return tags.every(tag => runtimeTags.includes(tag));
82
+ }
83
+
84
+ /**
85
+ * Get tags for current test from config
86
+ * @returns{string[]} - Array of test tags
87
+ */
88
+ function getTestTags() {
89
+ const currentTest = cy.state('test');
90
+ if (!currentTest) return [];
91
+
92
+ const testId = currentTest.id || currentTest.title;
93
+ return testTagStore.get(testId) || [];
94
+ }
95
+
96
+ /**
97
+ * Check if current test has a specific tag
98
+ * @param{string}tag - Tag to check
99
+ * @returns{boolean}
100
+ */
101
+ function hasTestTag(tag) {
102
+ const tags = getTestTags();
103
+ return tags.includes(tag);
104
+ }
105
+
106
+ /**
107
+ * Check if current test has any of the given tags
108
+ * @param{...string}tags - Tags to check
109
+ * @returns{boolean}
110
+ */
111
+ function hasAnyTestTag(...tags) {
112
+ const testTags = getTestTags();
113
+ return tags.some(tag => testTags.includes(tag));
114
+ }
115
+
116
+ /**
117
+ * Check if current test has all of the given tags
118
+ * @param{...string}tags - Tags to check
119
+ * @returns{boolean}
120
+ */
121
+ function hasAllTestTags(...tags) {
122
+ const testTags = getTestTags();
123
+ return tags.every(tag => testTags.includes(tag));
124
+ }
125
+
126
+ /**
127
+ * Check if current test matches the runtime tag
128
+ * @returns{boolean}
129
+ */
130
+ function matchesRuntimeTag() {
131
+ const runtimeTags = getAllRuntimeTags();
132
+ const testTags = getTestTags();
133
+
134
+ if (runtimeTags.length === 0) return true; // No runtime filter
135
+
136
+ return runtimeTags.some(runtimeTag => testTags.includes(runtimeTag));
137
+ }
138
+
139
+ /**
140
+ * Store tags for a test
141
+ * @param{string}testId - Test identifier
142
+ * @param{string[]}tags - Array of tags
143
+ */
144
+ function storeTestTags(testId, tags) {
145
+ if (tags && tags.length > 0) {
146
+ testTagStore.set(testId, tags);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Wrap it() to capture tags from config
152
+ */
153
+ function wrapIt(originalIt) {
154
+ return function(title, configOrFn, fn) {
155
+ let config = {};
156
+ let testFn = fn;
157
+
158
+ // Handle different signatures
159
+ if (typeof configOrFn === 'function') {
160
+ testFn = configOrFn;
161
+ } else if (typeof configOrFn === 'object') {
162
+ config = configOrFn;
163
+ testFn = fn;
164
+ }
165
+
166
+ // Extract tags from config
167
+ const tags = config.tags || [];
168
+
169
+ return originalIt(title, config, function() {
170
+ // Store tags for this test
171
+ const currentTest = cy.state('test');
172
+ if (currentTest && tags.length > 0) {
173
+ const testId = currentTest.id || currentTest.title;
174
+ storeTestTags(testId, tags);
175
+ }
176
+
177
+ // Execute the test
178
+ if (testFn) {
179
+ return testFn.call(this);
180
+ }
181
+ });
182
+ };
183
+ }
184
+
185
+ /**
186
+ * Wrap describe() to support tag inheritance (future enhancement)
187
+ */
188
+ function wrapDescribe(originalDescribe) {
189
+ return function(title, configOrFn, fn) {
190
+ let config = {};
191
+ let suiteFn = fn;
192
+
193
+ // Handle different signatures
194
+ if (typeof configOrFn === 'function') {
195
+ suiteFn = configOrFn;
196
+ } else if (typeof configOrFn === 'object') {
197
+ config = configOrFn;
198
+ suiteFn = fn;
199
+ }
200
+
201
+ // Note: Describe-level tags would need special handling
202
+ // For now, we focus on test-level tags
203
+
204
+ return originalDescribe(title, config, suiteFn);
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Plugin setup function for cypress.config.js
210
+ */
211
+ function setupNodeEvents(on, config) {
212
+ // This plugin does not provide test filtering
213
+ // For test filtering based on tags, install and configure @cypress/grep separately
214
+ // See: https://github.com/cypress-io/cypress/tree/develop/npm/grep
215
+
216
+ return config;
217
+ }
218
+
219
+ /**
220
+ * Support file setup function for cypress/support/e2e.js
221
+ */
222
+ function setupSupport() {
223
+ // Wrap global it/test functions
224
+ if (typeof it !== 'undefined') {
225
+ const originalIt = it;
226
+ global.it = wrapIt(originalIt);
227
+ global.test = wrapIt(originalIt);
228
+
229
+ // Preserve it.only, it.skip, etc.
230
+ ['only', 'skip'].forEach(modifier => {
231
+ if (originalIt[modifier]) {
232
+ global.it[modifier] = wrapIt(originalIt[modifier]);
233
+ global.test[modifier] = wrapIt(originalIt[modifier]);
234
+ }
235
+ });
236
+ }
237
+
238
+ // Wrap global describe/context functions (for future tag inheritance)
239
+ if (typeof describe !== 'undefined') {
240
+ const originalDescribe = describe;
241
+ global.describe = wrapDescribe(originalDescribe);
242
+ global.context = wrapDescribe(originalDescribe);
243
+
244
+ // Preserve describe.only, describe.skip, etc.
245
+ ['only', 'skip'].forEach(modifier => {
246
+ if (originalDescribe[modifier]) {
247
+ global.describe[modifier] = wrapDescribe(originalDescribe[modifier]);
248
+ global.context[modifier] = wrapDescribe(originalDescribe[modifier]);
249
+ }
250
+ });
251
+ }
252
+
253
+ // Add custom Cypress commands for async access
254
+ Cypress.Commands.add('getRuntimeTag', () => {
255
+ return cy.wrap(getRuntimeTag());
256
+ });
257
+
258
+ Cypress.Commands.add('getAllRuntimeTags', () => {
259
+ return cy.wrap(getAllRuntimeTags());
260
+ });
261
+
262
+ Cypress.Commands.add('isRuntimeTag', (tag) => {
263
+ return cy.wrap(isRuntimeTag(tag));
264
+ });
265
+
266
+ Cypress.Commands.add('getTestTags', () => {
267
+ return cy.wrap(getTestTags());
268
+ });
269
+
270
+ Cypress.Commands.add('hasTestTag', (tag) => {
271
+ return cy.wrap(hasTestTag(tag));
272
+ });
273
+
274
+ Cypress.Commands.add('matchesRuntimeTag', () => {
275
+ return cy.wrap(matchesRuntimeTag());
276
+ });
277
+
278
+ // Add global Tags API for synchronous access
279
+ Cypress.Tags = {
280
+ // Runtime tags (from CLI)
281
+ getRuntime:getRuntimeTag,
282
+ getAllRuntime:getAllRuntimeTags,
283
+ isRuntime:isRuntimeTag,
284
+ isAnyRuntime:isAnyRuntimeTag,
285
+ isAllRuntime:isAllRuntimeTags,
286
+
287
+ // Test tags (from config)
288
+ getTest:getTestTags,
289
+ hasTest:hasTestTag,
290
+ hasAnyTest:hasAnyTestTag,
291
+ hasAllTest:hasAllTestTags,
292
+
293
+ // Combined
294
+ matchesRuntime:matchesRuntimeTag
295
+ };
296
+ }
297
+
298
+ module.exports = {
299
+ setupNodeEvents,
300
+ setupSupport,
301
+ getRuntimeTag,
302
+ getAllRuntimeTags,
303
+ isRuntimeTag,
304
+ isAnyRuntimeTag,
305
+ isAllRuntimeTags,
306
+ getTestTags,
307
+ hasTestTag,
308
+ hasAnyTestTag,
309
+ hasAllTestTags,
310
+ matchesRuntimeTag
311
+ };