Skip to content

Commit fd04395

Browse files
authored
fix: filtering against list of specs (#182)
* fix: filtering against list of specs * add example project with single spec selected * update example * add example to CI * more coverage for one-spec * lower backend example coverage limit
1 parent 57bbc6b commit fd04395

File tree

15 files changed

+376
-40
lines changed

15 files changed

+376
-40
lines changed

.circleci/config.yml

+32-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ workflows:
7575
- run: npx nyc report --check-coverage true --lines 100 --include cypress/unit.js
7676

7777
- cypress/run:
78+
# TODO switch to separate example in "examples/..."
7879
name: backend coverage
7980
requires:
8081
- cypress/install
@@ -96,7 +97,7 @@ workflows:
9697
path: coverage
9798
# print code coverage summary to the terminal
9899
# and make sure there the coverage is above certain limit
99-
- run: npx nyc report --check-coverage true --lines 85
100+
- run: npx nyc report --check-coverage true --lines 72
100101
# and look at the server index file - should be fully covered
101102
- run: npx nyc report --check-coverage true --lines 100 --include test-backend/index.js
102103

@@ -280,6 +281,35 @@ workflows:
280281
node ../../scripts/only-covered main.js
281282
working_directory: examples/use-plugins-and-support
282283

284+
- cypress/run:
285+
attach-workspace: true
286+
name: example-one-spec
287+
requires:
288+
- cypress/install
289+
# there are no jobs to follow this one
290+
# so no need to save the workspace files (saves time)
291+
no-workspace: true
292+
command: npx cypress run --project examples/one-spec
293+
# store screenshots and videos
294+
store_artifacts: true
295+
post-steps:
296+
- run: cat examples/one-spec/.nyc_output/out.json
297+
# store the created coverage report folder
298+
# you can click on it in the CircleCI UI
299+
# to see live static HTML site
300+
- store_artifacts:
301+
path: examples/one-spec/coverage
302+
# make sure the examples captures 100% of code
303+
- run:
304+
command: npx nyc report --check-coverage true --lines 100
305+
working_directory: examples/one-spec
306+
- run:
307+
name: Check code coverage 📈
308+
command: |
309+
node ../../scripts/check-coverage main.js
310+
node ../../scripts/only-covered main.js
311+
working_directory: examples/one-spec
312+
283313
- publish:
284314
filters:
285315
branches:
@@ -296,3 +326,4 @@ workflows:
296326
- example-same-folder
297327
- example-support-files
298328
- example-use-plugins-and-support
329+
- example-one-spec

cypress/integration/filtering.js

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const { filterSpecsFromCoverage } = require('../../utils')
2+
3+
describe('minimatch', () => {
4+
it('string matches', () => {
5+
expect(
6+
Cypress.minimatch('/path/to/specA.js', '/path/to/specA.js'),
7+
'matches full strings'
8+
).to.be.true
9+
10+
expect(
11+
Cypress.minimatch('/path/to/specA.js', 'specA.js'),
12+
'does not match just the end'
13+
).to.be.false
14+
15+
expect(
16+
Cypress.minimatch('/path/to/specA.js', '**/specA.js'),
17+
'matches using **'
18+
).to.be.true
19+
})
20+
})
21+
22+
describe('filtering specs', () => {
23+
it('filters list of specs by single string', () => {
24+
const config = cy.stub()
25+
config.withArgs('testFiles').returns(['specA.js'])
26+
config.withArgs('integrationFolder').returns('/path/to/integration/')
27+
28+
const totalCoverage = {
29+
'/path/to/specA.js': {},
30+
'/path/to/specB.js': {}
31+
}
32+
const result = filterSpecsFromCoverage(totalCoverage, config)
33+
expect(result).to.deep.equal({
34+
'/path/to/specB.js': {}
35+
})
36+
})
37+
38+
it('filters list of specs by pattern', () => {
39+
const config = cy.stub()
40+
config.withArgs('testFiles').returns(['**/*B.js'])
41+
config.withArgs('integrationFolder').returns('/path/to/integration/')
42+
43+
const totalCoverage = {
44+
'/path/to/specA.js': {},
45+
'/path/to/specB.js': {}
46+
}
47+
const result = filterSpecsFromCoverage(totalCoverage, config)
48+
expect(result).to.deep.equal({
49+
'/path/to/specA.js': {}
50+
})
51+
})
52+
53+
it('filters list of specs by pattern and single spec', () => {
54+
const config = cy.stub()
55+
config.withArgs('testFiles').returns(['**/*B.js', 'specA.js'])
56+
config.withArgs('integrationFolder').returns('/path/to/integration/')
57+
58+
const totalCoverage = {
59+
'/path/to/specA.js': {},
60+
'/path/to/specB.js': {}
61+
}
62+
const result = filterSpecsFromCoverage(totalCoverage, config)
63+
expect(result, 'all specs have been filtered out').to.deep.equal({})
64+
})
65+
66+
it('filters list of specs in integration folder', () => {
67+
const config = cy.stub()
68+
config.withArgs('testFiles').returns('**/*.*') // default pattern
69+
config.withArgs('integrationFolder').returns('/path/to/integration/')
70+
71+
const totalCoverage = {
72+
'/path/to/specA.js': {},
73+
'/path/to/specB.js': {},
74+
// these files should be removed
75+
'/path/to/integration/spec1.js': {},
76+
'/path/to/integration/spec2.js': {}
77+
}
78+
const result = filterSpecsFromCoverage(totalCoverage, config)
79+
expect(result).to.deep.equal({
80+
'/path/to/specA.js': {},
81+
'/path/to/specB.js': {}
82+
})
83+
})
84+
})

examples/one-spec/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["istanbul"]
3+
}

examples/one-spec/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# example: one-spec
2+
3+
Only running a single spec

examples/one-spec/cypress.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"fixturesFolder": false,
3+
"pluginsFile": "cypress/plugins/index.js",
4+
"testFiles": ["spec-a.js"]
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference types="cypress" />
2+
it('spec a', () => {
3+
cy.visit('index.html')
4+
cy.contains('Page body')
5+
6+
cy.window()
7+
.invoke('add', 2, 3)
8+
.should('equal', 5)
9+
10+
cy.window()
11+
.invoke('sub', 2, 3)
12+
.should('equal', -1)
13+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="cypress" />
2+
it('spec b', () => {
3+
// should not run
4+
throw new Error('Spec b should not run')
5+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = (on, config) => {
2+
require('../../../../task')(on, config)
3+
on('file:preprocessor', require('../../../../use-babelrc'))
4+
return config
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '../../../../support'

examples/one-spec/index.html

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<body>
2+
Page body
3+
<script src="main-instrumented.js"></script>
4+
</body>

examples/one-spec/main-instrumented.js

+146
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/one-spec/main.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
window.add = (a, b) => a + b
2+
3+
window.sub = (a, b) => a - b

examples/one-spec/package.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "example-one-spec",
3+
"description": "Only running a single spec",
4+
"scripts": {
5+
"cy:open": "../../node_modules/.bin/cypress open"
6+
}
7+
}

support.js

+3-22
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/// <reference types="cypress" />
2+
// @ts-check
3+
4+
const { filterSpecsFromCoverage } = require('./utils')
25

36
/**
47
* Sends collected code coverage object to the backend code
@@ -53,28 +56,6 @@ const filterSupportFilesFromCoverage = totalCoverage => {
5356
return coverage
5457
}
5558

56-
/**
57-
* remove coverage for the spec files themselves,
58-
* only keep "external" application source file coverage
59-
*/
60-
const filterSpecsFromCoverage = totalCoverage => {
61-
const integrationFolder = Cypress.config('integrationFolder')
62-
const testFilePattern = Cypress.config('testFiles')
63-
const isUsingDefaultTestPattern = testFilePattern === '**/*.*'
64-
65-
const isInIntegrationFolder = filename =>
66-
filename.startsWith(integrationFolder)
67-
const isTestFile = filename => Cypress.minimatch(filename, testFilePattern)
68-
69-
const isA = (fileCoverge, filename) => isInIntegrationFolder(filename)
70-
const isB = (fileCoverge, filename) => isTestFile(filename)
71-
72-
const isTestFileFilter = isUsingDefaultTestPattern ? isA : isB
73-
74-
const coverage = Cypress._.omitBy(totalCoverage, isTestFileFilter)
75-
return coverage
76-
}
77-
7859
const registerHooks = () => {
7960
let windowCoverageObjects
8061

0 commit comments

Comments
 (0)