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 +9 -0
- package/bin/find.js +189 -141
- package/package.json +3 -2
- package/src/badge.js +77 -0
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
145
|
-
return specHasTags
|
|
146
|
-
})
|
|
147
|
-
}
|
|
173
|
+
}
|
|
148
174
|
|
|
149
|
-
|
|
150
|
-
debug('
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
206
|
-
|
|
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
|
|
212
|
-
|
|
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.
|
|
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.
|
|
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 ``
|
|
20
|
+
} else {
|
|
21
|
+
// only e2e tests
|
|
22
|
+
return ``
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
if (isNumber(nComponent)) {
|
|
26
|
+
// we have only the number of component tests
|
|
27
|
+
return ``
|
|
28
|
+
} else {
|
|
29
|
+
// we have nothing
|
|
30
|
+
return ``
|
|
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 }
|