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 +63 -0
- package/LICENSE.txt +21 -0
- package/README.md +423 -0
- package/package.json +45 -0
- package/src/index.d.ts +263 -0
- package/src/index.js +311 -0
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
|
+
};
|