@bahmutov/cy-grep 1.2.0 → 1.3.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @bahmutov/cy-grep ![cypress version](https://img.shields.io/badge/cypress-12.1.0-brightgreen)
1
+ # @bahmutov/cy-grep ![cypress version](https://img.shields.io/badge/cypress-12.3.0-brightgreen)
2
2
 
3
3
  > Filter tests using substring or tag
4
4
 
@@ -46,6 +46,7 @@ Table of Contents
46
46
  - [Omit filtered tests (grepOmitFiltered)](#omit-filtered-tests-grepomitfiltered)
47
47
  - [Disable grep](#disable-grep)
48
48
  - [Burn (repeat) tests](#burn-repeat-tests)
49
+ - [Required tags](#required-tags)
49
50
  - [TypeScript support](#typescript-support)
50
51
  - [General advice](#general-advice)
51
52
  - [DevTools console](#devtools-console)
@@ -56,9 +57,6 @@ Table of Contents
56
57
  - [Debugging in the browser](#debugging-in-the-browser)
57
58
  - [Examples](#examples)
58
59
  - [See also](#see-also)
59
- - [Migration guide](#migration-guide)
60
- - [from v1 to v2](#from-v1-to-v2)
61
- - [from v2 to v3](#from-v2-to-v3)
62
60
  - [Small Print](#small-print)
63
61
 
64
62
  <!-- /MarkdownTOC -->
@@ -217,8 +215,6 @@ $ npx cypress run --env grep="-hello world"
217
215
  $ npx cypress run --env grep="hello; -world"
218
216
  ```
219
217
 
220
- **Note:** Inverted title filter is not compatible with the `grepFilterSpecs` option
221
-
222
218
  ## Filter with tags
223
219
 
224
220
  You can select tests to run or skip using tags by passing `--env grepTags=...` value.
@@ -415,6 +411,16 @@ You can pass the number of times to run the tests via environment name `burn` or
415
411
 
416
412
  If you do not specify the "grep" or "grep tags" option, the "burn" will repeat _every_ test.
417
413
 
414
+ ## Required tags
415
+
416
+ Sometimes you might want to run a test or a suite of tests _only_ if a specific tag or tags are present. For example, you might have a test that cleans the data. This test is meant to run nightly, not on every test run. Thus you can set a `required` tag:
417
+
418
+ ```js
419
+ it('cleans up the data', { requiredTags: '@nightly' }, () => {...})
420
+ ```
421
+
422
+ When you run the tests now, this test will be skipped, as if it were `it.skip`. It will only run if you use the tag `@nightly`, for example: `npx cypress run --env grepTags=@nightly`.
423
+
418
424
  ## TypeScript support
419
425
 
420
426
  Because the Cypress test config object type definition does not have the `tags` property we are using above, the TypeScript linter will show an error. Just add an ignore comment above the test:
@@ -547,30 +553,28 @@ This module uses [debug](https://github.com/visionmedia/debug#readme) to log ver
547
553
 
548
554
  ### Debugging in the plugin
549
555
 
550
- Start Cypress with the environment variable `DEBUG=cypress-grep`. You will see a few messages from this plugin in the terminal output:
556
+ Start Cypress with the environment variable `DEBUG=cy-grep`. You will see a few messages from this plugin in the terminal output:
551
557
 
552
558
  ```
553
- $ DEBUG=cypress-grep npx cypress run --env grep=works,grepFilterSpecs=true
554
- cypress-grep: tests with "works" in their names
555
- cypress-grep: filtering specs using "works" in the title
556
- cypress-grep Cypress config env object: { grep: 'works', grepFilterSpecs: true }
559
+ $ DEBUG=cy-grep npx cypress run --env grep=works,grepFilterSpecs=true
560
+ cy-grep: tests with "works" in their names
561
+ cy-grep: filtering specs using "works" in the title
562
+ cy-grep Cypress config env object: { grep: 'works', grepFilterSpecs: true }
557
563
  ...
558
- cypress-grep found 1 spec files +5ms
559
- cypress-grep [ 'spec.js' ] +0ms
560
- cypress-grep spec file spec.js +5ms
561
- cypress-grep suite and test names: [ 'hello world', 'works', 'works 2 @tag1',
564
+ cy-grep found 1 spec files +5ms
565
+ cy-grep [ 'spec.js' ] +0ms
566
+ cy-grep spec file spec.js +5ms
567
+ cy-grep suite and test names: [ 'hello world', 'works', 'works 2 @tag1',
562
568
  'works 2 @tag1 @tag2', 'works @tag2' ] +0ms
563
- cypress-grep found "works" in 1 specs +0ms
564
- cypress-grep [ 'spec.js' ] +0ms
569
+ cy-grep found "works" in 1 specs +0ms
570
+ cy-grep [ 'spec.js' ] +0ms
565
571
  ```
566
572
 
567
573
  ### Debugging in the browser
568
574
 
569
- To enable debug console messages in the browser, from the DevTools console set `localStorage.debug='cypress-grep'` and run the tests again.
575
+ To enable debug console messages in the browser, from the DevTools console set `localStorage.debug='cy-grep'` and run the tests again.
570
576
 
571
- ![Debug messages](./images/debug.png)
572
-
573
- To see how to debug this plugin, watch the video [Debug cypress-grep Plugin](https://youtu.be/4YMAERddHYA).
577
+ To see how to debug this plugin, watch the video [Debug cypress-grep Plugin](https://youtu.be/4YMAERddHYA) but use the string `cy-grep`
574
578
 
575
579
  ## Examples
576
580
 
@@ -582,37 +586,6 @@ To see how to debug this plugin, watch the video [Debug cypress-grep Plugin](htt
582
586
  - [cypress-select-tests](https://github.com/bahmutov/cypress-select-tests)
583
587
  - [cypress-skip-test](https://github.com/cypress-io/cypress-skip-test)
584
588
 
585
- ## Migration guide
586
-
587
- ### from v1 to v2
588
-
589
- In v2 we have separated grepping by part of the title string from tags.
590
-
591
- **v1**
592
-
593
- ```
594
- --env grep="one two"
595
- ```
596
-
597
- The above scenario was confusing - did you want to find all tests with title containing "one two" or did you want to run tests tagged `one` or `two`?
598
-
599
- **v2**
600
-
601
- ```
602
- # enable the tests with string "one two" in their titles
603
- --env grep="one two"
604
- # enable the tests with tag "one" or "two"
605
- --env grepTags="one two"
606
- # enable the tests with both tags "one" and "two"
607
- --env grepTags="one+two"
608
- # enable the tests with "hello" in the title and tag "smoke"
609
- --env grep=hello,grepTags=smoke
610
- ```
611
-
612
- ### from v2 to v3
613
-
614
- Version >= 3 of cypress-grep _only_ supports Cypress >= 10.
615
-
616
589
  ## Small Print
617
590
 
618
591
  License: MIT - do anything with the code, but don't blame me if it does not work.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bahmutov/cy-grep",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Filter Cypress tests using title or tags",
5
5
  "main": "src/support.js",
6
6
  "scripts": {
@@ -12,15 +12,15 @@
12
12
  "dependencies": {
13
13
  "cypress-plugin-config": "^1.2.0",
14
14
  "debug": "^4.3.2",
15
- "find-test-names": "^1.22.2",
15
+ "find-test-names": "1.25.0",
16
16
  "globby": "^11.0.4"
17
17
  },
18
18
  "devDependencies": {
19
- "cypress": "12.1.0",
19
+ "cypress": "12.3.0",
20
20
  "cypress-each": "^1.11.0",
21
21
  "cypress-expect": "^2.5.3",
22
22
  "prettier": "^2.8.1",
23
- "semantic-release": "^19.0.5",
23
+ "semantic-release": "^20.0.3",
24
24
  "typescript": "^4.7.4"
25
25
  },
26
26
  "peerDependencies": {
package/src/index.d.ts CHANGED
@@ -8,8 +8,15 @@ declare namespace Cypress {
8
8
  * describe('block with config tag', { tags: '@smoke' }, () => {})
9
9
  * @example multiple tags
10
10
  * describe('block with config tag', { tags: ['@smoke', '@slow'] }, () => {})
11
+ * @see https://github.com/bahmutov/cy-grep
11
12
  */
12
13
  tags?: string | string[]
14
+ /**
15
+ * Provide a tag or a list of tags that is required for this suite to run.
16
+ * @example describe('mobile tests', { requiredTags: '@mobile' }, () => {})
17
+ * @see https://github.com/bahmutov/cy-grep
18
+ */
19
+ requiredTags?: string | string[]
13
20
  }
14
21
 
15
22
  // specify additional properties in the TestConfig object
@@ -21,8 +28,15 @@ declare namespace Cypress {
21
28
  * it('logs in', { tags: '@smoke' }, () => { ... })
22
29
  * @example multiple tags
23
30
  * it('works', { tags: ['@smoke', '@slow'] }, () => { ... })
31
+ * @see https://github.com/bahmutov/cy-grep
24
32
  */
25
33
  tags?: string | string[]
34
+ /**
35
+ * Provide a tag or a list of tags that is required for this test to run.
36
+ * @example it('cleans the data', { requiredTags: '@nightly' }, () => {})
37
+ * @see https://github.com/bahmutov/cy-grep
38
+ */
39
+ requiredTags?: string | string[]
26
40
  }
27
41
 
28
42
  interface Cypress {
package/src/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- const debug = require('debug')('cypress-grep')
1
+ const debug = require('debug')('cy-grep')
2
2
  const globby = require('globby')
3
3
  const { getTestNames, findEffectiveTestTags } = require('find-test-names')
4
4
  const fs = require('fs')
@@ -6,7 +6,7 @@ const { version } = require('../package.json')
6
6
  const { parseGrep, shouldTestRun } = require('./utils')
7
7
 
8
8
  /**
9
- * Prints the cypress-grep environment values if any.
9
+ * Prints the cy-grep environment values if any.
10
10
  * @param {Cypress.ConfigOptions} config
11
11
  */
12
12
  function cypressGrepPlugin(config) {
@@ -18,23 +18,23 @@ function cypressGrepPlugin(config) {
18
18
 
19
19
  if (!config.specPattern) {
20
20
  throw new Error(
21
- 'Incompatible versions detected, cypress-grep 3.0.0+ requires Cypress 10.0.0+',
21
+ 'Incompatible versions detected, cy-grep requires Cypress 10.0.0+',
22
22
  )
23
23
  }
24
24
 
25
- debug('cypress-grep plugin version %s', version)
25
+ debug('cy-grep plugin version %s', version)
26
26
  debug('Cypress config env object: %o', env)
27
27
 
28
28
  const grep = env.grep ? String(env.grep) : undefined
29
29
 
30
30
  if (grep) {
31
- console.log('cypress-grep: tests with "%s" in their names', grep.trim())
31
+ console.log('cy-grep: tests with "%s" in their names', grep.trim())
32
32
  }
33
33
 
34
34
  const grepTags = env.grepTags || env['grep-tags']
35
35
 
36
36
  if (grepTags) {
37
- console.log('cypress-grep: filtering using tag(s) "%s"', grepTags)
37
+ console.log('cy-grep: filtering using tag(s) "%s"', grepTags)
38
38
  const parsedGrep = parseGrep(null, grepTags)
39
39
 
40
40
  debug('parsed grep tags %o', parsedGrep.tags)
@@ -43,19 +43,19 @@ function cypressGrepPlugin(config) {
43
43
  const grepBurn = env.grepBurn || env['grep-burn'] || env.burn
44
44
 
45
45
  if (grepBurn) {
46
- console.log('cypress-grep: running filtered tests %d times', grepBurn)
46
+ console.log('cy-grep: running filtered tests %d times', grepBurn)
47
47
  }
48
48
 
49
49
  const grepUntagged = env.grepUntagged || env['grep-untagged']
50
50
 
51
51
  if (grepUntagged) {
52
- console.log('cypress-grep: running untagged tests')
52
+ console.log('cy-grep: running untagged tests')
53
53
  }
54
54
 
55
55
  const omitFiltered = env.grepOmitFiltered || env['grep-omit-filtered']
56
56
 
57
57
  if (omitFiltered) {
58
- console.log('cypress-grep: will omit filtered tests')
58
+ console.log('cy-grep: will omit filtered tests')
59
59
  }
60
60
 
61
61
  const { specPattern, excludeSpecPattern } = config
@@ -78,7 +78,7 @@ function cypressGrepPlugin(config) {
78
78
  let greppedSpecs = []
79
79
 
80
80
  if (grep) {
81
- console.log('cypress-grep: filtering specs using "%s" in the title', grep)
81
+ console.log('cy-grep: filtering specs using "%s" in the title', grep)
82
82
  const parsedGrep = parseGrep(grep)
83
83
 
84
84
  debug('parsed grep %o', parsedGrep)
@@ -123,7 +123,7 @@ function cypressGrepPlugin(config) {
123
123
  debug('spec file %s', specFile)
124
124
  debug('effective test tags %o', testTags)
125
125
  return Object.keys(testTags).some((testTitle) => {
126
- const effectiveTags = testTags[testTitle]
126
+ const effectiveTags = testTags[testTitle].effectiveTags
127
127
  return shouldTestRun(parsedGrep, null, effectiveTags)
128
128
  })
129
129
  } catch (err) {
package/src/support.js CHANGED
@@ -8,7 +8,8 @@ const {
8
8
  getPluginConfigValue,
9
9
  setPluginConfigValue,
10
10
  } = require('cypress-plugin-config')
11
- const debug = require('debug')('@bahmutov/cy-grep')
11
+ // to debug in the browser, set the "localStorage.debug='cy-grep'"
12
+ const debug = require('debug')('cy-grep')
12
13
 
13
14
  debug.log = console.info.bind(console)
14
15
 
@@ -18,9 +19,9 @@ const _describe = describe
18
19
 
19
20
  /**
20
21
  * Wraps the "it" and "describe" functions that support tags.
21
- * @see https://github.com/cypress-io/cypress-grep
22
+ * @see https://github.com/bahmutov/cy-grep
22
23
  */
23
- function cypressGrep() {
24
+ function registerCyGrep() {
24
25
  /** @type {string} Part of the test title go grep */
25
26
  let grep = getPluginConfigValue('grep')
26
27
 
@@ -41,12 +42,12 @@ function cypressGrep() {
41
42
  getPluginConfigValue('grepUntagged') ||
42
43
  getPluginConfigValue('grep-untagged')
43
44
 
44
- if (!grep && !grepTags && !burnSpecified && !grepUntagged) {
45
- // nothing to do, the user has no specified the "grep" string
46
- debug('Nothing to grep, version %s', version)
45
+ // if (!grep && !grepTags && !burnSpecified && !grepUntagged) {
46
+ // nothing to do, the user has no specified the "grep" string
47
+ // debug('Nothing to grep, version %s', version)
47
48
 
48
- return
49
- }
49
+ // return
50
+ // }
50
51
 
51
52
  /** @type {number} Number of times to repeat each running test */
52
53
  const grepBurn =
@@ -70,9 +71,8 @@ function cypressGrep() {
70
71
  debug('parsed grep %o', parsedGrep)
71
72
 
72
73
  // prevent multiple registrations
73
- // https://github.com/cypress-io/cypress-grep/issues/59
74
74
  if (it.name === 'itGrep') {
75
- debug('already registered cypress-grep')
75
+ debug('already registered cy-grep')
76
76
 
77
77
  return
78
78
  }
@@ -90,32 +90,41 @@ function cypressGrep() {
90
90
  }
91
91
 
92
92
  let configTags = options && options.tags
93
-
94
93
  if (typeof configTags === 'string') {
95
94
  configTags = [configTags]
96
95
  }
96
+ let configRequiredTags = options && options.requiredTags
97
+ if (typeof configRequiredTags === 'string') {
98
+ configRequiredTags = [configRequiredTags]
99
+ }
97
100
 
98
101
  const nameToGrep = suiteStack
99
102
  .map((item) => item.name)
100
103
  .concat(name)
101
104
  .join(' ')
102
- const tagsToGrep = suiteStack
105
+ const effectiveTestTags = suiteStack
103
106
  .flatMap((item) => item.tags)
104
107
  .concat(configTags)
105
108
  .filter(Boolean)
109
+ const requiredTestTags = suiteStack
110
+ .flatMap((item) => item.requiredTags)
111
+ .concat(configRequiredTags)
112
+ .filter(Boolean)
113
+ // console.log({ nameToGrep, effectiveTestTags, requiredTestTags })
106
114
 
107
115
  const shouldRun = shouldTestRun(
108
116
  parsedGrep,
109
117
  nameToGrep,
110
- tagsToGrep,
118
+ effectiveTestTags,
111
119
  grepUntagged,
120
+ requiredTestTags,
112
121
  )
113
122
 
114
- if (tagsToGrep && tagsToGrep.length) {
123
+ if (effectiveTestTags && effectiveTestTags.length) {
115
124
  debug(
116
125
  'should test "%s" with tags %s run? %s',
117
126
  name,
118
- tagsToGrep.join(','),
127
+ effectiveTestTags.join(','),
119
128
  shouldRun,
120
129
  )
121
130
  } else {
@@ -171,7 +180,6 @@ function cypressGrep() {
171
180
  }
172
181
 
173
182
  let configTags = options && options.tags
174
-
175
183
  if (typeof configTags === 'string') {
176
184
  configTags = [configTags]
177
185
  }
@@ -205,7 +213,6 @@ function cypressGrep() {
205
213
  it.skip = _it.skip
206
214
  it.only = _it.only
207
215
  // preserve "it.each" method if found
208
- // https://github.com/cypress-io/cypress-grep/issues/72
209
216
  if (typeof _it.each === 'function') {
210
217
  it.each = _it.each
211
218
  }
@@ -237,7 +244,6 @@ if (!Cypress.grep) {
237
244
  * // remove all current grep settings
238
245
  * // and run all tests
239
246
  * Cypress.grep()
240
- * @see "Grep from DevTools console" https://github.com/cypress-io/cypress-grep#devtools-console
241
247
  */
242
248
  Cypress.grep = function grep(grep, tags, burn) {
243
249
  setPluginConfigValue('grep', grep)
@@ -287,4 +293,4 @@ if (!Cypress.grepFailed) {
287
293
  }
288
294
  }
289
295
 
290
- module.exports = cypressGrep
296
+ module.exports = registerCyGrep
package/src/utils.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * The string can have "-" in front of it to invert the match.
8
8
  * @param {string} s Input substring of the test title
9
9
  */
10
- function parseTitleGrep (s) {
10
+ function parseTitleGrep(s) {
11
11
  if (!s || typeof s !== 'string') {
12
12
  return null
13
13
  }
@@ -26,7 +26,7 @@ function parseTitleGrep (s) {
26
26
  }
27
27
  }
28
28
 
29
- function parseFullTitleGrep (s) {
29
+ function parseFullTitleGrep(s) {
30
30
  if (!s || typeof s !== 'string') {
31
31
  return []
32
32
  }
@@ -39,7 +39,7 @@ function parseFullTitleGrep (s) {
39
39
  * Parses tags to grep for.
40
40
  * @param {string} s Tags string like "@tag1+@tag2"
41
41
  */
42
- function parseTagsGrep (s) {
42
+ function parseTagsGrep(s) {
43
43
  if (!s) {
44
44
  return []
45
45
  }
@@ -48,37 +48,37 @@ function parseTagsGrep (s) {
48
48
 
49
49
  // top level split - using space or comma, each part is OR
50
50
  const ORS = s
51
- .split(/[ ,]/)
52
- // remove any empty tags
53
- .filter(Boolean)
54
- .map((part) => {
55
- // now every part is an AND
56
- if (part.startsWith('--')) {
57
- explicitNotTags.push({
58
- tag: part.slice(2),
59
- invert: true,
60
- })
51
+ .split(/[ ,]/)
52
+ // remove any empty tags
53
+ .filter(Boolean)
54
+ .map((part) => {
55
+ // now every part is an AND
56
+ if (part.startsWith('--')) {
57
+ explicitNotTags.push({
58
+ tag: part.slice(2),
59
+ invert: true,
60
+ })
61
61
 
62
- return
63
- }
62
+ return
63
+ }
64
+
65
+ const parsed = part.split('+').map((tag) => {
66
+ if (tag.startsWith('-')) {
67
+ return {
68
+ tag: tag.slice(1),
69
+ invert: true,
70
+ }
71
+ }
64
72
 
65
- const parsed = part.split('+').map((tag) => {
66
- if (tag.startsWith('-')) {
67
73
  return {
68
- tag: tag.slice(1),
69
- invert: true,
74
+ tag,
75
+ invert: false,
70
76
  }
71
- }
77
+ })
72
78
 
73
- return {
74
- tag,
75
- invert: false,
76
- }
79
+ return parsed
77
80
  })
78
81
 
79
- return parsed
80
- })
81
-
82
82
  // filter out undefined from explicit not tags
83
83
  const ORS_filtered = ORS.filter((x) => x !== undefined)
84
84
 
@@ -88,14 +88,29 @@ function parseTagsGrep (s) {
88
88
  })
89
89
 
90
90
  if (ORS_filtered.length === 0) {
91
- ORS_filtered[ 0 ] = explicitNotTags
91
+ ORS_filtered[0] = explicitNotTags
92
92
  }
93
93
  }
94
94
 
95
95
  return ORS_filtered
96
96
  }
97
97
 
98
- function shouldTestRunTags (parsedGrepTags, tags = []) {
98
+ function shouldTestRunRequiredTags(parsedGrepTags, requiredTags = []) {
99
+ if (!requiredTags.length) {
100
+ // there are no tags to check
101
+ return true
102
+ }
103
+
104
+ return requiredTags.every((onlyTag) => {
105
+ return parsedGrepTags.some((orPart) => {
106
+ return orPart.some((p) => {
107
+ return !p.invert && p.tag === onlyTag
108
+ })
109
+ })
110
+ })
111
+ }
112
+
113
+ function shouldTestRunTags(parsedGrepTags, tags = []) {
99
114
  if (!parsedGrepTags.length) {
100
115
  // there are no parsed tags to search for, the test should run
101
116
  return true
@@ -121,7 +136,7 @@ function shouldTestRunTags (parsedGrepTags, tags = []) {
121
136
  return onePartMatched
122
137
  }
123
138
 
124
- function shouldTestRunTitle (parsedGrep, testName) {
139
+ function shouldTestRunTitle(parsedGrep, testName) {
125
140
  if (!testName) {
126
141
  // if there is no title, let it run
127
142
  return true
@@ -152,7 +167,20 @@ function shouldTestRunTitle (parsedGrep, testName) {
152
167
  }
153
168
 
154
169
  // note: tags take precedence over the test name
155
- function shouldTestRun (parsedGrep, testName, tags = [], grepUntagged = false) {
170
+ /**
171
+ * Returns boolean if the test with the given name and effective tags
172
+ * should run, given the runtime grep (parsed) structure.
173
+ * @param {string|undefined} testName The full test title
174
+ * @param {string[]} tags The effective test tags
175
+ * @param {string[]} requiredTags The effective "required" test tags
176
+ */
177
+ function shouldTestRun(
178
+ parsedGrep,
179
+ testName,
180
+ tags = [],
181
+ grepUntagged = false,
182
+ requiredTags = [],
183
+ ) {
156
184
  if (grepUntagged) {
157
185
  return !tags.length
158
186
  }
@@ -163,13 +191,16 @@ function shouldTestRun (parsedGrep, testName, tags = [], grepUntagged = false) {
163
191
  testName = undefined
164
192
  }
165
193
 
194
+ const combinedTagsAndRequiredTags = [...tags, ...requiredTags]
195
+
166
196
  return (
167
197
  shouldTestRunTitle(parsedGrep.title, testName) &&
168
- shouldTestRunTags(parsedGrep.tags, tags)
198
+ shouldTestRunTags(parsedGrep.tags, combinedTagsAndRequiredTags) &&
199
+ shouldTestRunRequiredTags(parsedGrep.tags, requiredTags)
169
200
  )
170
201
  }
171
202
 
172
- function parseGrep (titlePart, tags) {
203
+ function parseGrep(titlePart, tags) {
173
204
  return {
174
205
  title: parseFullTitleGrep(titlePart),
175
206
  tags: parseTagsGrep(tags),
@@ -183,5 +214,6 @@ module.exports = {
183
214
  parseTagsGrep,
184
215
  shouldTestRun,
185
216
  shouldTestRunTags,
217
+ shouldTestRunRequiredTags,
186
218
  shouldTestRunTitle,
187
219
  }