Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/angular_devkit/build_angular/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ LARGE_SPECS = {
},
"extract-i18n": {},
"karma": {
"shards": 3,
"shards": 6,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theory: We're doubling the number of tests, so we can (and should) double the number of shards.

"size": "large",
"flaky": True,
"extra_deps": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { setTimeout } from 'node:timers/promises';
import { tags } from '@angular-devkit/core';
import { last, tap } from 'rxjs';
import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

// In each of the test below we'll have to call setTimeout to wait for the coverage
// analysis to be done. This is because karma-coverage performs the analysis
Expand All @@ -21,8 +21,12 @@ import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';

const coveragePath = 'coverage/lcov.info';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
describe('Behavior: "codeCoverage"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('should generate coverage report when file was previously processed by Babel', async () => {
// Force Babel transformation.
await harness.appendToFile('src/app/app.component.ts', '// async');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Behavior: "Errors"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('should fail when there is a TypeScript error', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Behavior: "module commonjs"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('should work when module is commonjs', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@

import { concatMap, count, debounceTime, take, timeout } from 'rxjs';
import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Behavior: "Rebuilds"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('recovers from compilation failures in watch mode', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "assets"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('includes assets', async () => {
await harness.writeFiles({
'./src/string-file-asset.txt': 'string-file-asset.txt',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import { setTimeout } from 'node:timers/promises';
import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

// In each of the test below we'll have to call setTimeout to wait for the coverage
// analysis to be done. This is because karma-coverage performs the analysis
Expand All @@ -18,8 +18,12 @@ import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';

const coveragePath = 'coverage/lcov.info';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "codeCoverageExclude"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('should exclude file from coverage when set', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { setTimeout } from 'node:timers/promises';
import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

// In each of the test below we'll have to call setTimeout to wait for the coverage
// analysis to be done. This is because karma-coverage performs the analysis
Expand All @@ -19,8 +19,12 @@ import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';

const coveragePath = 'coverage/lcov.info';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "codeCoverage"', () => {
beforeEach(() => {
setupTarget(harness);
});

it('should generate coverage report when option is set to true', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "exclude"', () => {
beforeEach(() => {
setupTarget(harness);
});

beforeEach(async () => {
await harness.writeFiles({
'src/app/error.spec.ts': `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "include"', () => {
beforeEach(() => {
setupTarget(harness);
});

it(`should fail when includes doesn't match any files`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "styles"', () => {
beforeEach(() => {
setupTarget(harness);
});

it(`processes 'styles.css' styles`, async () => {
await harness.writeFiles({
'src/styles.css': 'p {display: none}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
*/

import { execute } from '../../index';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup';
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';

describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => {
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
describe('Option: "webWorkerTsConfig"', () => {
beforeEach(() => {
setupTarget(harness);
});

beforeEach(async () => {
await harness.writeFiles({
'src/tsconfig.worker.json': `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
*/

import { Schema } from '../schema';
import { BuilderHandlerFn } from '@angular-devkit/architect';
import { json } from '@angular-devkit/core';
import { ApplicationBuilderOptions as ApplicationSchema, buildApplication } from '@angular/build';
import * as path from 'node:path';
import { readFileSync } from 'node:fs';

import { JasmineBuilderHarness } from '../../../testing';
import { host } from '../../../testing/test-utils';
import { BuilderHarness } from '../../../testing';
import { buildWebpackBrowser } from '../../browser';
import { Schema as BrowserSchema } from '../../browser/schema';
import {
BASE_OPTIONS as BROWSER_BASE_OPTIONS,
BROWSER_BUILDER_INFO,
} from '../../browser/tests/setup';

export { describeBuilder } from '../../../testing';

Expand All @@ -27,3 +42,133 @@ export const BASE_OPTIONS = Object.freeze<Schema>({
progress: false,
watch: false,
});

const optionSchemaCache = new Map<string, json.schema.JsonSchema>();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is similar to the dev-server code and could likely be refactored to be shared across builders: https://github.com/angular/angular-cli/blob/main/packages/angular_devkit/build_angular/src/builders/dev-server/tests/jasmine-helpers.ts#L18-L25

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to improve the test harness in general so that refactor could fit in there. Probably a good item for the next fixit.


function getCachedSchema(options: { schemaPath: string }): json.schema.JsonSchema {
let optionSchema = optionSchemaCache.get(options.schemaPath);
if (optionSchema === undefined) {
optionSchema = JSON.parse(readFileSync(options.schemaPath, 'utf8')) as json.schema.JsonSchema;
optionSchemaCache.set(options.schemaPath, optionSchema);
}
return optionSchema;
}

/**
* Adds a `build` target to a builder test harness for the browser builder with the base options
* used by the browser builder tests.
*
* @param harness The builder harness to use when setting up the browser builder target
* @param extraOptions The additional options that should be used when executing the target.
*/
export function setupBrowserTarget<T>(
harness: BuilderHarness<T>,
extraOptions?: Partial<BrowserSchema>,
): void {
const browserSchema = getCachedSchema(BROWSER_BUILDER_INFO);

harness.withBuilderTarget(
'build',
buildWebpackBrowser,
{
...BROWSER_BASE_OPTIONS,
...extraOptions,
},
{
builderName: BROWSER_BUILDER_INFO.name,
optionSchema: browserSchema,
},
);
}

/**
* Contains all required application builder fields.
* Also disables progress reporting to minimize logging output.
*/
export const APPLICATION_BASE_OPTIONS = Object.freeze<ApplicationSchema>({
index: 'src/index.html',
browser: 'src/main.ts',
outputPath: 'dist',
tsConfig: 'src/tsconfig.app.json',
progress: false,

// Disable optimizations
optimization: false,

// Enable polling (if a test enables watch mode).
// This is a workaround for bazel isolation file watch not triggering in tests.
poll: 100,
});

// TODO: Remove and use import after Vite-based dev server is moved to new package
export const APPLICATION_BUILDER_INFO = Object.freeze({
name: '@angular-devkit/build-angular:application',
schemaPath: path.join(
path.dirname(require.resolve('@angular/build/package.json')),
'src/builders/application/schema.json',
),
});

/**
* Adds a `build` target to a builder test harness for the application builder with the base options
* used by the application builder tests.
*
* @param harness The builder harness to use when setting up the application builder target
* @param extraOptions The additional options that should be used when executing the target.
*/
export function setupApplicationTarget<T>(
harness: BuilderHarness<T>,
extraOptions?: Partial<ApplicationSchema>,
): void {
const applicationSchema = getCachedSchema(APPLICATION_BUILDER_INFO);

harness.withBuilderTarget(
'build',
buildApplication,
{
...APPLICATION_BASE_OPTIONS,
...extraOptions,
},
{
builderName: APPLICATION_BUILDER_INFO.name,
optionSchema: applicationSchema,
},
);
}

/** Runs the test against both an application- and a browser-builder context. */
export function describeKarmaBuilder<T>(
builderHandler: BuilderHandlerFn<T & json.JsonObject>,
options: { name?: string; schemaPath: string },
specDefinitions: ((
harness: JasmineBuilderHarness<T>,
setupTarget: typeof setupApplicationTarget,
isApplicationTarget: true,
) => void) &
((
harness: JasmineBuilderHarness<T>,
setupTarget: typeof setupBrowserTarget,
isApplicationTarget: false,
) => void),
) {
const optionSchema = getCachedSchema(options);
const harness = new JasmineBuilderHarness<T>(builderHandler, host, {
builderName: options.name,
optionSchema,
});

describe(options.name || builderHandler.name, () => {
for (const isApplicationTarget of [true, false]) {
describe(isApplicationTarget ? 'with application builder' : 'with browser builder', () => {
beforeEach(() => host.initialize().toPromise());
afterEach(() => host.restore().toPromise());

if (isApplicationTarget) {
specDefinitions(harness, setupApplicationTarget, true);
} else {
specDefinitions(harness, setupBrowserTarget, false);
}
});
}
});
}