5
5
* Use of this source code is governed by an MIT-style license that can be
6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
+
9
+ // tslint:disable:no-implicit-dependencies
10
+
8
11
import { Architect } from '@angular-devkit/architect' ;
9
12
import { normalize , virtualFs } from '@angular-devkit/core' ;
13
+ import { last , tap } from 'rxjs/operators' ;
14
+ import { promisify } from 'util' ;
10
15
import { createArchitect , host , karmaTargetSpec } from '../test-utils' ;
11
16
17
+ // In each of the test below we'll have to call setTimeout to wait for the coverage
18
+ // analysis to be done. This is because karma-coverage performs the analysis
19
+ // asynchronously but the promise that it returns is not awaited by Karma.
20
+ // Coverage analysis begins when onRunComplete() is invoked, and output files
21
+ // are subsequently written to disk. For more information, see
22
+ // https://github.com/karma-runner/karma-coverage/blob/32acafa90ed621abd1df730edb44ae55a4009c2c/lib/reporter.js#L221
23
+
24
+ const setTimeoutPromise = promisify ( setTimeout ) ;
25
+
12
26
describe ( 'Karma Builder code coverage' , ( ) => {
13
27
const coverageFilePath = normalize ( 'coverage/lcov.info' ) ;
14
28
let architect : Architect ;
@@ -23,16 +37,19 @@ describe('Karma Builder code coverage', () => {
23
37
it ( 'supports code coverage option' , async ( ) => {
24
38
const run = await architect . scheduleTarget ( karmaTargetSpec , { codeCoverage : true } ) ;
25
39
26
- await expectAsync ( run . result ) . toBeResolvedTo ( jasmine . objectContaining ( { success : true } ) ) ;
40
+ const { success} = await run . result ;
41
+ expect ( success ) . toBe ( true ) ;
27
42
28
43
await run . stop ( ) ;
29
44
45
+ await setTimeoutPromise ( 1000 ) ;
46
+
30
47
const exists = host . scopedSync ( ) . exists ( coverageFilePath ) ;
31
- expect ( exists ) . toBe ( true ) ;
48
+ expect ( exists ) . toBe ( true , ` ${ coverageFilePath } does not exist` ) ;
32
49
33
50
if ( exists ) {
34
51
const content = virtualFs . fileBufferToString ( host . scopedSync ( ) . read ( coverageFilePath ) ) ;
35
- expect ( content ) . toContain ( 'polyfills .ts' ) ;
52
+ expect ( content ) . toContain ( 'app.component .ts' ) ;
36
53
expect ( content ) . toContain ( 'test.ts' ) ;
37
54
}
38
55
} , 120000 ) ;
@@ -41,23 +58,24 @@ describe('Karma Builder code coverage', () => {
41
58
const overrides = {
42
59
codeCoverage : true ,
43
60
codeCoverageExclude : [
44
- 'src/polyfills.ts' ,
45
61
'**/test.ts' ,
46
62
] ,
47
63
} ;
48
64
49
65
const run = await architect . scheduleTarget ( karmaTargetSpec , overrides ) ;
50
66
51
- await expectAsync ( run . result ) . toBeResolvedTo ( jasmine . objectContaining ( { success : true } ) ) ;
67
+ const { success} = await run . result ;
68
+ expect ( success ) . toBe ( true ) ;
52
69
53
70
await run . stop ( ) ;
54
71
72
+ await setTimeoutPromise ( 1000 ) ;
73
+
55
74
const exists = host . scopedSync ( ) . exists ( coverageFilePath ) ;
56
75
expect ( exists ) . toBe ( true ) ;
57
76
58
77
if ( exists ) {
59
78
const content = virtualFs . fileBufferToString ( host . scopedSync ( ) . read ( coverageFilePath ) ) ;
60
- expect ( content ) . not . toContain ( 'polyfills.ts' ) ;
61
79
expect ( content ) . not . toContain ( 'test.ts' ) ;
62
80
}
63
81
} , 120000 ) ;
@@ -98,10 +116,13 @@ describe('Karma Builder code coverage', () => {
98
116
99
117
const run = await architect . scheduleTarget ( karmaTargetSpec , { codeCoverage : true } ) ;
100
118
101
- await expectAsync ( run . result ) . toBeResolvedTo ( jasmine . objectContaining ( { success : true } ) ) ;
119
+ const { success} = await run . result ;
120
+ expect ( success ) . toBe ( true ) ;
102
121
103
122
await run . stop ( ) ;
104
123
124
+ await setTimeoutPromise ( 1000 ) ;
125
+
105
126
const exists = host . scopedSync ( ) . exists ( coverageFilePath ) ;
106
127
expect ( exists ) . toBe ( true ) ;
107
128
@@ -111,20 +132,18 @@ describe('Karma Builder code coverage', () => {
111
132
}
112
133
} , 120000 ) ;
113
134
114
- it ( `should fail when coverage is below threhold and 'emitWarning' is false` , async ( ) => {
115
- host . replaceInFile ( 'karma.conf.js' , 'fixWebpackSourcePaths: true' ,
116
- `
117
- fixWebpackSourcePaths: true,
118
- thresholds: {
119
- emitWarning: false,
120
- global: {
121
- statements: 100,
122
- lines: 100,
123
- branches: 100,
124
- functions: 100
135
+ it ( 'should exit with non-zero code when coverage is below threshold' , async ( ) => {
136
+ host . replaceInFile ( 'karma.conf.js' , 'coverageReporter: {' , `
137
+ coverageReporter: {
138
+ check: {
139
+ global: {
140
+ statements: 100,
141
+ lines: 100,
142
+ branches: 100,
143
+ functions: 100
144
+ }
125
145
},
126
- }` ,
127
- ) ;
146
+ ` ) ;
128
147
129
148
host . appendToFile ( 'src/app/app.component.ts' , `
130
149
export function nonCovered(): boolean {
@@ -133,7 +152,21 @@ describe('Karma Builder code coverage', () => {
133
152
` ) ;
134
153
135
154
const run = await architect . scheduleTarget ( karmaTargetSpec , { codeCoverage : true } ) ;
136
- await expectAsync ( run . result ) . toBeResolvedTo ( jasmine . objectContaining ( { success : false } ) ) ;
155
+
156
+ // In incremental mode, karma-coverage does not have the ability to mark a
157
+ // run as failed if code coverage does not pass. This is because it does
158
+ // the coverage asynchoronously and Karma does not await the promise
159
+ // returned by the plugin.
160
+ expect ( ( await run . result ) . success ) . toBeTrue ( ) ;
161
+
162
+ // However the program must exit with non-zero exit code.
163
+ // This is a more common use case of coverage testing and must be supported.
164
+ await run . output . pipe (
165
+ last ( ) ,
166
+ tap ( buildEvent => expect ( buildEvent . success ) . toBeFalse ( ) ) ,
167
+ ) . toPromise ( ) ;
168
+
137
169
await run . stop ( ) ;
170
+
138
171
} , 120000 ) ;
139
172
} ) ;
0 commit comments