Skip to content

Commit 6173609

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular-devkit/build-angular): transform remapped sourcemap into a plain object
`remapping` will return a SourceMap which it's prototype is not a simple object. This causes Babel to fail when it invoked from `istanbul-lib-instrument` because Babel with `don't know how to turn this value into a node` error as Babel will not process not simple objects. See: https://github.com/babel/babel/blob/780aa48d2a34dc55f556843074b6aed45e7eabeb/packages/babel-types/src/converters/valueToNode.ts#L115-L130 Closes #21967 (cherry picked from commit da1733c)
1 parent b93e63f commit 6173609

File tree

3 files changed

+99
-51
lines changed

3 files changed

+99
-51
lines changed

packages/angular_devkit/build_angular/src/babel/webpack-loader.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,16 @@ export default custom<AngularCustomOptions>(() => {
185185
// `@ampproject/remapping` source map objects but both are compatible with Webpack.
186186
// This method for merging is used because it provides more accurate output
187187
// and is faster while using less memory.
188-
result.map = remapping(
189-
[result.map as SourceMapInput, inputSourceMap as SourceMapInput],
190-
() => null,
191-
) as typeof result.map;
188+
result.map = {
189+
// Convert the SourceMap back to simple plain object.
190+
// This is needed because otherwise code-coverage will fail with `don't know how to turn this value into a node`
191+
// Which is throw by Babel when it is invoked again from `istanbul-lib-instrument`.
192+
// https://github.com/babel/babel/blob/780aa48d2a34dc55f556843074b6aed45e7eabeb/packages/babel-types/src/converters/valueToNode.ts#L115-L130
193+
...(remapping(
194+
[result.map as SourceMapInput, inputSourceMap as SourceMapInput],
195+
() => null,
196+
) as typeof result.map),
197+
};
192198
}
193199

194200
return result;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { last, tap } from 'rxjs/operators';
10+
import { promisify } from 'util';
11+
import { execute } from '../../index';
12+
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
13+
14+
// In each of the test below we'll have to call setTimeout to wait for the coverage
15+
// analysis to be done. This is because karma-coverage performs the analysis
16+
// asynchronously but the promise that it returns is not awaited by Karma.
17+
// Coverage analysis begins when onRunComplete() is invoked, and output files
18+
// are subsequently written to disk. For more information, see
19+
// https://github.com/karma-runner/karma-coverage/blob/32acafa90ed621abd1df730edb44ae55a4009c2c/lib/reporter.js#L221
20+
21+
const setTimeoutPromise = promisify(setTimeout);
22+
const coveragePath = 'coverage/lcov.info';
23+
24+
describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
25+
describe('Behavior: "codeCoverage"', () => {
26+
it('should generate coverage report when file was previously processed by Babel', async () => {
27+
// Force Babel transformation.
28+
await harness.appendToFile('src/app/app.component.ts', '// async');
29+
30+
harness.useTarget('test', {
31+
...BASE_OPTIONS,
32+
codeCoverage: true,
33+
});
34+
35+
const { result } = await harness.executeOnce();
36+
expect(result?.success).toBeTrue();
37+
38+
await setTimeoutPromise(1000);
39+
harness.expectFile(coveragePath).toExist();
40+
});
41+
42+
it('should exit with non-zero code when coverage is below threshold', async () => {
43+
await harness.modifyFile('karma.conf.js', (content) =>
44+
content.replace(
45+
'coverageReporter: {',
46+
`coverageReporter: {
47+
check: {
48+
global: {
49+
statements: 100,
50+
lines: 100,
51+
branches: 100,
52+
functions: 100
53+
}
54+
},
55+
`,
56+
),
57+
);
58+
59+
await harness.appendToFile(
60+
'src/app/app.component.ts',
61+
`
62+
export function nonCovered(): boolean {
63+
return true;
64+
}
65+
`,
66+
);
67+
68+
harness.useTarget('test', {
69+
...BASE_OPTIONS,
70+
codeCoverage: true,
71+
});
72+
73+
await harness
74+
.execute()
75+
.pipe(
76+
// In incremental mode, karma-coverage does not have the ability to mark a
77+
// run as failed if code coverage does not pass. This is because it does
78+
// the coverage asynchoronously and Karma does not await the promise
79+
// returned by the plugin.
80+
81+
// However the program must exit with non-zero exit code.
82+
// This is a more common use case of coverage testing and must be supported.
83+
last(),
84+
tap((buildEvent) => expect(buildEvent.result?.success).toBeFalse()),
85+
)
86+
.toPromise();
87+
});
88+
});
89+
});

packages/angular_devkit/build_angular/src/karma/tests/options/code-coverage_spec.ts

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -109,52 +109,5 @@ describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
109109
await setTimeoutPromise(1000);
110110
harness.expectFile(coveragePath).content.not.toContain('my-lib');
111111
});
112-
113-
it('should exit with non-zero code when coverage is below threshold', async () => {
114-
await harness.modifyFile('karma.conf.js', (content) =>
115-
content.replace(
116-
'coverageReporter: {',
117-
`coverageReporter: {
118-
check: {
119-
global: {
120-
statements: 100,
121-
lines: 100,
122-
branches: 100,
123-
functions: 100
124-
}
125-
},
126-
`,
127-
),
128-
);
129-
130-
await harness.appendToFile(
131-
'src/app/app.component.ts',
132-
`
133-
export function nonCovered(): boolean {
134-
return true;
135-
}
136-
`,
137-
);
138-
139-
harness.useTarget('test', {
140-
...BASE_OPTIONS,
141-
codeCoverage: true,
142-
});
143-
144-
await harness
145-
.execute()
146-
.pipe(
147-
// In incremental mode, karma-coverage does not have the ability to mark a
148-
// run as failed if code coverage does not pass. This is because it does
149-
// the coverage asynchoronously and Karma does not await the promise
150-
// returned by the plugin.
151-
152-
// However the program must exit with non-zero exit code.
153-
// This is a more common use case of coverage testing and must be supported.
154-
last(),
155-
tap((buildEvent) => expect(buildEvent.result?.success).toBeFalse()),
156-
)
157-
.toPromise();
158-
});
159112
});
160113
});

0 commit comments

Comments
 (0)