find-cypress-specs 1.31.5 → 1.32.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
@@ -226,6 +226,15 @@ $ npx find-cypress-specs --names --skipped
226
226
 
227
227
  Prints each spec that has skipped tests.
228
228
 
229
+ ## Count tests
230
+
231
+ You can see the total number of E2E and component tests
232
+
233
+ ```
234
+ $ npx find-cypress-specs --test-counts
235
+ 4 e2e tests, 2 component tests
236
+ ```
237
+
229
238
  ## Count skipped tests
230
239
 
231
240
  Prints the single number with the count of skipped tests
package/bin/find.js CHANGED
@@ -11,6 +11,7 @@ const consoleTable = require('console.table')
11
11
  const debug = require('debug')('find-cypress-specs')
12
12
  const { getDependsInFolder } = require('spec-change')
13
13
  const core = require('@actions/core')
14
+ const pluralize = require('pluralize')
14
15
 
15
16
  const args = arg({
16
17
  '--names': Boolean,
@@ -42,6 +43,8 @@ const args = arg({
42
43
  '--max-added-traced-specs': Number,
43
44
  // find component specs
44
45
  '--component': Boolean,
46
+ // count total number of E2E and component tests
47
+ '--test-counts': Boolean,
45
48
  // aliases
46
49
  '-n': '--names',
47
50
  '--name': '--names',
@@ -54,171 +57,216 @@ const args = arg({
54
57
  // calls "it.skip" pending tests
55
58
  // https://glebbahmutov.com/blog/cypress-test-statuses/
56
59
  '--pending': '--skipped',
60
+ '--tc': '--test-counts',
57
61
  })
58
62
 
59
63
  debug('arguments %o', args)
60
- const specType = args['--component'] ? 'component' : 'e2e'
61
-
62
- const specs = getSpecs(undefined, specType)
63
-
64
- if (args['--branch']) {
65
- debug('determining specs changed against branch %s', args['--branch'])
66
- let changedFiles = findChangedFiles(args['--branch'], args['--parent'])
67
- debug('changed files %o', changedFiles)
68
- debug('comparing against the specs %o', specs)
69
- if (args['--trace-imports']) {
70
- debug('tracing dependent changes in folder %s', args['--trace-imports'])
71
-
72
- const saveDependenciesFile = 'deps.json'
73
- let deps
74
- if (args['--cache-trace']) {
75
- if (fs.existsSync(saveDependenciesFile)) {
76
- debug(
77
- 'loading cached traced dependencies from file %s',
78
- saveDependenciesFile,
79
- )
80
- deps = JSON.parse(fs.readFileSync(saveDependenciesFile, 'utf-8')).deps
81
- }
82
- }
83
64
 
84
- if (!deps) {
85
- const absoluteFolder = path.join(process.cwd(), args['--trace-imports'])
86
- const depsOptions = { folder: absoluteFolder, time: args['--time-trace'] }
65
+ if (args['--test-counts']) {
66
+ debug('finding all e2e specs')
67
+ const e2eSpecs = getSpecs(undefined, 'e2e')
68
+ debug('found %d e2e specs', e2eSpecs.length)
69
+ debug('finding all component specs')
70
+ const componentSpecs = getSpecs(undefined, 'component')
71
+ debug('found %d component specs', componentSpecs.length)
72
+
73
+ debug('counting all e2e tests')
74
+ const { jsonResults: e2eResults } = getTests(e2eSpecs)
75
+ debug(e2eResults)
76
+ let nE2E = 0
77
+ Object.keys(e2eResults).forEach((filename) => {
78
+ const n = e2eResults[filename].counts.tests
79
+ nE2E += n
80
+ })
81
+ debug('found %d E2E tests', nE2E)
82
+
83
+ debug('counting all component tests')
84
+ const { jsonResults: componentResults } = getTests(componentSpecs)
85
+ debug(componentResults)
86
+ let nComponent = 0
87
+ Object.keys(componentResults).forEach((filename) => {
88
+ const n = componentResults[filename].counts.tests
89
+ nComponent += n
90
+ })
91
+ debug('found %d component tests', nComponent)
92
+ console.log(
93
+ '%d e2e %s, %d component %s',
94
+ nE2E,
95
+ pluralize('test', nE2E),
96
+ nComponent,
97
+ pluralize('test', nComponent),
98
+ )
99
+ } else {
100
+ const specType = args['--component'] ? 'component' : 'e2e'
101
+
102
+ const specs = getSpecs(undefined, specType)
103
+
104
+ if (args['--branch']) {
105
+ debug('determining specs changed against branch %s', args['--branch'])
106
+ let changedFiles = findChangedFiles(args['--branch'], args['--parent'])
107
+ debug('changed files %o', changedFiles)
108
+ debug('comparing against the specs %o', specs)
109
+ if (args['--trace-imports']) {
110
+ debug('tracing dependent changes in folder %s', args['--trace-imports'])
111
+
112
+ const saveDependenciesFile = 'deps.json'
113
+ let deps
87
114
  if (args['--cache-trace']) {
88
- depsOptions.saveDepsFilename = saveDependenciesFile
89
- debug(
90
- 'will save found dependencies into the file %s',
91
- saveDependenciesFile,
92
- )
115
+ if (fs.existsSync(saveDependenciesFile)) {
116
+ debug(
117
+ 'loading cached traced dependencies from file %s',
118
+ saveDependenciesFile,
119
+ )
120
+ deps = JSON.parse(fs.readFileSync(saveDependenciesFile, 'utf-8')).deps
121
+ }
93
122
  }
94
- debug('tracing options %o', depsOptions)
95
- deps = getDependsInFolder(depsOptions)
96
- }
97
- debug('traced dependencies via imports and require')
98
- debug(deps)
99
-
100
- // add a sensible limit to the number of extra specs to add
101
- // when we trace the dependencies in the changed source files
102
- const addedTracedFiles = []
103
- const maxAddTracedFiles = args['--max-added-traced-specs'] || 1000
104
- debug('maximum traced files to add %d', maxAddTracedFiles)
105
-
106
- Object.entries(deps).forEach(([filename, fileDependents]) => {
107
- const f = path.join(args['--trace-imports'], filename)
108
- if (changedFiles.includes(f)) {
109
- debug(
110
- 'the source file %s has changed, including its dependents %o in the list of changed files',
111
- f,
112
- fileDependents,
113
- )
114
- fileDependents.forEach((name) => {
115
- const nameInCypressFolder = path.join(args['--trace-imports'], name)
116
- if (!changedFiles.includes(nameInCypressFolder)) {
117
- if (addedTracedFiles.length < maxAddTracedFiles) {
118
- changedFiles.push(nameInCypressFolder)
119
- addedTracedFiles.push(nameInCypressFolder)
120
- }
121
- }
122
- })
123
+
124
+ if (!deps) {
125
+ const absoluteFolder = path.join(process.cwd(), args['--trace-imports'])
126
+ const depsOptions = {
127
+ folder: absoluteFolder,
128
+ time: args['--time-trace'],
129
+ }
130
+ if (args['--cache-trace']) {
131
+ depsOptions.saveDepsFilename = saveDependenciesFile
132
+ debug(
133
+ 'will save found dependencies into the file %s',
134
+ saveDependenciesFile,
135
+ )
136
+ }
137
+ debug('tracing options %o', depsOptions)
138
+ deps = getDependsInFolder(depsOptions)
123
139
  }
124
- })
125
- debug('added %d traced specs %o', addedTracedFiles.length, addedTracedFiles)
126
- }
140
+ debug('traced dependencies via imports and require')
141
+ debug(deps)
142
+
143
+ // add a sensible limit to the number of extra specs to add
144
+ // when we trace the dependencies in the changed source files
145
+ const addedTracedFiles = []
146
+ const maxAddTracedFiles = args['--max-added-traced-specs'] || 1000
147
+ debug('maximum traced files to add %d', maxAddTracedFiles)
127
148
 
128
- let changedSpecs = specs.filter((file) => changedFiles.includes(file))
129
- debug('changed %d specs %o', changedSpecs.length, changedSpecs)
130
-
131
- if (args['--tagged']) {
132
- const splitTags = args['--tagged']
133
- .split(',')
134
- .map((s) => s.trim())
135
- .filter(Boolean)
136
- debug('filtering changed specs by tags %o', splitTags)
137
- changedSpecs = changedSpecs.filter((file) => {
138
- const source = fs.readFileSync(file, 'utf8')
139
- const result = getTestNames(source, true)
140
- const specTagCounts = countTags(result.structure)
141
- const specHasTags = Object.keys(specTagCounts).some((tag) =>
142
- splitTags.includes(tag),
149
+ Object.entries(deps).forEach(([filename, fileDependents]) => {
150
+ const f = path.join(args['--trace-imports'], filename)
151
+ if (changedFiles.includes(f)) {
152
+ debug(
153
+ 'the source file %s has changed, including its dependents %o in the list of changed files',
154
+ f,
155
+ fileDependents,
156
+ )
157
+ fileDependents.forEach((name) => {
158
+ const nameInCypressFolder = path.join(args['--trace-imports'], name)
159
+ if (!changedFiles.includes(nameInCypressFolder)) {
160
+ if (addedTracedFiles.length < maxAddTracedFiles) {
161
+ changedFiles.push(nameInCypressFolder)
162
+ addedTracedFiles.push(nameInCypressFolder)
163
+ }
164
+ }
165
+ })
166
+ }
167
+ })
168
+ debug(
169
+ 'added %d traced specs %o',
170
+ addedTracedFiles.length,
171
+ addedTracedFiles,
143
172
  )
144
- debug('spec %s has any of the tags? %o', file, specHasTags)
145
- return specHasTags
146
- })
147
- }
173
+ }
148
174
 
149
- if (args['--set-gha-outputs']) {
150
- debug('setting GitHub Actions outputs changedSpecsN and changedSpecs')
151
- debug('changedSpecsN %d', changedSpecs.length)
152
- debug('plus changedSpecs')
153
- core.setOutput('changedSpecsN', changedSpecs.length)
154
- core.setOutput('changedSpecs', changedSpecs.join(','))
155
- }
175
+ let changedSpecs = specs.filter((file) => changedFiles.includes(file))
176
+ debug('changed %d specs %o', changedSpecs.length, changedSpecs)
156
177
 
157
- if (args['--count']) {
158
- console.log(changedSpecs.length)
159
- } else {
160
- console.log(changedSpecs.join(','))
161
- }
162
- } else if (args['--names'] || args['--tags'] || args['--tagged']) {
163
- // counts the number of tests for each tag across all specs
164
- const { jsonResults, tagTestCounts } = getTests(specs, {
165
- tags: args['--tags'],
166
- tagged: args['--tagged'],
167
- skipped: args['--skipped'],
168
- })
178
+ if (args['--tagged']) {
179
+ const splitTags = args['--tagged']
180
+ .split(',')
181
+ .map((s) => s.trim())
182
+ .filter(Boolean)
183
+ debug('filtering changed specs by tags %o', splitTags)
184
+ changedSpecs = changedSpecs.filter((file) => {
185
+ const source = fs.readFileSync(file, 'utf8')
186
+ const result = getTestNames(source, true)
187
+ const specTagCounts = countTags(result.structure)
188
+ const specHasTags = Object.keys(specTagCounts).some((tag) =>
189
+ splitTags.includes(tag),
190
+ )
191
+ debug('spec %s has any of the tags? %o', file, specHasTags)
192
+ return specHasTags
193
+ })
194
+ }
195
+
196
+ if (args['--set-gha-outputs']) {
197
+ debug('setting GitHub Actions outputs changedSpecsN and changedSpecs')
198
+ debug('changedSpecsN %d', changedSpecs.length)
199
+ debug('plus changedSpecs')
200
+ core.setOutput('changedSpecsN', changedSpecs.length)
201
+ core.setOutput('changedSpecs', changedSpecs.join(','))
202
+ }
169
203
 
170
- if (args['--names']) {
171
204
  if (args['--count']) {
172
- let n = 0
173
- Object.keys(jsonResults).forEach((filename) => {
174
- const skippedCount = jsonResults[filename].counts.pending
175
- n += skippedCount
176
- })
177
- console.log(n)
205
+ console.log(changedSpecs.length)
178
206
  } else {
179
- if (args['--json']) {
180
- console.log(JSON.stringify(jsonResults, null, 2))
207
+ console.log(changedSpecs.join(','))
208
+ }
209
+ } else if (args['--names'] || args['--tags'] || args['--tagged']) {
210
+ // counts the number of tests for each tag across all specs
211
+ const { jsonResults, tagTestCounts } = getTests(specs, {
212
+ tags: args['--tags'],
213
+ tagged: args['--tagged'],
214
+ skipped: args['--skipped'],
215
+ })
216
+
217
+ if (args['--names']) {
218
+ if (args['--count']) {
219
+ let n = 0
220
+ Object.keys(jsonResults).forEach((filename) => {
221
+ const skippedCount = jsonResults[filename].counts.pending
222
+ n += skippedCount
223
+ })
224
+ console.log(n)
181
225
  } else {
182
- const str = stringAllInfo(jsonResults)
183
- console.log(str)
226
+ if (args['--json']) {
227
+ console.log(JSON.stringify(jsonResults, null, 2))
228
+ } else {
229
+ const str = stringAllInfo(jsonResults)
230
+ console.log(str)
231
+ }
232
+ console.log('')
184
233
  }
185
- console.log('')
186
234
  }
187
- }
188
235
 
189
- if (args['--tags']) {
190
- const tagEntries = Object.entries(tagTestCounts)
191
- const sortedTagEntries = tagEntries.sort((a, b) => {
192
- // every entry is [tag, count], so compare the tags
193
- return a[0].localeCompare(b[0])
194
- })
195
- if (args['--json']) {
196
- // assemble a json object with the tag counts
197
- const tagResults = Object.fromEntries(sortedTagEntries)
198
- console.log(JSON.stringify(tagResults, null, 2))
199
- } else {
200
- const table = consoleTable.getTable(['Tag', 'Tests'], sortedTagEntries)
201
- console.log(table)
236
+ if (args['--tags']) {
237
+ const tagEntries = Object.entries(tagTestCounts)
238
+ const sortedTagEntries = tagEntries.sort((a, b) => {
239
+ // every entry is [tag, count], so compare the tags
240
+ return a[0].localeCompare(b[0])
241
+ })
242
+ if (args['--json']) {
243
+ // assemble a json object with the tag counts
244
+ const tagResults = Object.fromEntries(sortedTagEntries)
245
+ console.log(JSON.stringify(tagResults, null, 2))
246
+ } else {
247
+ const table = consoleTable.getTable(['Tag', 'Tests'], sortedTagEntries)
248
+ console.log(table)
249
+ }
202
250
  }
203
- }
204
251
 
205
- if (!args['--names'] && !args['--tags']) {
206
- const specs = Object.keys(jsonResults)
252
+ if (!args['--names'] && !args['--tags']) {
253
+ const specs = Object.keys(jsonResults)
254
+ if (args['--count']) {
255
+ debug('printing the number of specs %d', specs.length)
256
+ console.log(specs.length)
257
+ } else {
258
+ debug('printing the spec names list only')
259
+ const specNames = specs.join(',')
260
+ console.log(specNames)
261
+ }
262
+ }
263
+ } else {
207
264
  if (args['--count']) {
208
265
  debug('printing the number of specs %d', specs.length)
209
266
  console.log(specs.length)
210
267
  } else {
211
- debug('printing the spec names list only')
212
- const specNames = specs.join(',')
213
- console.log(specNames)
268
+ debug('printing just %d spec names', specs.length)
269
+ console.log(specs.join(','))
214
270
  }
215
271
  }
216
- } else {
217
- if (args['--count']) {
218
- debug('printing the number of specs %d', specs.length)
219
- console.log(specs.length)
220
- } else {
221
- debug('printing just %d spec names', specs.length)
222
- console.log(specs.join(','))
223
- }
224
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "find-cypress-specs",
3
- "version": "1.31.5",
3
+ "version": "1.32.0",
4
4
  "description": "Find Cypress spec files using the config settings",
5
5
  "main": "src",
6
6
  "files": [
@@ -26,6 +26,7 @@
26
26
  "demo-names-tagged": "node ./bin/find --names --tagged @user",
27
27
  "print-changed-specs": "node ./bin/find --branch main",
28
28
  "count-changed-specs": "node ./bin/find --branch main --count",
29
+ "demo-test-counts": "node ./bin/find --test-counts",
29
30
  "semantic-release": "semantic-release",
30
31
  "deps": "spec-change --folder . --mask 'cypress/**/*.{js,ts}'",
31
32
  "deps-changed": "DEBUG=find-cypress-specs node ./bin/find --branch main --parent --trace-imports cypress --time-trace --cache-trace",
@@ -51,7 +52,7 @@
51
52
  "execa-wrap": "^1.4.0",
52
53
  "prettier": "^2.5.1",
53
54
  "really-need": "^1.9.2",
54
- "semantic-release": "21.0.0",
55
+ "semantic-release": "21.0.1",
55
56
  "sinon": "^13.0.1",
56
57
  "typescript": "^4.6.3"
57
58
  },
package/src/badge.js ADDED
@@ -0,0 +1,77 @@
1
+ const debug = require('debug')('find-cypress-specs')
2
+ const os = require('os')
3
+
4
+ function isNumber(n) {
5
+ return typeof n === 'number' && !isNaN(n)
6
+ }
7
+
8
+ /**
9
+ * Returns Markdown for a badge with the given number of tests.
10
+ * @param {number} nE2E Number of end-to-end tests
11
+ * @param {number|undefined} nComponent Number of component tests (optional)
12
+ * @see https://shields.io/
13
+ */
14
+ function getBadgeMarkdown(nE2E, nComponent) {
15
+ debug('forming new test count badge with %o', { nE2E, nComponent })
16
+ if (isNumber(nE2E)) {
17
+ if (isNumber(nComponent)) {
18
+ // we have both e2e and component tests
19
+ return `![Cypress tests](https://img.shields.io/badge/cy%20tests-E2E%20${nE2E}%20%7C%20component%20${nComponent}-blue)`
20
+ } else {
21
+ // only e2e tests
22
+ return `![Cypress tests](https://img.shields.io/badge/cy%20tests-E2E%20${nE2E}-blue)`
23
+ }
24
+ } else {
25
+ if (isNumber(nComponent)) {
26
+ // we have only the number of component tests
27
+ return `![Cypress tests](https://img.shields.io/badge/cy%20tests-component%20${nComponent}-blue)`
28
+ } else {
29
+ // we have nothing
30
+ return `![Cypress tests](https://img.shields.io/badge/cy%20tests-unknown-inactive)`
31
+ }
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Replaces the whole Markdown image badge with new badge with test counters.
37
+ */
38
+ function replaceBadge({
39
+ markdown, // current markdown text
40
+ nE2E, // number of E2E tests
41
+ nComponent, // number of component tests
42
+ }) {
43
+ debug('replacing badge with new numbers %o', { nE2E, nComponent })
44
+ const badgeRe = new RegExp(
45
+ `\\!\\[Cypress tests\\]` +
46
+ '\\(https://img\\.shields\\.io/badge/cy%20tests\\-' +
47
+ '.+-(?:blue|inactive)\\)',
48
+ )
49
+
50
+ const badge = getBadgeMarkdown(nE2E, nComponent)
51
+ debug('new badge contents "%s"', badge)
52
+ let found
53
+
54
+ let updatedReadmeText = markdown.replace(badgeRe, (match) => {
55
+ found = true
56
+ return badge
57
+ })
58
+
59
+ if (!found) {
60
+ console.log('⚠️ Could not find test count badge')
61
+ console.log('Insert new badge on the first line')
62
+ debug('inserting new badge: %s', badge)
63
+
64
+ const lines = markdown.split(os.EOL)
65
+ if (lines.length < 1) {
66
+ console.error('README file has no lines, cannot insert test count badge')
67
+ return markdown
68
+ }
69
+ lines[0] += ' ' + badge
70
+ updatedReadmeText = lines.join(os.EOL)
71
+ } else {
72
+ debug('replaced badge')
73
+ }
74
+ return updatedReadmeText
75
+ }
76
+
77
+ module.exports = { getBadgeMarkdown, replaceBadge }