find-cypress-specs 1.11.0 → 1.13.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 +18 -1
- package/bin/find.js +32 -43
- package/package.json +5 -3
- package/src/count.js +56 -0
- package/src/print.js +50 -0
- package/src/tagged.js +54 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# find-cypress-specs [![renovate-app badge][renovate-badge]][renovate-app]  [](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
|
|
|
@@ -17,6 +17,16 @@ $ npx find-cypress-specs --branch main
|
|
|
17
17
|
# prints only some specs, the ones that have changed against the "origin/main"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
### number of changed files
|
|
21
|
+
|
|
22
|
+
You can print just the number of changed specs
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
$ npx find-cypress-specs --branch main --count
|
|
26
|
+
# prints the number of spec files changed against the branch "origin/main"
|
|
27
|
+
5
|
|
28
|
+
```
|
|
29
|
+
|
|
20
30
|
## Test names
|
|
21
31
|
|
|
22
32
|
You can print each spec file with the suite and test names inside of it (found using [find-test-names](https://github.com/bahmutov/find-test-names))
|
|
@@ -60,6 +70,13 @@ Each tag count includes the tests that use the tag directly, and the _effective_
|
|
|
60
70
|
|
|
61
71
|
You can print the results in JSON format using `--json` or `-j` option.
|
|
62
72
|
|
|
73
|
+
## Test names filtered by a tag
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
$ npx find-cypress-specs --names --tagged <single tag>
|
|
77
|
+
# finds all specs and tests, then filters the output by a single tag
|
|
78
|
+
```
|
|
79
|
+
|
|
63
80
|
## Details
|
|
64
81
|
|
|
65
82
|
Cypress uses the resolved [configuration values](https://on.cypress.io/configuration) to find the spec files to run. It searches the `integrationFolder` for all patterns listed in `testFiles` and removes any files matching the `ignoreTestFiles` patterns.
|
package/bin/find.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const arg = require('arg')
|
|
4
4
|
const { getSpecs, collectResults, findChangedFiles } = require('../src')
|
|
5
|
+
const { pickTaggedTestsFrom } = require('../src/tagged')
|
|
6
|
+
const { addCounts } = require('../src/count')
|
|
7
|
+
const { stringAllInfo } = require('../src/print')
|
|
8
|
+
|
|
5
9
|
const fs = require('fs')
|
|
6
10
|
const pluralize = require('pluralize')
|
|
7
11
|
const { getTestNames, formatTestList, countTags } = require('find-test-names')
|
|
@@ -15,6 +19,9 @@ const args = arg({
|
|
|
15
19
|
'--json': Boolean,
|
|
16
20
|
// find the specs that have changed against this Git branch
|
|
17
21
|
'--branch': String,
|
|
22
|
+
'--count': Boolean,
|
|
23
|
+
// filter all tests to those that have the given tag
|
|
24
|
+
'--tagged': String,
|
|
18
25
|
|
|
19
26
|
// aliases
|
|
20
27
|
'-n': '--names',
|
|
@@ -33,43 +40,23 @@ if (args['--names'] || args['--tags']) {
|
|
|
33
40
|
console.log('no specs found')
|
|
34
41
|
} else {
|
|
35
42
|
console.log('')
|
|
36
|
-
let testsN = 0
|
|
37
|
-
let pendingTestsN = 0
|
|
38
|
-
|
|
39
43
|
// counts the number of tests for each tag across all specs
|
|
40
44
|
const tagTestCounts = {}
|
|
41
|
-
|
|
42
45
|
const jsonResults = {}
|
|
43
46
|
|
|
44
47
|
specs.forEach((filename) => {
|
|
45
|
-
jsonResults[filename] =
|
|
48
|
+
jsonResults[filename] = {
|
|
49
|
+
counts: {
|
|
50
|
+
tests: 0,
|
|
51
|
+
pending: 0,
|
|
52
|
+
},
|
|
53
|
+
tests: [],
|
|
54
|
+
}
|
|
46
55
|
const source = fs.readFileSync(filename, 'utf8')
|
|
47
56
|
const result = getTestNames(source, true)
|
|
48
57
|
// enable if need to debug the parsed test
|
|
49
58
|
// console.dir(result.structure, { depth: null })
|
|
50
|
-
|
|
51
|
-
testsN += result.testCount
|
|
52
|
-
const testCount = pluralize('test', result.testNames.length, true)
|
|
53
|
-
pendingTestsN += result.pendingTestCount
|
|
54
|
-
|
|
55
|
-
if (args['--names']) {
|
|
56
|
-
if (args['--json']) {
|
|
57
|
-
collectResults(result.structure, jsonResults[filename])
|
|
58
|
-
} else {
|
|
59
|
-
if (result.pendingTestCount) {
|
|
60
|
-
console.log(
|
|
61
|
-
'%s (%s, %d pending)',
|
|
62
|
-
filename,
|
|
63
|
-
testCount,
|
|
64
|
-
result.pendingTestCount,
|
|
65
|
-
)
|
|
66
|
-
} else {
|
|
67
|
-
console.log('%s (%s)', filename, testCount)
|
|
68
|
-
}
|
|
69
|
-
console.log(formatTestList(result.structure))
|
|
70
|
-
console.log('')
|
|
71
|
-
}
|
|
72
|
-
}
|
|
59
|
+
collectResults(result.structure, jsonResults[filename].tests)
|
|
73
60
|
|
|
74
61
|
if (args['--tags']) {
|
|
75
62
|
const specTagCounts = countTags(result.structure)
|
|
@@ -83,24 +70,22 @@ if (args['--names'] || args['--tags']) {
|
|
|
83
70
|
}
|
|
84
71
|
})
|
|
85
72
|
|
|
73
|
+
addCounts(jsonResults)
|
|
74
|
+
|
|
86
75
|
if (args['--names']) {
|
|
76
|
+
if (args['--tagged']) {
|
|
77
|
+
// filter all collected tests to those that have the given tag
|
|
78
|
+
debug('filtering all tests by tag "%s"', args['--tagged'])
|
|
79
|
+
pickTaggedTestsFrom(jsonResults, args['--tagged'])
|
|
80
|
+
// recompute the number of tests
|
|
81
|
+
addCounts(jsonResults)
|
|
82
|
+
}
|
|
83
|
+
|
|
87
84
|
if (args['--json']) {
|
|
88
85
|
console.log(JSON.stringify(jsonResults, null, 2))
|
|
89
86
|
} else {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
'found %s (%s, %d pending)',
|
|
93
|
-
pluralize('spec', specs.length, true),
|
|
94
|
-
pluralize('test', testsN, true),
|
|
95
|
-
pendingTestsN,
|
|
96
|
-
)
|
|
97
|
-
} else {
|
|
98
|
-
console.log(
|
|
99
|
-
'found %s (%s)',
|
|
100
|
-
pluralize('spec', specs.length, true),
|
|
101
|
-
pluralize('test', testsN, true),
|
|
102
|
-
)
|
|
103
|
-
}
|
|
87
|
+
const str = stringAllInfo(jsonResults)
|
|
88
|
+
console.log(str)
|
|
104
89
|
}
|
|
105
90
|
console.log('')
|
|
106
91
|
}
|
|
@@ -126,7 +111,11 @@ if (args['--names'] || args['--tags']) {
|
|
|
126
111
|
const changedFiles = findChangedFiles(args['--branch'])
|
|
127
112
|
debug('changed files %o', changedFiles)
|
|
128
113
|
const changedSpecs = specs.filter((file) => changedFiles.includes(file))
|
|
129
|
-
|
|
114
|
+
if (args['--count']) {
|
|
115
|
+
console.log(changedSpecs.length)
|
|
116
|
+
} else {
|
|
117
|
+
console.log(changedSpecs.join(','))
|
|
118
|
+
}
|
|
130
119
|
} else {
|
|
131
120
|
console.log(specs.join(','))
|
|
132
121
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "find-cypress-specs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Find Cypress spec files using the config settings",
|
|
5
5
|
"main": "src",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"demo-names-and-tags": "node ./bin/find --names --tags",
|
|
21
21
|
"demo-names-and-tags-json": "node ./bin/find --names --tags --json",
|
|
22
22
|
"demo-names-json": "node ./bin/find --names --json",
|
|
23
|
+
"demo-names-tagged": "node ./bin/find --names --tagged @user",
|
|
23
24
|
"print-changed-specs": "node ./bin/find --branch main",
|
|
25
|
+
"count-changed-specs": "node ./bin/find --branch main --count",
|
|
24
26
|
"semantic-release": "semantic-release"
|
|
25
27
|
},
|
|
26
28
|
"repository": {
|
|
@@ -38,10 +40,10 @@
|
|
|
38
40
|
"homepage": "https://github.com/bahmutov/find-cypress-specs#readme",
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"ava": "^4.0.0",
|
|
41
|
-
"cypress": "
|
|
43
|
+
"cypress": "9.5.1",
|
|
42
44
|
"execa-wrap": "^1.4.0",
|
|
43
45
|
"prettier": "^2.5.1",
|
|
44
|
-
"semantic-release": "
|
|
46
|
+
"semantic-release": "19.0.2"
|
|
45
47
|
},
|
|
46
48
|
"dependencies": {
|
|
47
49
|
"arg": "^5.0.1",
|
package/src/count.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For each file and each
|
|
3
|
+
*/
|
|
4
|
+
function addCounts(json) {
|
|
5
|
+
Object.keys(json).forEach((filename) => {
|
|
6
|
+
const fileInfo = json[filename]
|
|
7
|
+
if (!fileInfo.counts) {
|
|
8
|
+
fileInfo.counts = {
|
|
9
|
+
tests: 0,
|
|
10
|
+
pending: 0,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fileInfo.counts.tests =
|
|
15
|
+
countTests(fileInfo.tests) + countTests(fileInfo.suites)
|
|
16
|
+
fileInfo.counts.pending =
|
|
17
|
+
countPendingTests(fileInfo.tests) + countPendingTests(fileInfo.suites)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function countTests(testsOrSuites) {
|
|
22
|
+
if (!testsOrSuites) {
|
|
23
|
+
return 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return testsOrSuites.reduce((count, test) => {
|
|
27
|
+
if (test.type === 'test') {
|
|
28
|
+
return count + 1
|
|
29
|
+
} else if (test.type === 'suite') {
|
|
30
|
+
return count + countTests(test.tests) + countTests(test.suites)
|
|
31
|
+
}
|
|
32
|
+
}, 0)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function countPendingTests(testsOrSuites) {
|
|
36
|
+
if (!testsOrSuites) {
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return testsOrSuites.reduce((count, test) => {
|
|
41
|
+
if (test.type === 'test') {
|
|
42
|
+
return test.pending ? count + 1 : count
|
|
43
|
+
} else if (test.type === 'suite') {
|
|
44
|
+
if (test.pending) {
|
|
45
|
+
// all tests inside should count as pending
|
|
46
|
+
return count + countTests(test.tests) + countTests(test.suites)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
count + countPendingTests(test.tests) + countPendingTests(test.suites)
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
}, 0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { addCounts }
|
package/src/print.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const pluralize = require('pluralize')
|
|
2
|
+
const { formatTestList } = require('find-test-names')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Outputs a string representation of the json test results object,
|
|
6
|
+
* like a tree of suites and tests.
|
|
7
|
+
*/
|
|
8
|
+
function stringFileTests(fileName, fileInfo) {
|
|
9
|
+
const testCount = pluralize('test', fileInfo.counts.tests, true)
|
|
10
|
+
const headerLine = fileInfo.counts.pending
|
|
11
|
+
? `${fileName} (${testCount}, ${fileInfo.counts.pending} pending)`
|
|
12
|
+
: `${fileName} (${testCount})`
|
|
13
|
+
|
|
14
|
+
const body = formatTestList(fileInfo.tests)
|
|
15
|
+
|
|
16
|
+
return headerLine + '\n' + body + '\n'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function stringAllInfo(allInfo) {
|
|
20
|
+
let fileCount = 0
|
|
21
|
+
let testCount = 0
|
|
22
|
+
let pendingTestCount = 0
|
|
23
|
+
|
|
24
|
+
const allInfoString = Object.keys(allInfo)
|
|
25
|
+
.map((fileName) => {
|
|
26
|
+
const fileInfo = allInfo[fileName]
|
|
27
|
+
fileCount += 1
|
|
28
|
+
testCount += fileInfo.counts.tests
|
|
29
|
+
pendingTestCount += fileInfo.counts.pending
|
|
30
|
+
return stringFileTests(fileName, fileInfo)
|
|
31
|
+
})
|
|
32
|
+
.join('\n')
|
|
33
|
+
|
|
34
|
+
// footer line is something like
|
|
35
|
+
// found 2 specs (4 tests, 1 pending)
|
|
36
|
+
let footer = `found ${pluralize('spec', fileCount, true)}`
|
|
37
|
+
const testWord = pluralize('test', testCount, true)
|
|
38
|
+
if (pendingTestCount) {
|
|
39
|
+
footer += ` (${testWord}, ${pendingTestCount} pending)`
|
|
40
|
+
} else {
|
|
41
|
+
footer += ` (${testWord})`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return allInfoString + '\n' + footer
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
stringFileTests,
|
|
49
|
+
stringAllInfo,
|
|
50
|
+
}
|
package/src/tagged.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { addCounts } = require('./count')
|
|
2
|
+
|
|
3
|
+
// note: modifies the tests in place
|
|
4
|
+
function pickTaggedTests(tests, tag) {
|
|
5
|
+
if (!Array.isArray(tests)) {
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
const filteredTests = tests.filter((test) => {
|
|
9
|
+
if (test.type === 'test') {
|
|
10
|
+
return test.tags && test.tags.includes(tag)
|
|
11
|
+
} else if (test.type === 'suite') {
|
|
12
|
+
if (test.tags && test.tags.includes(tag)) {
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// maybe there is some test inside this suite
|
|
17
|
+
// with the tag? Filter all other tests
|
|
18
|
+
return (
|
|
19
|
+
pickTaggedTests(test.tests, tag) || pickTaggedTests(test.suites, tag)
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
tests.length = 0
|
|
24
|
+
tests.push(...filteredTests)
|
|
25
|
+
return filteredTests.length > 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function removeEmptyNodes(json) {
|
|
29
|
+
Object.keys(json).forEach((filename) => {
|
|
30
|
+
const fileTests = json[filename].tests
|
|
31
|
+
if (!fileTests.length) {
|
|
32
|
+
delete json[filename]
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
return json
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Takes an object of tests collected from all files,
|
|
40
|
+
* and removes all tests that do not have the given tag applied.
|
|
41
|
+
* Modifies the given object in place.
|
|
42
|
+
*/
|
|
43
|
+
function pickTaggedTestsFrom(json, tag) {
|
|
44
|
+
Object.keys(json).forEach((filename) => {
|
|
45
|
+
const fileTests = json[filename].tests
|
|
46
|
+
pickTaggedTests(fileTests, tag)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const result = removeEmptyNodes(json)
|
|
50
|
+
addCounts(result)
|
|
51
|
+
return result
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { pickTaggedTestsFrom, removeEmptyNodes, pickTaggedTests }
|