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
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"bazel-out",
"dist-schema",
"goldens/public-api",
"modules/testing/builder/projects",
"packages/angular_devkit/build_angular/src/babel-bazel.d.ts",
"packages/angular_devkit/build_angular/test",
"packages/angular_devkit/build_webpack/test",
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ jobs:
uses: angular/dev-infra/github-actions/bazel/configure-remote@f8b2efa171f4ebec4d0cb3d5c5d4f7cb680f2af9
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run tests
- name: Run module tests
run: yarn bazel test //modules/...
- name: Run package tests
run: yarn bazel test //packages/...

e2e:
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/docs/design/analytics.md
/dist-schema/
/goldens/public-api
/modules/testing/builder/projects/
/packages/angular_devkit/build_angular/test/
/packages/angular_devkit/core/src/workspace/json/test/
/packages/angular_devkit/schematics_cli/blank/project-files/
Expand Down
45 changes: 45 additions & 0 deletions modules/testing/builder/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
load("@npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
load("//tools:defaults.bzl", "ts_library")

package(default_visibility = ["//visibility:public"])

ts_library(
name = "builder",
testonly = True,
srcs = glob(
include = [
"src/**/*.ts",
],
exclude = [
"src/**/*_spec.ts",
],
),
data = glob(["projects/**/*"]),
deps = [
"//packages/angular_devkit/architect",
"//packages/angular_devkit/architect/node",
"//packages/angular_devkit/architect/testing",
"//packages/angular_devkit/core",
"//packages/angular_devkit/core/node",
"@npm//rxjs",
],
)

ts_library(
name = "unit_test_lib",
testonly = True,
srcs = glob(
include = [
"src/**/*_spec.ts",
],
),
deps = [
":builder",
"//packages/angular_devkit/architect/testing",
],
)

jasmine_node_test(
name = "unit_test",
srcs = [":unit_test_lib"],
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

/* eslint-disable import/no-extraneous-dependencies */

import {
BuilderContext,
BuilderHandlerFn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

/* eslint-disable import/no-extraneous-dependencies */

import { TestProjectHost } from '@angular-devkit/architect/testing';
import { BuilderHarness } from './builder-harness';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/

import {
BuilderWatcherCallback,
BuilderWatcherFactory,
} from '../tools/webpack/plugins/builder-watch-plugin';
type BuilderWatcherCallback = (
events: Array<{ path: string; type: 'created' | 'modified' | 'deleted'; time?: number }>,
) => void;

interface BuilderWatcherFactory {
watch(
files: Iterable<string>,
directories: Iterable<string>,
callback: BuilderWatcherCallback,
): { close(): void };
}

class WatcherDescriptor {
constructor(
Expand Down
15 changes: 15 additions & 0 deletions modules/testing/builder/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export {
BuilderHarness,
BuilderHarnessExecutionOptions,
BuilderHarnessExecutionResult,
} from './builder-harness';
export { HarnessFileMatchers, JasmineBuilderHarness, describeBuilder } from './jasmine-helpers';
export * from './test-utils';
176 changes: 176 additions & 0 deletions modules/testing/builder/src/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

/* eslint-disable import/no-extraneous-dependencies */

import { Architect, BuilderOutput, ScheduleOptions, Target } from '@angular-devkit/architect';
import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
import { TestProjectHost, TestingArchitectHost } from '@angular-devkit/architect/testing';
import {
Path,
getSystemPath,
join,
json,
normalize,
schema,
virtualFs,
workspaces,
} from '@angular-devkit/core';
import { firstValueFrom } from 'rxjs';

// Default timeout for large specs is 2.5 minutes.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 150000;

export const workspaceRoot = join(normalize(__dirname), `../projects/hello-world-app/`);
export const host = new TestProjectHost(workspaceRoot);
export const outputPath: Path = normalize('dist');

export const browserTargetSpec = { project: 'app', target: 'build' };
export const devServerTargetSpec = { project: 'app', target: 'serve' };
export const extractI18nTargetSpec = { project: 'app', target: 'extract-i18n' };
export const karmaTargetSpec = { project: 'app', target: 'test' };
export const tslintTargetSpec = { project: 'app', target: 'lint' };
export const protractorTargetSpec = { project: 'app-e2e', target: 'e2e' };

export async function createArchitect(workspaceRoot: Path) {
const registry = new schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
const workspaceSysPath = getSystemPath(workspaceRoot);

const { workspace } = await workspaces.readWorkspace(
workspaceSysPath,
workspaces.createWorkspaceHost(host),
);
const architectHost = new TestingArchitectHost(
workspaceSysPath,
workspaceSysPath,
new WorkspaceNodeModulesArchitectHost(workspace, workspaceSysPath),
);
const architect = new Architect(architectHost, registry);

return {
workspace,
architectHost,
architect,
};
}

export interface BrowserBuildOutput {
output: BuilderOutput;
files: { [file: string]: Promise<string> };
}

export async function browserBuild(
architect: Architect,
host: virtualFs.Host,
target: Target,
overrides?: json.JsonObject,
scheduleOptions?: ScheduleOptions,
): Promise<BrowserBuildOutput> {
const run = await architect.scheduleTarget(target, overrides, scheduleOptions);
const output = (await run.result) as BuilderOutput & { outputs: { path: string }[] };
expect(output.success).toBe(true);

if (!output.success) {
await run.stop();

return {
output,
files: {},
};
}

const [{ path }] = output.outputs;
expect(path).toBeTruthy();
const outputPath = normalize(path);

const fileNames = await firstValueFrom(host.list(outputPath));
const files = fileNames.reduce((acc: { [name: string]: Promise<string> }, path) => {
let cache: Promise<string> | null = null;
Object.defineProperty(acc, path, {
enumerable: true,
get() {
if (cache) {
return cache;
}
if (!fileNames.includes(path)) {
return Promise.reject('No file named ' + path);
}
cache = firstValueFrom(host.read(join(outputPath, path))).then((content) =>
virtualFs.fileBufferToString(content),
);

return cache;
},
});

return acc;
}, {});

await run.stop();

return {
output,
files,
};
}

export const lazyModuleFiles: { [path: string]: string } = {
'src/app/lazy/lazy-routing.module.ts': `
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LazyRoutingModule { }
`,
'src/app/lazy/lazy.module.ts': `
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { LazyRoutingModule } from './lazy-routing.module';

@NgModule({
imports: [
CommonModule,
LazyRoutingModule
],
declarations: []
})
export class LazyModule { }
`,
};

export const lazyModuleFnImport: { [path: string]: string } = {
'src/app/app.module.ts': `
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { RouterModule } from '@angular/router';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
RouterModule.forRoot([
{ path: 'lazy', loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) }
])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
`,
};
5 changes: 3 additions & 2 deletions packages/angular_devkit/build_angular/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
# Use of this source code is governed by an MIT-style license that can be
# found in the LICENSE file at https://angular.io/license

load("@npm//@angular/build-tooling/bazel/api-golden:index.bzl", "api_golden_test_npm_package")
load("@npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
load("//tools:defaults.bzl", "pkg_npm", "ts_library")
load("//tools:ts_json_schema.bzl", "ts_json_schema")
load("@npm//@angular/build-tooling/bazel/api-golden:index.bzl", "api_golden_test_npm_package")

licenses(["notice"])

Expand Down Expand Up @@ -285,7 +285,6 @@ ts_library(
testonly = True,
srcs = glob(
include = [
"src/test-utils.ts",
"src/testing/**/*.ts",
"src/**/tests/*.ts",
],
Expand All @@ -297,6 +296,7 @@ ts_library(
tsconfig = "//:tsconfig-test.json",
deps = [
":build_angular",
"//modules/testing/builder",
"//packages/angular_devkit/architect",
"//packages/angular_devkit/architect/node",
"//packages/angular_devkit/architect/testing",
Expand Down Expand Up @@ -394,6 +394,7 @@ LARGE_SPECS = {
# Dependencies needed to compile and run the specs themselves.
":build_angular",
":build_angular_test_utils",
"//modules/testing/builder",
"//packages/angular_devkit/architect",
"//packages/angular_devkit/architect/node",
"//packages/angular_devkit/architect/testing",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';

const MAIN_OUTPUT = 'dist/main.js';
const NAMED_LAZY_OUTPUT = 'dist/src_lazy-module_ts.js';
const UNNAMED_LAZY_OUTPUT = 'dist/28.js';
const UNNAMED_LAZY_OUTPUT = 'dist/358.js';

describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
describe('Option: "namedChunks"', () => {
Expand Down Expand Up @@ -61,7 +61,7 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
const { result } = await harness.executeOnce();

expect(result?.success).toBe(true);

debugger;
harness.expectFile(MAIN_OUTPUT).toExist();
harness.expectFile(NAMED_LAZY_OUTPUT).toNotExist();
harness.expectFile(UNNAMED_LAZY_OUTPUT).toExist();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
BuilderHarness,
BuilderHarnessExecutionOptions,
BuilderHarnessExecutionResult,
} from '../../../testing/builder-harness';
} from '../../../testing';

export async function executeOnceAndFetch<T>(
harness: BuilderHarness<T>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { BuilderHandlerFn } from '@angular-devkit/architect';
import { json } from '@angular-devkit/core';
import { readFileSync } from 'fs';
import { JasmineBuilderHarness } from '../../../testing/jasmine-helpers';
import { JasmineBuilderHarness } from '../../../testing';
import { host } from '../../../testing/test-utils';
import { setupApplicationTarget, setupBrowserTarget } from './setup';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { createServer } from 'node:http';
import { JasmineBuilderHarness } from '../../../../testing/jasmine-helpers';
import { BuilderHarness } from '../../../../testing';
import { executeDevServer } from '../../index';
import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
Expand Down Expand Up @@ -275,7 +275,7 @@ async function createProxyServer() {
/**
* Vite specific tests
*/
function viteOnlyTests(harness: JasmineBuilderHarness<unknown>): void {
function viteOnlyTests(harness: BuilderHarness<unknown>): void {
it('proxies support regexp as context', async () => {
harness.useTarget('serve', {
...BASE_OPTIONS,
Expand Down
Loading