find-cypress-specs 1.25.3 → 1.27.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
- # find-cypress-specs [![renovate-app badge][renovate-badge]][renovate-app] ![cypress version](https://img.shields.io/badge/cypress-12.5.0-brightgreen) [![ci](https://github.com/bahmutov/find-cypress-specs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/bahmutov/find-cypress-specs/actions/workflows/ci.yml)
1
+ # find-cypress-specs [![renovate-app badge][renovate-badge]][renovate-app] ![cypress version](https://img.shields.io/badge/cypress-12.7.0-brightgreen) [![ci](https://github.com/bahmutov/find-cypress-specs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/bahmutov/find-cypress-specs/actions/workflows/ci.yml)
2
2
 
3
3
  > Find Cypress spec files using the config settings
4
4
 
@@ -10,6 +10,14 @@ cypress/e2e/spec.js,cypress/e2e/featureA/user.js
10
10
 
11
11
  Supports JS and TS specs
12
12
 
13
+ ## Component specs
14
+
15
+ By default, it finds the E2E specs and tests. You can find component specs using `--component` CLI option
16
+
17
+ ```
18
+ $ npx find-cypress-specs --component
19
+ ```
20
+
13
21
  ## against branch
14
22
 
15
23
  By default, this module simply prints all spec filenames. You can add `--branch` parameter to only print the specs changed against that `origin/branch`.
@@ -224,6 +232,8 @@ You can see how Cypress finds the specs using `DEBUG=cypress:cli,cypress:server:
224
232
 
225
233
  Run the utility with environment variable `DEBUG=find-cypress-specs` to see the verbose logs
226
234
 
235
+ ![Debug output](./images/debug.png)
236
+
227
237
  ## Videos
228
238
 
229
239
  - [Use Ava Snapshots And Execa-wrap To Write End-to-End Tests For CLI Utilities](https://youtu.be/rsw17RqP0G0)
@@ -237,6 +247,8 @@ Run the utility with environment variable `DEBUG=find-cypress-specs` to see the
237
247
 
238
248
  You can use this module via its NPM module API.
239
249
 
250
+ ### getSpecs
251
+
240
252
  ```js
241
253
  const { getSpecs } = require('find-cypress-specs')
242
254
  // somewhere in the cypress.config.js
@@ -246,6 +258,30 @@ setupNodeEvents(on, config) {
246
258
  }
247
259
  ```
248
260
 
261
+ You can pass the `config` object to the `getSpecs` method. If there is no `config` parameter, it will read the config file automatically.
262
+
263
+ ```js
264
+ const specs = getSpecs({
265
+ e2e: {
266
+ specPattern: '*/e2e/featureA/*.cy.ts',
267
+ },
268
+ })
269
+ // ['cypress/e2e/featureA/spec.cy.ts']
270
+ ```
271
+
272
+ ### getTests
273
+
274
+ Returns an object with individual test information
275
+
276
+ ```js
277
+ const { getTests } = require('find-cypress-specs')
278
+ const { jsonResults, tagTestCounts } = getTests()
279
+ // jsonResults is an object
280
+ // with an entry per spec file
281
+ ```
282
+
283
+ See [get-tests.js](./test/npm/get-tests.js) for details and examples.
284
+
249
285
  ## Small print
250
286
 
251
287
  Author: Gleb Bahmutov <gleb.bahmutov@gmail.com> © 2022
package/bin/find.js CHANGED
@@ -1,9 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const arg = require('arg')
4
- const { getSpecs, collectResults, findChangedFiles } = require('../src')
5
- const { pickTaggedTestsFrom, leavePendingTestsOnly } = require('../src/tagged')
6
- const { addCounts } = require('../src/count')
4
+ const { getSpecs, findChangedFiles, getTests } = require('../src')
7
5
  const { stringAllInfo } = require('../src/print')
8
6
 
9
7
  const fs = require('fs')
@@ -42,6 +40,8 @@ const args = arg({
42
40
  '--time-trace': Boolean,
43
41
  // do not add more than this number of extra specs after tracing
44
42
  '--max-added-traced-specs': Number,
43
+ // find component specs
44
+ '--component': Boolean,
45
45
  // aliases
46
46
  '-n': '--names',
47
47
  '--name': '--names',
@@ -57,95 +57,50 @@ const args = arg({
57
57
  })
58
58
 
59
59
  debug('arguments %o', args)
60
+ const specType = args['--component'] ? 'component' : 'e2e'
60
61
 
61
- const specs = getSpecs()
62
- if (args['--names'] || args['--tags']) {
63
- if (!specs.length) {
64
- console.log('no specs found')
65
- } else {
66
- console.log('')
67
- // counts the number of tests for each tag across all specs
68
- const tagTestCounts = {}
69
- const jsonResults = {}
70
-
71
- specs.forEach((filename) => {
72
- jsonResults[filename] = {
73
- counts: {
74
- tests: 0,
75
- pending: 0,
76
- },
77
- tests: [],
78
- }
79
- const source = fs.readFileSync(filename, 'utf8')
80
- const result = getTestNames(source, true)
81
- // enable if need to debug the parsed test
82
- // console.dir(result.structure, { depth: null })
83
- collectResults(result.structure, jsonResults[filename].tests)
84
-
85
- if (args['--tags']) {
86
- const specTagCounts = countTags(result.structure)
87
- Object.keys(specTagCounts).forEach((tag) => {
88
- if (!(tag in tagTestCounts)) {
89
- tagTestCounts[tag] = specTagCounts[tag]
90
- } else {
91
- tagTestCounts[tag] += specTagCounts[tag]
92
- }
93
- })
94
- }
95
- })
96
-
97
- addCounts(jsonResults)
98
-
99
- if (args['--names']) {
100
- if (args['--tagged']) {
101
- // filter all collected tests to those that have the given tag(s)
102
- const splitTags = args['--tagged']
103
- .split(',')
104
- .map((s) => s.trim())
105
- .filter(Boolean)
106
- debug('filtering all tests by tag "%o"', splitTags)
107
- pickTaggedTestsFrom(jsonResults, splitTags)
108
- // recompute the number of tests
109
- addCounts(jsonResults)
110
- } else if (args['--skipped']) {
111
- debug('leaving only skipped (pending) tests')
112
- leavePendingTestsOnly(jsonResults)
113
- // recompute the number of tests
114
- addCounts(jsonResults)
115
- }
62
+ const specs = getSpecs(undefined, specType)
116
63
 
117
- if (args['--count']) {
118
- let n = 0
119
- Object.keys(jsonResults).forEach((filename) => {
120
- const skippedCount = jsonResults[filename].counts.pending
121
- n += skippedCount
122
- })
123
- console.log(n)
124
- } else {
125
- if (args['--json']) {
126
- console.log(JSON.stringify(jsonResults, null, 2))
127
- } else {
128
- const str = stringAllInfo(jsonResults)
129
- console.log(str)
130
- }
131
- console.log('')
132
- }
133
- }
134
-
135
- if (args['--tags']) {
136
- const tagEntries = Object.entries(tagTestCounts)
137
- const sortedTagEntries = tagEntries.sort((a, b) => {
138
- // every entry is [tag, count], so compare the tags
139
- return a[0].localeCompare(b[0])
64
+ if (args['--names'] || args['--tags']) {
65
+ // counts the number of tests for each tag across all specs
66
+ const { jsonResults, tagTestCounts } = getTests(specs, {
67
+ tags: args['--tags'],
68
+ tagged: args['--tagged'],
69
+ skipped: args['--skipped'],
70
+ })
71
+
72
+ if (args['--names']) {
73
+ if (args['--count']) {
74
+ let n = 0
75
+ Object.keys(jsonResults).forEach((filename) => {
76
+ const skippedCount = jsonResults[filename].counts.pending
77
+ n += skippedCount
140
78
  })
79
+ console.log(n)
80
+ } else {
141
81
  if (args['--json']) {
142
- // assemble a json object with the tag counts
143
- const tagResults = Object.fromEntries(sortedTagEntries)
144
- console.log(JSON.stringify(tagResults, null, 2))
82
+ console.log(JSON.stringify(jsonResults, null, 2))
145
83
  } else {
146
- const table = consoleTable.getTable(['Tag', 'Tests'], sortedTagEntries)
147
- console.log(table)
84
+ const str = stringAllInfo(jsonResults)
85
+ console.log(str)
148
86
  }
87
+ console.log('')
88
+ }
89
+ }
90
+
91
+ if (args['--tags']) {
92
+ const tagEntries = Object.entries(tagTestCounts)
93
+ const sortedTagEntries = tagEntries.sort((a, b) => {
94
+ // every entry is [tag, count], so compare the tags
95
+ return a[0].localeCompare(b[0])
96
+ })
97
+ if (args['--json']) {
98
+ // assemble a json object with the tag counts
99
+ const tagResults = Object.fromEntries(sortedTagEntries)
100
+ console.log(JSON.stringify(tagResults, null, 2))
101
+ } else {
102
+ const table = consoleTable.getTable(['Tag', 'Tests'], sortedTagEntries)
103
+ console.log(table)
149
104
  }
150
105
  }
151
106
  } else if (args['--branch']) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "find-cypress-specs",
3
- "version": "1.25.3",
3
+ "version": "1.27.0",
4
4
  "description": "Find Cypress spec files using the config settings",
5
5
  "main": "src",
6
6
  "files": [
@@ -14,9 +14,9 @@
14
14
  "test": "ava",
15
15
  "cy:run": "DEBUG=cypress:cli,cypress:server:specs cypress run",
16
16
  "demo": "DEBUG=find-cypress-specs node ./bin/find",
17
- "demo-names": "node ./bin/find --names",
18
- "demo-skipped-tests": "node ./bin/find --names --skipped",
19
- "demo-count-skipped-tests": "node ./bin/find --names --skipped --count",
17
+ "demo-names": "DEBUG=find-cypress-specs node ./bin/find --names",
18
+ "demo-skipped-tests": "DEBUG=find-cypress-specs node ./bin/find --names --skipped",
19
+ "demo-count-skipped-tests": "DEBUG=find-cypress-specs node ./bin/find --names --skipped --count",
20
20
  "demo-tags": "node ./bin/find --tags",
21
21
  "demo-tags-json": "node ./bin/find --tags --json",
22
22
  "demo-names-and-tags": "node ./bin/find --names --tags",
@@ -27,7 +27,8 @@
27
27
  "count-changed-specs": "node ./bin/find --branch main --count",
28
28
  "semantic-release": "semantic-release",
29
29
  "deps": "spec-change --folder . --mask 'cypress/**/*.{js,ts}'",
30
- "deps-changed": "DEBUG=find-cypress-specs node ./bin/find --branch main --parent --trace-imports cypress --time-trace --cache-trace"
30
+ "deps-changed": "DEBUG=find-cypress-specs node ./bin/find --branch main --parent --trace-imports cypress --time-trace --cache-trace",
31
+ "demo-component": "DEBUG=find-cypress-specs node ./bin/find --component --names"
31
32
  },
32
33
  "repository": {
33
34
  "type": "git",
@@ -44,7 +45,7 @@
44
45
  "homepage": "https://github.com/bahmutov/find-cypress-specs#readme",
45
46
  "devDependencies": {
46
47
  "ava": "^4.0.0",
47
- "cypress": "12.5.0",
48
+ "cypress": "12.7.0",
48
49
  "execa-wrap": "^1.4.0",
49
50
  "prettier": "^2.5.1",
50
51
  "really-need": "^1.9.2",
package/src/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ const { addCounts } = require('../src/count')
2
+ const { getTestNames, countTags } = require('find-test-names')
3
+ const { pickTaggedTestsFrom, leavePendingTestsOnly } = require('../src/tagged')
4
+
1
5
  const debug = require('debug')('find-cypress-specs')
2
6
  const fs = require('fs')
3
7
  const path = require('path')
@@ -109,19 +113,36 @@ function findCypressSpecsV9(opts = {}) {
109
113
  return filtered.map((file) => path.join(options.integrationFolder, file))
110
114
  }
111
115
 
112
- function findCypressSpecsV10(opts = {}) {
113
- if (!('e2e' in opts)) {
114
- throw new Error('Missing e2e in the config object')
116
+ function findCypressSpecsV10(opts = {}, type = 'e2e') {
117
+ if (type !== 'e2e' && type !== 'component') {
118
+ throw new Error(`Unknown spec type ${type}`)
115
119
  }
116
- const defaults = {
120
+
121
+ if (!(type in opts)) {
122
+ throw new Error(`Missing "${type}" object in the Cypress config object`)
123
+ }
124
+ const e2eDefaults = {
117
125
  specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
118
126
  excludeSpecPattern: [],
119
127
  }
120
- const options = {
121
- specPattern: opts.e2e.specPattern || defaults.specPattern,
122
- excludeSpecPattern:
123
- opts.e2e.excludeSpecPattern || defaults.excludeSpecPattern,
128
+ const componentDefaults = {
129
+ specPattern: '**/*.cy.{js,jsx,ts,tsx}',
130
+ excludeSpecPattern: ['/snapshots/*', '/image_snapshots/*'],
124
131
  }
132
+ // https://on.cypress.io/configuration
133
+ const options = {}
134
+
135
+ if (type === 'e2e') {
136
+ options.specPattern = opts.e2e.specPattern || e2eDefaults.specPattern
137
+ options.excludeSpecPattern =
138
+ opts.e2e.excludeSpecPattern || e2eDefaults.excludeSpecPattern
139
+ } else if (type === 'component') {
140
+ options.specPattern =
141
+ opts.component.specPattern || componentDefaults.specPattern
142
+ options.excludeSpecPattern =
143
+ opts.component.excludeSpecPattern || componentDefaults.excludeSpecPattern
144
+ }
145
+
125
146
  debug('options v10 %o', options)
126
147
 
127
148
  const files = globby.sync(options.specPattern, {
@@ -133,6 +154,13 @@ function findCypressSpecsV10(opts = {}) {
133
154
  // go through the files again and eliminate files that match
134
155
  // the ignore patterns
135
156
  const ignorePatterns = [].concat(options.excludeSpecPattern)
157
+
158
+ // when using component spec pattern, ignore all E2E specs
159
+ if (type === 'component') {
160
+ const e2eIgnorePattern = options.e2e?.specPattern || e2eDefaults.specPattern
161
+ ignorePatterns.push(e2eIgnorePattern)
162
+ }
163
+
136
164
  debug('ignore patterns %o', ignorePatterns)
137
165
 
138
166
  // a function which returns true if the file does NOT match
@@ -155,21 +183,35 @@ function findCypressSpecsV10(opts = {}) {
155
183
  return filtered
156
184
  }
157
185
 
158
- function getSpecs() {
159
- const options = getConfig()
160
- return findCypressSpecs(options)
186
+ function getSpecs(options, type = 'e2e') {
187
+ if (typeof options === 'undefined') {
188
+ options = getConfig()
189
+ }
190
+ return findCypressSpecs(options, type)
161
191
  }
162
192
 
163
- function findCypressSpecs(options) {
164
- if (options.e2e) {
165
- debug('config has "e2e" property, treating as Cypress v10+')
166
- const specs = findCypressSpecsV10(options)
193
+ function findCypressSpecs(options, type = 'e2e') {
194
+ debug('finding specs of type %s', type)
195
+
196
+ if (type === 'e2e') {
197
+ if (options.e2e) {
198
+ debug('config has "e2e" property, treating as Cypress v10+')
199
+ const specs = findCypressSpecsV10(options, type)
200
+ return specs
201
+ }
202
+
203
+ debug('reading Cypress config < v10')
204
+ const specs = findCypressSpecsV9(options)
167
205
  return specs
206
+ } else if (type === 'component') {
207
+ debug('finding component specs')
208
+ const specs = findCypressSpecsV10(options, type)
209
+ return specs
210
+ } else {
211
+ console.error('Do not know how to find specs of type "%s"', type)
212
+ console.error('returning an empty list')
213
+ return []
168
214
  }
169
-
170
- debug('reading Cypress config < v10')
171
- const specs = findCypressSpecsV9(options)
172
- return specs
173
215
  }
174
216
 
175
217
  function collectResults(structure, results) {
@@ -266,6 +308,68 @@ function findChangedFiles(branch, useParent) {
266
308
  }
267
309
  }
268
310
 
311
+ /**
312
+ * Collects all specs and for each finds all suits and tests with their tags.
313
+ */
314
+ function getTests(specs, options = {}) {
315
+ if (!specs) {
316
+ specs = getSpecs()
317
+ }
318
+
319
+ const { tags, tagged, skipped } = options
320
+
321
+ // counts the number of tests for each tag across all specs
322
+ const tagTestCounts = {}
323
+ const jsonResults = {}
324
+
325
+ specs.forEach((filename) => {
326
+ jsonResults[filename] = {
327
+ counts: {
328
+ tests: 0,
329
+ pending: 0,
330
+ },
331
+ tests: [],
332
+ }
333
+ const source = fs.readFileSync(filename, 'utf8')
334
+ const result = getTestNames(source, true)
335
+ // enable if need to debug the parsed test
336
+ // console.dir(result.structure, { depth: null })
337
+ collectResults(result.structure, jsonResults[filename].tests)
338
+
339
+ if (tags) {
340
+ const specTagCounts = countTags(result.structure)
341
+ Object.keys(specTagCounts).forEach((tag) => {
342
+ if (!(tag in tagTestCounts)) {
343
+ tagTestCounts[tag] = specTagCounts[tag]
344
+ } else {
345
+ tagTestCounts[tag] += specTagCounts[tag]
346
+ }
347
+ })
348
+ }
349
+ })
350
+
351
+ addCounts(jsonResults)
352
+
353
+ if (tagged) {
354
+ // filter all collected tests to those that have the given tag(s)
355
+ const splitTags = tagged
356
+ .split(',')
357
+ .map((s) => s.trim())
358
+ .filter(Boolean)
359
+ debug('filtering all tests by tag "%o"', splitTags)
360
+ pickTaggedTestsFrom(jsonResults, splitTags)
361
+ // recompute the number of tests
362
+ addCounts(jsonResults)
363
+ } else if (skipped) {
364
+ debug('leaving only skipped (pending) tests')
365
+ leavePendingTestsOnly(jsonResults)
366
+ // recompute the number of tests
367
+ addCounts(jsonResults)
368
+ }
369
+
370
+ return { jsonResults, tagTestCounts }
371
+ }
372
+
269
373
  module.exports = {
270
374
  getSpecs,
271
375
  // individual utilities
@@ -273,4 +377,5 @@ module.exports = {
273
377
  findCypressSpecs,
274
378
  collectResults,
275
379
  findChangedFiles,
380
+ getTests,
276
381
  }