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 CHANGED
@@ -1,4 +1,4 @@
1
- # find-cypress-specs [![renovate-app badge][renovate-badge]][renovate-app] ![cypress version](https://img.shields.io/badge/cypress-9.2.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-9.5.1-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
 
@@ -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
- if (pendingTestsN) {
91
- console.log(
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
- console.log(changedSpecs.join(','))
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.11.0",
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": "^9.2.0",
43
+ "cypress": "9.5.1",
42
44
  "execa-wrap": "^1.4.0",
43
45
  "prettier": "^2.5.1",
44
- "semantic-release": "^18.0.1"
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 }