diff --git a/.circleci/config.yml b/.circleci/config.yml index f6dcc84bec80..8986ca1c02df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -609,6 +609,14 @@ jobs: command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials' - run: ./scripts/ci/publish-build-artifacts.sh + aio_misc: + executor: default-executor + steps: + - custom_attach_workspace + - run: + name: Check website provided in contributors.json file + command: yarn node aio/scripts/test-external-urls.js + aio_monitoring_stable: executor: default-executor steps: @@ -879,6 +887,9 @@ workflows: monitoring: jobs: - setup + - aio_misc: + requires: + - setup - aio_monitoring_stable: requires: - setup diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml index db7344bce146..9b3e3a1c2695 100644 --- a/.github/workflows/feature-requests.yml +++ b/.github/workflows/feature-requests.yml @@ -1,13 +1,15 @@ name: Feature request triage bot -on: [workflow_dispatch] +on: + schedule: + # Run at 14:00 every day + - cron: '0 14 * * *' jobs: feature_triage: if: github.repository == 'angular/angular' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/feature-request@698f598d8a5ebc6917989512e8a3defc830ac06f + - uses: angular/dev-infra/github-actions/feature-request@a3fb5e24b659411f1163461a778a1fa79e119d2f with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} - limit: 50 diff --git a/CHANGELOG.md b/CHANGELOG.md index 48879b98a966..65ed0017aedb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ + +# 12.0.2 (2021-05-26) +### forms +| Commit | Description | +| -- | -- | +| 19d7bf4162 | fix(forms): Add float number support for min and max validator (#42223) | +### migrations +| Commit | Description | +| -- | -- | +| 11c7bec065 | fix(migrations): add migration to replace `/deep/` with `::ng-deep` (#42214) | +### platform-browser +| Commit | Description | +| -- | -- | +| 84ab81c286 | fix(platform-browser): update started state on reset (#41608) | +## Special Thanks: +Alan Agius, Andrew Scott, David Shevitz, George Kalpakas, Igor Minar, Joey Perrott, Kapunahele Wong, Madleina Scheidegger, Paul Gschwendtner, Pete Bacon Darwin, Sam Severance, Teri Glover, Zach Arend, chenyunhsin, iRealNirmal, mgechev and twerske + + +# 12.1.0-next.3 (2021-05-26) +### forms +| Commit | Description | +| -- | -- | +| 3d9062dad7 | fix(forms): Add float number support for min and max validator (#42223) | +### migrations +| Commit | Description | +| -- | -- | +| 7f6213a2f4 | fix(migrations): add migration to replace `/deep/` with `::ng-deep` (#42214) | +### platform-browser +| Commit | Description | +| -- | -- | +| 3a6af8e629 | fix(platform-browser): update started state on reset (#41608) | +## Special Thanks: +Alan Agius, Andrew Scott, David Shevitz, George Kalpakas, Igor Minar, Joey Perrott, Kapunahele Wong, Madleina Scheidegger, Paul Gschwendtner, Pete Bacon Darwin, Renovate Bot, Sam Severance, Teri Glover, Zach Arend, chenyunhsin, iRealNirmal, mgechev and twerske + + # 12.1.0-next.2 (2021-05-19) ### common diff --git a/aio/content/examples/animations/src/app/animations.1.ts b/aio/content/examples/animations/src/app/animations.1.ts index ae7558f53d1c..507d4d7c8975 100644 --- a/aio/content/examples/animations/src/app/animations.1.ts +++ b/aio/content/examples/animations/src/app/animations.1.ts @@ -1,7 +1,9 @@ -// #docregion -import { animation, style, animate } from '@angular/animations'; +// #docplaster +// #docregion animation-const, trigger-const +import { animation, style, animate, trigger, transition, useAnimation } from '@angular/animations'; +// #enddocregion trigger-const -export const transAnimation = animation([ +export const transitionAnimation = animation([ style({ height: '{{ height }}', opacity: '{{ opacity }}', @@ -9,3 +11,19 @@ export const transAnimation = animation([ }), animate('{{ time }}') ]); +// #enddocregion animation-const + +// #docregion trigger-const +export const triggerAnimation = trigger('openClose', [ + transition('open => closed', [ + useAnimation(transitionAnimation, { + params: { + height: 0, + opacity: 1, + backgroundColor: 'red', + time: '1s' + } + }) + ]) +]); +// #enddocregion trigger-const diff --git a/aio/content/examples/animations/src/app/animations.ts b/aio/content/examples/animations/src/app/animations.ts index 8b6abd5ec76f..040985da9c67 100644 --- a/aio/content/examples/animations/src/app/animations.ts +++ b/aio/content/examples/animations/src/app/animations.ts @@ -1,4 +1,3 @@ -// #docregion reusable import { animation, trigger, animateChild, group, transition, animate, style, query @@ -12,7 +11,6 @@ export const transAnimation = animation([ }), animate('{{ time }}') ]); -// #enddocregion reusable // Routable animations // #docregion route-animations diff --git a/aio/content/examples/elements/src/app/app.module.ts b/aio/content/examples/elements/src/app/app.module.ts index f273e77b14fb..a56491dc2560 100644 --- a/aio/content/examples/elements/src/app/app.module.ts +++ b/aio/content/examples/elements/src/app/app.module.ts @@ -6,16 +6,11 @@ import { AppComponent } from './app.component'; import { PopupComponent } from './popup.component'; import { PopupService } from './popup.service'; -// Include the `PopupService` provider, -// but exclude `PopupComponent` from compilation, -// because it will be added dynamically. - @NgModule({ imports: [BrowserModule, BrowserAnimationsModule], providers: [PopupService], declarations: [AppComponent, PopupComponent], bootstrap: [AppComponent], - entryComponents: [PopupComponent], }) export class AppModule { } diff --git a/aio/content/examples/forms/src/app/app.module.ts b/aio/content/examples/forms/src/app/app.module.ts index f5482927a94d..1a94fe3e3101 100644 --- a/aio/content/examples/forms/src/app/app.module.ts +++ b/aio/content/examples/forms/src/app/app.module.ts @@ -1,6 +1,7 @@ // #docregion import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; @@ -9,6 +10,7 @@ import { HeroFormComponent } from './hero-form/hero-form.component'; @NgModule({ imports: [ BrowserModule, + CommonModule, FormsModule ], declarations: [ diff --git a/aio/content/examples/forms/src/app/hero-form/hero-form.component.html b/aio/content/examples/forms/src/app/hero-form/hero-form.component.html index d05b100d9b68..44bbb7c52c8f 100644 --- a/aio/content/examples/forms/src/app/hero-form/hero-form.component.html +++ b/aio/content/examples/forms/src/app/hero-form/hero-form.component.html @@ -147,7 +147,7 @@

Hero Form

- {{diagnostic}} + {{ model | json }}
{ let fixture: ComponentFixture; let h1: HTMLElement; + describe('setup that may fail', () => { + // #docregion setup-may-fail + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BannerComponent ], + }); // missing call to compileComponents() + fixture = TestBed.createComponent(BannerComponent); + }); + // #enddocregion setup-may-fail + + it('should create', () => { + expect(fixture.componentInstance).toBeDefined(); + }); + }); + describe('Two beforeEach', () => { // #docregion async-before-each beforeEach(async () => { - TestBed - .configureTestingModule({ - declarations: [BannerComponent], - }) - .compileComponents(); // compile template and css + await TestBed.configureTestingModule({ + declarations: [ BannerComponent ], + }).compileComponents(); // compile template and css }); // #enddocregion async-before-each @@ -37,7 +50,7 @@ describe('BannerComponent (external files)', () => { // #docregion one-before-each beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [BannerComponent], + declarations: [ BannerComponent ], }).compileComponents(); fixture = TestBed.createComponent(BannerComponent); component = fixture.componentInstance; diff --git a/aio/content/examples/testing/src/app/banner/banner.component.spec.ts b/aio/content/examples/testing/src/app/banner/banner.component.spec.ts index 0b79132224ea..a14fe656a994 100644 --- a/aio/content/examples/testing/src/app/banner/banner.component.spec.ts +++ b/aio/content/examples/testing/src/app/banner/banner.component.spec.ts @@ -11,18 +11,15 @@ describe('BannerComponent (inline template)', () => { let fixture: ComponentFixture; let h1: HTMLElement; - // #docregion configure-and-create - beforeEach(async () => { - await TestBed.configureTestingModule({ + beforeEach(() => { + TestBed.configureTestingModule({ declarations: [ BannerComponent ], }); fixture = TestBed.createComponent(BannerComponent); - // #enddocregion configure-and-create component = fixture.componentInstance; // BannerComponent test instance h1 = fixture.nativeElement.querySelector('h1'); - // #docregion configure-and-create }); - // #enddocregion setup, configure-and-create + // #enddocregion setup // #docregion test-w-o-detect-changes it('no title in the DOM after createComponent()', () => { diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts index ca8260f1d60f..086936986d49 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.spec.ts @@ -193,9 +193,9 @@ function heroModuleSetup() { // #docregion title-case-pipe it('should convert hero name to Title Case', () => { // get the name's input and display elements from the DOM - const hostElement = fixture.nativeElement; - const nameInput: HTMLInputElement = hostElement.querySelector('input'); - const nameDisplay: HTMLElement = hostElement.querySelector('span'); + const hostElement: HTMLElement = fixture.nativeElement; + const nameInput: HTMLInputElement = hostElement.querySelector('input')!; + const nameDisplay: HTMLElement = hostElement.querySelector('span')!; // simulate user entering a new name into the input box nameInput.value = 'quick BROWN fOx'; diff --git a/aio/content/examples/testing/src/app/hero/hero-detail.component.ts b/aio/content/examples/testing/src/app/hero/hero-detail.component.ts index deee62725e1f..75443e0355ee 100644 --- a/aio/content/examples/testing/src/app/hero/hero-detail.component.ts +++ b/aio/content/examples/testing/src/app/hero/hero-detail.component.ts @@ -1,6 +1,6 @@ /* tslint:disable:member-ordering */ // #docplaster -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Hero } from '../model/hero'; @@ -23,7 +23,7 @@ export class HeroDetailComponent implements OnInit { // #enddocregion ctor // #enddocregion prototype - @Input() hero!: Hero; + hero!: Hero; // #docregion ng-on-init ngOnInit(): void { diff --git a/aio/content/guide/angular-compiler-options.md b/aio/content/guide/angular-compiler-options.md index 76b79b939c7b..ecdeacc0c843 100644 --- a/aio/content/guide/angular-compiler-options.md +++ b/aio/content/guide/angular-compiler-options.md @@ -221,3 +221,14 @@ When you use the CLI command `ng new --strict`, it is set to `true` in the gener ### `trace` When `true`, prints extra information while compiling templates. Default is `false`. + + +{@a cli-options} +## Command Line Options + +While most of the time you interact with the Angular Compiler indirectly using Angular CLI, when debugging certain issues, you might find it useful to invoke the Angular Compiler directly. +You can use the `ngc` command provided by the `@angular/compiler-cli` npm package to call the compiler from the command line. + +The `ngc` command is just a wrapper around TypeScript's `tsc` compiler command and is primarily configured via the `tsconfig.json` configuration options documented in [the previous sections](#angular-compiler-options). + +In addition to the configuration file, you can also use [`tsc` command line options](https://www.typescriptlang.org/docs/handbook/compiler-options.html) to configure `ngc`. diff --git a/aio/content/guide/browser-support.md b/aio/content/guide/browser-support.md index 73ac6ea6813d..a83bf2d46b70 100644 --- a/aio/content/guide/browser-support.md +++ b/aio/content/guide/browser-support.md @@ -49,6 +49,16 @@ using [Sauce Labs](https://saucelabs.com/) and
+ +{@a ie11} +## Configuring Angular CLI for compatibility with IE11 + +While Angular supports all browsers listed above, in order to improve the build times and output, Angular CLI applications don't support IE11 by default. + +Angular CLI uses [`browserlist`](https://github.com/browserslist/browserslist) to configure browser support for applications. + +You can enable the IE11 support by following the instructions in the `.browserslistrc` file at the root of your project. + ## Polyfills Angular is built on the latest standards of the web platform. diff --git a/aio/content/guide/build.md b/aio/content/guide/build.md index 7dec0019b1e0..86809fbefa57 100644 --- a/aio/content/guide/build.md +++ b/aio/content/guide/build.md @@ -291,63 +291,6 @@ To disable these warnings, you can add the CommonJS module name to `allowedCommo }, -{@a browser-compat} - -## Configuring browser compatibility - -The CLI uses [Autoprefixer](https://github.com/postcss/autoprefixer) to ensure compatibility with different browser and browser versions. -You may find it necessary to target specific browsers or exclude certain browser versions from your build. - -Internally, Autoprefixer relies on a library called [Browserslist](https://github.com/browserslist/browserslist) to figure out which browsers to support with prefixing. -Browserlist looks for configuration options in a `browserslist` property of the package configuration file, or in a configuration file named `.browserslistrc`. -Autoprefixer looks for the `browserslist` configuration when it prefixes your CSS. - -* You can tell Autoprefixer what browsers to target by adding a browserslist property to the package configuration file, `package.json`: -``` - "browserslist": [ - "> 1%", - "last 2 versions" - ] -``` - -* Alternatively, you can add a new file, `.browserslistrc`, to the project directory, that specifies browsers you want to support: -``` - ### Supported Browsers - > 1% - last 2 versions -``` - -See the [browserslist repo](https://github.com/browserslist/browserslist) for more examples of how to target specific browsers and versions. - -### Backward compatibility with Lighthouse - -If you want to produce a progressive web app and are using [Lighthouse](https://developers.google.com/web/tools/lighthouse/) to grade the project, add the following `browserslist` entry to your `package.json` file, in order to eliminate the [old flexbox](https://developers.google.com/web/tools/lighthouse/audits/old-flexbox) prefixes: - -``` -"browserslist": [ - "last 2 versions", - "not ie <= 10", - "not ie_mob <= 10" -] -``` - -### Backward compatibility with CSS grid - -CSS grid layout support in Autoprefixer, which was previously on by default, is off by default in Angular 8 and higher. - -To use CSS grid with IE10/11, you must explicitly enable it using the `autoplace` option. -To do this, add the following to the top of the global styles file (or within a specific css selector scope): - -``` -/* autoprefixer grid: autoplace */ -``` -or -``` -/* autoprefixer grid: no-autoplace */ -``` - -For more information, see [Autoprefixer documentation](https://autoprefixer.github.io/). - {@a proxy} @@ -534,3 +477,9 @@ function setupForCorporateProxy(proxyConfig) { module.exports = setupForCorporateProxy(proxyConfig); ``` + +{@a browser-compat} + +## Configuring browser compatibility + +See [browser support guide](guide/browser-support). diff --git a/aio/content/guide/cli-builder.md b/aio/content/guide/cli-builder.md index acfe855731f3..346b7150cdda 100644 --- a/aio/content/guide/cli-builder.md +++ b/aio/content/guide/cli-builder.md @@ -119,7 +119,7 @@ In either case, you must provide required inputs, but can allow other inputs to You define builder inputs in a JSON schema associated with that builder. The Architect tool collects the resolved input values into an `options` object, and validates their types against the schema before passing them to the builder function. -(The Schematics library does the same kind of validation of user input). +(The Schematics library does the same kind of validation of user input.) For our example builder, we expect the `options` value to be a `JsonObject` with two keys: a `command` that is a string, and an `args` array of string values. @@ -153,7 +153,7 @@ For more information, see the [JSON schemas website](http://json-schema.org/). To link our builder implementation with its schema and name, we need to create a *builder definition* file, which we can point to in `package.json`. -Create a file named `builders.json` file that looks like this. +Create a file named `builders.json` that looks like this: @@ -204,7 +204,7 @@ Targets are defined in the `angular.json` [CLI configuration file](guide/workspa A target specifies the builder to use, its default options configuration, and named alternative configurations. The Architect tool uses the target definition to resolve input options for a given run. -The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'. +The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'. By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`. @@ -276,7 +276,7 @@ For more information see [Workspace Configuration](guide/workspace-config). You can also invoke a builder directly from another builder or test by calling `BuilderContext.scheduleBuilder()`. You pass an `options` object directly to the method, and those option values are validated against the schema of the builder without further adjustment. - Only the `BuilderContext.scheduleTarget()` method resolves the configuration and overrides through the `angular.json` file. + Only the `BuilderContext.scheduleTarget()` method resolves the configuration and overrides through the `angular.json` file. @@ -340,7 +340,7 @@ We need to update the `angular.json` file to add a target for this builder to th * We'll add a new target section to the "architect" object for our project. -* The target named "touch" uses our builder, which we published to `@example/command-runner`. (See [Publishing your Library](guide/creating-libraries#publishing-your-library)) +* The target named "touch" uses our builder, which we published to `@example/command-runner`. (See [Publishing your Library](guide/creating-libraries#publishing-your-library).) * The options object provides default values for the two inputs that we defined; `command`, which is the Unix command to execute, and `args`, an array that contains the file to operate on. diff --git a/aio/content/guide/deployment.md b/aio/content/guide/deployment.md index 2e61fc23161d..8c3f2c9b40d2 100644 --- a/aio/content/guide/deployment.md +++ b/aio/content/guide/deployment.md @@ -451,6 +451,22 @@ the subfolder is `my/app/` and you should add `` to the se When the `base` tag is mis-configured, the application fails to load and the browser console displays `404 - Not Found` errors for the missing files. Look at where it _tried_ to find those files and adjust the base tag appropriately. +{@a deploy-url} + +## The `deploy` url + +A command line option used to specify the base path for resolving relative URLs for assets such as images, scripts, and style sheets at _compile_ time. For example: `ng build --deploy-url /my/assets`. + +The effects of defining a `deploy url` and `base href` can overlap. +* Both can be used for initial scripts, stylesheets, lazy scripts, and css resources. + +However, defining a `base href` has a few unique effects. +* Defining a `base href` can be used for locating relative template (HTML) assets, and relative fetch/XMLHttpRequests. + +The `base href` can also be used to define the Angular router's default base (see [APP_BASE_HREF](https://angular.io/api/common/APP_BASE_HREF)). Users with more complicated setups may need to manually configure the `APP_BASE_HREF` token within the application. (e.g., application routing base is / but assets/scripts/etc. are at /assets/). + +Unlike the `base href` which can be defined in a single place, the `deploy url` needs to be hard-coded into an application at build time. This means specifying a `deploy url` will decrease build speed, but this is the unfortunate cost of using an option that embeds itself throughout an application. That is why a `base href` is generally the better option. + {@a differential-loading} ## Differential Loading diff --git a/aio/content/guide/elements.md b/aio/content/guide/elements.md index d495e294693a..70c55a5c6916 100644 --- a/aio/content/guide/elements.md +++ b/aio/content/guide/elements.md @@ -147,9 +147,9 @@ Using an Angular custom element makes the process much simpler and more transpar The Popup Service example application (shown below) defines a component that you can either load dynamically or convert to a custom element. - `popup.component.ts` defines a simple pop-up element that displays an input message, with some animation and styling. -- `popup.service.ts` creates an injectable service that provides two different ways to invoke the PopupComponent; as a dynamic component, or as a custom element. Notice how much more setup is required for the dynamic-loading method. -- `app.module.ts` adds the PopupComponent in the module's `entryComponents` list, to exclude it from compilation and avoid startup warnings or errors. -- `app.component.ts` defines the application's root component, which uses the PopupService to add the pop-up to the DOM at run time. When the application runs, the root component's constructor converts PopupComponent to a custom element. +- `popup.service.ts` creates an injectable service that provides two different ways to invoke the `PopupComponent`; as a dynamic component, or as a custom element. Notice how much more setup is required for the dynamic-loading method. +- `app.module.ts` adds the `PopupComponent` in the module's `declarations` list. +- `app.component.ts` defines the application's root component, which uses the `PopupService` to add the pop-up to the DOM at run time. When the application runs, the root component's constructor converts `PopupComponent` to a custom element. For comparison, the demo shows both methods. One button adds the popup using the dynamic-loading method, and the other uses the custom element. You can see that the result is the same; only the preparation is different. diff --git a/aio/content/guide/event-binding.md b/aio/content/guide/event-binding.md index 2d2c91fb3c41..b292d904bfbe 100644 --- a/aio/content/guide/event-binding.md +++ b/aio/content/guide/event-binding.md @@ -24,6 +24,26 @@ The event binding listens for the button's click events and calls the component' Syntax diagram +## Binding to passive events + +Angular also supports passive event listeners. For example, you can use the following steps to make a scroll event passive. + +1. Create a file `zone-flags.ts` under `src` directory. +2. Add the following line into this file. + +``` +(window as any)['__zone_symbol__PASSIVE_EVENTS'] = ['scroll']; +``` + +3. In the `src/polyfills.ts` file, before importing zone.js, import the newly created `zone-flags`. + +``` +import './zone-flags'; +import 'zone.js'; // Included with Angular CLI. +``` + +After those steps, if you add event listeners for the `scroll` event, the listeners will be `passive`. + ## Custom events with `EventEmitter` [Directives](guide/built-in-directives) typically raise custom events with an Angular [EventEmitter](api/core/EventEmitter) as follows. diff --git a/aio/content/guide/forms.md b/aio/content/guide/forms.md index dd6630d9d5aa..71fe2eb035ee 100644 --- a/aio/content/guide/forms.md +++ b/aio/content/guide/forms.md @@ -220,7 +220,7 @@ Any unique value will do, but using a descriptive name is helpful. 2. You can now remove the diagnostic messages that show interpolated values. -3. To confirm that two-way data binding works for the entire hero model, add a new binding at the top to the component's `diagnostic` property. +3. To confirm that two-way data binding works for the entire hero model, add a new text binding with the [`json` pipe](api/common/JsonPipe) (which would serialize the data to a string) at the top to the component's template. After these revisions, the form template should look like the following: @@ -238,7 +238,7 @@ If you run the app now and change every hero model property, the form might disp The diagnostic near the top of the form confirms that all of your changes are reflected in the model. -4. When you have observed the effects, you can delete the `{{diagnostic}}` binding. +4. When you have observed the effects, you can delete the `{{ model | json }}` text binding. ## Track control states diff --git a/aio/content/guide/lazy-loading-ngmodules.md b/aio/content/guide/lazy-loading-ngmodules.md index f85990b22f95..0a926ef6c6ff 100644 --- a/aio/content/guide/lazy-loading-ngmodules.md +++ b/aio/content/guide/lazy-loading-ngmodules.md @@ -271,7 +271,7 @@ With the CLI, the command to generate a service is as follows: ng generate service -In your service, import the following router members, implement `Resolve`, and inject the `Router` service: +In the newly-created service, implement the `Resolve` interface provided by the `@angular/router` package: @@ -279,8 +279,14 @@ import { Resolve } from '@angular/router'; ... -export class CrisisDetailResolverService implements Resolve<> { - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> { +/* An interface that represents your data model */ +export interface Crisis { + id: number; + name: string; +} + +export class CrisisDetailResolverService implements Resolve { + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { // your logic goes here } } @@ -291,7 +297,7 @@ Import this resolver into your module's routing module. -import { YourResolverService } from './your-resolver.service'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; @@ -302,22 +308,44 @@ Add a `resolve` object to the component's `route` configuration. path: '/your-path', component: YourComponent, resolve: { - crisis: YourResolverService + crisis: CrisisDetailResolverService } } -In the component, use an `Observable` to get the data from the `ActivatedRoute`. +In the component's constructor, inject an instance of the `ActivatedRoute` class that represents the current route. + - -ngOnInit() { - this.route.data - .subscribe((your-parameters) => { - // your data-specific code goes here - }); +import { ActivatedRoute } from '@angular/router'; + +@Component({ ... }) +class YourComponent { + constructor(private route: ActivatedRoute) {} } + + + +Use the injected instance of the `ActivatedRoute` class to access `data` associated with a given route. + + + +import { ActivatedRoute } from '@angular/router'; + +@Component({ ... }) +class YourComponent { + constructor(private route: ActivatedRoute) {} + + ngOnInit() { + this.route.data + .subscribe(data => { + const crisis: Crisis = data.crisis; + // ... + }); + } +} + For more information with a working example, see the [routing tutorial section on preloading](guide/router-tutorial-toh#preloading-background-loading-of-feature-areas). diff --git a/aio/content/guide/lifecycle-hooks.md b/aio/content/guide/lifecycle-hooks.md index 445b6cae8808..6cae1b0efbc0 100644 --- a/aio/content/guide/lifecycle-hooks.md +++ b/aio/content/guide/lifecycle-hooks.md @@ -60,7 +60,7 @@ Angular executes hook methods in the following sequence. You can use them to per - Called before `ngOnInit()` and whenever one or more data-bound input properties change. + Called before `ngOnInit()` (if the component has bound inputs) and whenever one or more data-bound input properties change. Note that if your component has no inputs or you use it without providing any inputs, the framework will not call `ngOnChanges()`. @@ -79,7 +79,7 @@ Angular executes hook methods in the following sequence. You can use them to per - Called once, after the first `ngOnChanges()`. + Called once, after the first `ngOnChanges()`. `ngOnInit()` is still called even when `ngOnChanges()` is not (which is the case when there are no template-bound inputs). @@ -395,14 +395,7 @@ Here it is attached to the repeated hero `
`: -Each spy's creation and destruction marks the appearance and disappearance of the attached hero `
` -with an entry in the *Hook Log* as seen here: - - - -Adding a hero results in a new hero `
`. The spy's `ngOnInit()` logs that event. +Each spy's creation and destruction marks the appearance and disappearance of the attached hero `
` with an entry in the *Hook Log*. Adding a hero results in a new hero `
`. The spy's `ngOnInit()` logs that event. The *Reset* button clears the `heroes` list. Angular removes all hero `
` elements from the DOM and destroys their spy directives at the same time. diff --git a/aio/content/guide/ngmodule-faq.md b/aio/content/guide/ngmodule-faq.md index c505b8f2743e..74dadddcec3b 100644 --- a/aio/content/guide/ngmodule-faq.md +++ b/aio/content/guide/ngmodule-faq.md @@ -180,6 +180,12 @@ Only call and import a `forRoot()` result in the root application module, `AppMo Avoid importing it in any other module, particularly in a lazy-loaded module. For more information on `forRoot()` see [the `forRoot()` pattern](guide/singleton-services#the-forroot-pattern) section of the [Singleton Services](guide/singleton-services) guide. +
+Note: the `forRoot()` import can be used in a module other than `AppModule`. Importantly, +`forRoot()` should only be called once, and the module that imports the `forRoot()` needs to be available to +the root `ModuleInjector`. For more information, refer to the guide on [Hierarchical injectors](guide/hierarchical-dependency-injection#moduleinjector). +
+ For a service, instead of using `forRoot()`, specify `providedIn: 'root'` on the service's `@Injectable()` decorator, which makes the service automatically available to the whole application and thus singleton by default. diff --git a/aio/content/guide/reusable-animations.md b/aio/content/guide/reusable-animations.md index fe3b44f1ea59..1a31f5c78fdd 100644 --- a/aio/content/guide/reusable-animations.md +++ b/aio/content/guide/reusable-animations.md @@ -1,21 +1,19 @@ # Reusable animations -#### Prerequisites +This topic provides some examples of how to create reusable animations. -A basic understanding of the following concepts: +## Prerequisites + +Before continuing with this topic, you should be familiar with the following: * [Introduction to Angular animations](guide/animations) * [Transition and triggers](guide/transition-and-triggers) -
- -The [AnimationOptions](api/animations/AnimationOptions) interface in Angular animations enables you to create animations that you can reuse across different components. - ## Creating reusable animations To create a reusable animation, use the [`animation()`](api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your application components using the [`useAnimation()`](api/animations/useAnimation) API. - + In the above code snippet, `transAnimation` is made reusable by declaring it as an export variable. @@ -24,7 +22,11 @@ In the above code snippet, `transAnimation` is made reusable by declaring it as **Note:** The `height`, `opacity`, `backgroundColor`, and `time` inputs are replaced during runtime.
-You can import the reusable `transAnimation` variable in your component class and reuse it using the `useAnimation()` method as shown below. +You can also export a part of an animation. For example, the following snippet exports the animation `trigger`. + + + +From this point, you can import resuable animation variables in your component class. For example, the following code snippet imports the `transAnimation` variable for use in the `useAnimation()` method. diff --git a/aio/content/guide/router-tutorial-toh.md b/aio/content/guide/router-tutorial-toh.md index e3cdc5582b17..34ecfc5dea5d 100644 --- a/aio/content/guide/router-tutorial-toh.md +++ b/aio/content/guide/router-tutorial-toh.md @@ -1661,6 +1661,12 @@ _before_ the `AppRoutingModule`: +
+The import order of the modules is important because the order of the routes defined in the modules affects route matching. +If the `AppModule` were imported first, its wildcard route (`path: '**'`) would take precedence over the routes defined in `CrisisCenterModule`. +For more information, see the section on [route order](guide/router#route-order). +
+ Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide the feature routes. The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes. diff --git a/aio/content/guide/testing-components-scenarios.md b/aio/content/guide/testing-components-scenarios.md index 12dfd645a206..97d0a666fe0b 100644 --- a/aio/content/guide/testing-components-scenarios.md +++ b/aio/content/guide/testing-components-scenarios.md @@ -1469,9 +1469,9 @@ the following version of the `BannerComponent` does. The test fails when the `TestBed` tries to create the component. Recall that the application hasn't been compiled. diff --git a/aio/content/guide/testing-utility-apis.md b/aio/content/guide/testing-utility-apis.md index 6ad5a453c1ef..1f4628bc9f71 100644 --- a/aio/content/guide/testing-utility-apis.md +++ b/aio/content/guide/testing-utility-apis.md @@ -25,7 +25,7 @@ Here's a summary of the stand-alone functions, in order of likely utility: Runs the body of a test (`it`) or setup (`beforeEach`) function within a special _async test zone_. - See [discussion above](guide/testing-components-scenarios#waitForAsync). + See [waitForAsync](guide/testing-components-scenarios#waitForAsync). @@ -38,7 +38,7 @@ Here's a summary of the stand-alone functions, in order of likely utility: Runs the body of a test (`it`) within a special _fakeAsync test zone_, enabling - a linear control flow coding style. See [discussion above](guide/testing-components-scenarios#fake-async). + a linear control flow coding style. See [fakeAsync](guide/testing-components-scenarios#fake-async). @@ -63,7 +63,7 @@ Here's a summary of the stand-alone functions, in order of likely utility: Accepts an optional argument that moves the virtual clock forward by the specified number of milliseconds, clearing asynchronous activities scheduled within that timeframe. - See [discussion above](guide/testing-components-scenarios#tick). + See [tick](guide/testing-components-scenarios#tick). @@ -227,7 +227,7 @@ Here are the most important static methods, in order of likely utility. Compile the testing module asynchronously after you've finished configuring it. You **must** call this method if _any_ of the testing module components have a `templateUrl` or `styleUrls` because fetching component template and style files is necessarily asynchronous. - See [above](guide/testing-components-scenarios#compile-components). + See [compileComponents](guide/testing-components-scenarios#compile-components). After calling `compileComponents`, the `TestBed` configuration is frozen for the duration of the current spec. @@ -540,7 +540,7 @@ Here are the most useful methods for testers. To resume testing after completion of asynchronous activity or asynchronous change detection, hook that promise. - See [above](guide/testing-components-scenarios#when-stable). + See [whenStable](guide/testing-components-scenarios#when-stable). @@ -710,7 +710,7 @@ Here are the most useful `DebugElement` members for testers, in approximate orde Triggers the event by its name if there is a corresponding listener in the element's `listeners` collection. The second parameter is the _event object_ expected by the handler. - See [above](guide/testing-components-scenarios#trigger-event-handler). + See [triggerEventHandler](guide/testing-components-scenarios#trigger-event-handler). If the event lacks a listener or there's some other problem, consider calling `nativeElement.dispatchEvent(eventObject)`. diff --git a/aio/content/images/guide/lifecycle-hooks/spy-directive.gif b/aio/content/images/guide/lifecycle-hooks/spy-directive.gif deleted file mode 100644 index 60056082ba32..000000000000 Binary files a/aio/content/images/guide/lifecycle-hooks/spy-directive.gif and /dev/null differ diff --git a/aio/content/marketing/contributors.json b/aio/content/marketing/contributors.json index c571303b8d9f..603e8885f354 100644 --- a/aio/content/marketing/contributors.json +++ b/aio/content/marketing/contributors.json @@ -278,7 +278,6 @@ "name": "Dylan Hunn", "picture": "dylan-hunn.jpg", "twitter": "dylhunn", - "website": "https://hunn.io", "bio": "Dylan is a software engineer at Google on the Angular Core team. He loves board games, the open web, and adorable rabbits.", "groups": ["Angular"], "lead": "jelbourn" @@ -309,7 +308,6 @@ "name": "Josue Gutierrez", "picture": "josue.jpg", "twitter": "eusoj", - "website": "http://techtam.io", "bio": "Based in Mexico, Josue has been web developer since the last 10 years, he is part of the Google Developer Expert Program, passionate about teaching and building communities", "groups": ["GDE"] }, @@ -342,7 +340,6 @@ "name": "Gerard Sans", "picture": "gerardsans.jpg", "twitter": "gerardsans", - "website": "https://medium.com/@gerard.sans", "bio": "Gerard is very excited about the future of the Web and JavaScript. Always happy Computer Science Engineer and humble Google Developer Expert. He loves to share his learnings by giving talks, trainings and writing about cool technologies. He loves running AngularZone and GraphQL London, mentoring students and giving back to the community.", "groups": ["GDE"] }, @@ -366,7 +363,6 @@ "name": "Igor Minar", "picture": "igor-minar.jpg", "twitter": "IgorMinar", - "website": "https://google.com/+IgorMinar", "bio": "Igor is a software engineer at Google. He is a lead on the Angular project, practitioner of test driven development, open source enthusiast, hacker. In his free time, Igor enjoys spending time with his wife and two kids, doing outdoor activities (including but not limited to sports, gardening and building retaining walls).", "groups": ["Angular"] }, @@ -405,7 +401,6 @@ "name": "Jeremy Wilken", "picture": "jeremywilken.jpg", "twitter": "gnomeontherun", - "website": "https://gnomeontherun.com", "bio": "Based in Austin Texas, Jeremy is an application architect and homebrewer. He is a Google Developer Expert in Web Technologies and Angular, with a focus on speaking and training and author of Angular in Action and Ionic in Action.", "groups": ["GDE"] }, @@ -431,7 +426,6 @@ "name": "Joe Eames", "picture": "joeeames.jpg", "twitter": "josepheames", - "website": "https://joeeames.me", "bio": "Joe Eames is a developer and educator. He publishes course on Angular and JavaScript on Pluralsight.com. He is an organizer of ng-conf, a Google Developer Expert in Angular, gives lots of talks & workshops, and loves all things web.", "groups": ["GDE"] }, @@ -525,7 +519,7 @@ "name": "Kevin Kreuzer", "picture": "kevin-kreuzer.jpg", "twitter": "kreuzercode", - "website": "kreuzercode.com", + "website": "http://kreuzercode.com", "bio": "Kevin is a passionate freelance front-end engineer and Google Developer Expert based in Switzerland. He is a JavaScript enthusiast and fascinated by Angular. Kevin always tries to learn new things, expand his knowledge, and share it with others in the form of blog posts, workshops, podcasts, or presentations. He is a writer for various publications and the most active writer on Angular in-depth in 2019. Contributing to multiple projects and maintaining 7 npm packages, Kevin is also a big believer in open source. Furthermore, Kevin is a big football fan. Since his childhood, he has supported Real Madrid, which you might notice in a lot of his blog posts and tutorials.", "groups": ["GDE"] }, @@ -593,14 +587,12 @@ "website": "https://www.softwarearchitekt.at", "bio": "Trainer and Consultant with focus on Angular. Writes for O'Reilly, the German Java Magazine and Heise. Regularly speaks at conferences.", "mentor": "mgechev", - "groups": ["GDE", - "Collaborators"] + "groups": ["GDE", "Collaborators"] }, "martinakraus": { "name": "Martina Kraus", "picture": "martinakraus.jpg", "twitter": "MartinaKraus11", - "website": "http://martina-kraus.io", "bio": "Martina is a software engineer and an Angular enthusiast. As a trainer, mentor and speaker she loves sharing knowledge about Web Technologies and Angular. She is Organizer of ngGirls and the local Angular Meetup", "groups": ["GDE"] }, @@ -689,7 +681,6 @@ "name": "Nir Kaufman", "picture": "nirkaufman.jpg", "twitter": "nirkaufman", - "website": "http://ngnir.life/", "bio": "Nir is a Principal Frontend Consultant & Head of the Angular department at 500Tech, Google Developer Expert and community leader. He organizes the largest Angular meetup group in Israel (Angular-IL), talks and teaches about front-end technologies around the world. He is also the author of two books about Angular and the founder of the 'Frontend Band'.", "groups": ["GDE"] }, @@ -778,7 +769,6 @@ "name": "Sander Elias", "picture": "sanderelias.jpg", "twitter": "esoSanderElias", - "website": "https://sanderelias.nl", "bio": "Sander is a versed developer with over 4 decades of practice under his belt. He is also an Google Developer Expert for web, specializing in Angular. Organizer of meetups and conferences. Helping out others wherever he can. When he is not breathing code, he is fiddling around with IOT, photography, science and anything that might vaguely is gadget-like! Thinks he a master of the grill, but in reality you probably don't get a food-poisoning ;) Also, and actually the most important thing to him, he is a father of 4, and has the most patient girlfriend in the universe.", "groups": ["GDE"] }, @@ -810,7 +800,6 @@ "name": "Siddharth Ajmera", "picture": "sidd-ajmera.jpg", "twitter": "SiddAjmera", - "website": "https://webstackup.com/", "bio": "Siddharth is a Full Stack JavaScript Developer and a GDE in Angular. He's passionate about sharing his knowledge on Angular, Firebase and the Web in general. He's the organizer of WebStack, a local community of developers focused on Web, Mobile, Voice and Server related technologies in general. WebStack hosts free monthly meetups every 2nd or 3rd Saturday of the month. Siddharth is also an avid photographer and loves traveling. Find him anywhere on the Web with `SiddAjmera`.", "groups": ["GDE"] }, @@ -895,7 +884,6 @@ "name": "Emma Twersky", "picture": "twerske.jpg", "twitter": "twerske", - "website": "https://twerske.github.io/", "bio": "Emma is a Developer Advocate at Google. She is passionate about good user experiences and design.", "groups": ["Angular"], "lead": "mgechev" diff --git a/aio/content/marketing/resources.json b/aio/content/marketing/resources.json index ad6374cce17c..8077504f41b4 100644 --- a/aio/content/marketing/resources.json +++ b/aio/content/marketing/resources.json @@ -156,13 +156,13 @@ }, "ab2": { "desc": "Use Angular and Meteor to build full-stack JavaScript apps for Mobile and Desktop.", - "logo": "http://www.angular-meteor.com/images/logo.png", + "logo": "https://angular-meteor.com/assets/images/logo-large.png", "title": "Meteor", "url": "https://github.com/urigo/angular-meteor" }, "ab3": { "desc": "Apollo is a data stack for modern apps, built with GraphQL.", - "logo": "http://docs.apollostack.com/logo/large.png", + "logo": "", "title": "Apollo", "url": "https://www.apollographql.com/docs/angular/" }, @@ -341,7 +341,7 @@ }, "a2b": { "desc": "PrimeNG is a collection of rich UI components for Angular", - "logo": "http://www.primefaces.org/primeng/showcase/resources/images/primeng.svg", + "logo": "https://www.primefaces.org/primeng/resources/images/logo-primeng.svg", "title": "Prime Faces", "url": "https://www.primefaces.org/primeng/" }, @@ -353,7 +353,7 @@ }, "a5b": { "desc": "High-performance UI controls with the most complete Angular support available. Wijmo’s controls are all written in TypeScript and have zero dependencies. FlexGrid control includes full declarative markup, including cell templates.", - "logo": "http://wijmocdn.azureedge.net/wijmositeblob/wijmo-theme/logos/wijmo-55.png", + "logo": "", "title": "Wijmo", "url": "https://www.grapecity.com/wijmo" }, @@ -438,7 +438,7 @@ "desc": "Amexio is a rich set of Angular components powered by HTML5 & CSS3 for Responsive Web Design and 80+ built-in Material Design Themes. Amexio has 3 Editions, Standard, Enterprise and Creative. Std Edition consists of basic UI Components which include Grid, Tabs, Form Inputs and so on. While Enterprise Edition consists of components like Calendar, Tree Tabs, Social Media Logins (Facebook, GitHub, Twitter and so on) and Creative Edition is focused building elegant and beautiful websites. With more than 200+ components/features. All the editions are open-sourced and free, based on Apache 2 License.", "title": "Amexio - Angular Extensions", "url": "https://amexio.tech/", - "logo": "http://www.amexio.org/amexio-logo.png" + "logo": "" }, "bm": { "desc": "A lightweight Material Design library for Angular, based upon Google's Material Components for the Web", @@ -765,7 +765,7 @@ "500tech": { "desc": "Learn from 500Tech, an Angular consultancy in Israel. This course was built by an expert developer, who lives and breathes Angular, and has practical experience with real world large scale Angular apps.", "title": "Angular Hands-on Course (Israel)", - "url": "http://angular2.courses.500tech.com/" + "url": "https://500tech.com/courses/angular/" }, "9ab": { "desc": "OnSite Training From the Authors of \"Become A Ninja with Angular\"", diff --git a/aio/content/navigation.json b/aio/content/navigation.json index 1755a29b527a..74ec5993c5e0 100644 --- a/aio/content/navigation.json +++ b/aio/content/navigation.json @@ -411,27 +411,6 @@ } ] }, - { - "title": "Schematics", - "tooltip": "Understanding schematics.", - "children": [ - { - "url": "guide/schematics", - "title": "Schematics Overview", - "tooltip": "Extending CLI generation capabilities." - }, - { - "url": "guide/schematics-authoring", - "title": "Authoring Schematics", - "tooltip": "Understand the structure of a schematic." - }, - { - "url": "guide/schematics-for-libraries", - "title": "Schematics for Libraries", - "tooltip": "Use schematics to integrate your library with the Angular CLI." - } - ] - }, { "title": "Service Workers & PWA", "tooltip": "Angular service workers: Controlling caching of application resources.", @@ -571,6 +550,27 @@ "title": "DevTools", "tooltip": "DevTools", "url": "guide/devtools" + }, + { + "title": "Schematics", + "tooltip": "Understanding schematics.", + "children": [ + { + "url": "guide/schematics", + "title": "Schematics Overview", + "tooltip": "Extending CLI generation capabilities." + }, + { + "url": "guide/schematics-authoring", + "title": "Authoring Schematics", + "tooltip": "Understand the structure of a schematic." + }, + { + "url": "guide/schematics-for-libraries", + "title": "Schematics for Libraries", + "tooltip": "Use schematics to integrate your library with the Angular CLI." + } + ] } ] }, diff --git a/aio/content/start/index.md b/aio/content/start/index.md index e2e3b5570b6b..0205aa35bbae 100644 --- a/aio/content/start/index.md +++ b/aio/content/start/index.md @@ -4,7 +4,7 @@ Welcome to Angular! This tutorial introduces you to the essentials of Angular by walking you through building an e-commerce site with a catalog, shopping cart, and check-out form. -To help you get started right away, this tutorial uses a ready-made application that you can examine and modify interactively on [Stackblitz](https://stackblitz.com/)—without having to [set up a local work environment](guide/setup-local "Setup guide"). +To help you get started right away, this tutorial uses a ready-made application that you can examine and modify interactively on [StackBlitz](https://stackblitz.com/)—without having to [set up a local work environment](guide/setup-local "Setup guide"). StackBlitz is a browser-based development environment where you can create, save, and share projects using a variety of technologies. ## Prerequisites diff --git a/aio/content/tutorial/toh-pt0.md b/aio/content/tutorial/toh-pt0.md index 61bfccb37e88..6ae60f5e5765 100644 --- a/aio/content/tutorial/toh-pt0.md +++ b/aio/content/tutorial/toh-pt0.md @@ -40,8 +40,7 @@ The Angular CLI installs the necessary Angular `npm` packages and other dependen It also creates the following workspace and starter project files: * A new workspace, with a root folder named `angular-tour-of-heroes`. - * An initial skeleton app project, also called `angular-tour-of-heroes` (in the `src` subfolder). - * An end-to-end test project (in the e2e subfolder). + * An initial skeleton app project in the `src/app` subfolder. * Related configuration files. The initial app project contains a simple Welcome app, ready to run. diff --git a/aio/package.json b/aio/package.json index 21a5031c5514..f83b9be5948f 100644 --- a/aio/package.json +++ b/aio/package.json @@ -79,7 +79,7 @@ }, "//engines-comment": "Keep this in sync with /package.json and /aio/tools/examples/shared/package.json", "engines": { - "node": "^12.20.0 || ^14.0.0", + "node": ">=14.0.0", "yarn": ">=1.22.4 <2", "npm": "Please use yarn instead of NPM to install dependencies" }, diff --git a/aio/scripts/test-external-urls.js b/aio/scripts/test-external-urls.js new file mode 100644 index 000000000000..a0b110ca55b0 --- /dev/null +++ b/aio/scripts/test-external-urls.js @@ -0,0 +1,99 @@ +/** + * @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 + */ + +const {green, red} = require('chalk'); +const fetch = require('node-fetch'); +const {join} = require('path'); + +/** The full path to the contributons.json fie. */ +const contributorsFilePath = join(__dirname, '../content/marketing/contributors.json'); + + +/** Verify the provided contributor websites are reachable via http(s). */ +(async () => { + /** The json object from the contributors.json file. */ + const contributorsJson = require(contributorsFilePath); + /** The contributors flattened into an array containing the object key as a property. */ + const contributors = Object.entries(contributorsJson).map(([key, entry]) => ({key, ...entry})); + /** Discovered contributor entries with failures loading the provided website. */ + const failures = []; + /** The longest discovered length of a value in the key, website or message property. */ + let padding = {key: 0, website: 0, message: 0}; + + /** Adds a provided failure to the list, updating the paddings as appropriate. */ + const addFailure = (failure) => { + padding.key = Math.max(padding.key, failure.key.length); + padding.website = Math.max(padding.website, failure.website.length); + padding.message = Math.max(padding.message, failure.message.length); + failures.push(failure); + }; + + // By creating an array of Promises resolving for each attempt at checking if a contributors + // website is reachable, these checks can be done in parallel. + await Promise.allSettled(contributors.map(async entry => { + // If no website is provided no check is needed. + if (entry.website === undefined) { + return; + } + + // Ensure the provided website value is a valid external url serves via http or https. + let url; + try { + url = new URL(entry.website); + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + // Throw a generic error here to have the more specific error rethrown by the catch block. + throw Error; + } + } catch { + addFailure({...entry, message: 'Not a valid http(s) URL'}); + return; + } + + // Check validated websites to confirm they can be reached via fetch. + try { + const result = await fetch(url, {method: 'HEAD'}); + if (!result.ok) { + // If the url is for linkedin.com and the status returned is a `999`, we can assume that + // the page is working as expected as linkedin.com returns a `999` status for + // non-browser based requests. Other pages returning a `999` may still indicate an + // error in the request for the page. + if (result.status === 999 && url.hostname.includes('linkedin.com')) { + return; + } + + // If the page returns a 429 for too many requests, we will assume it works and continue + // checking in the future. + if (result.status === 429) { + return; + } + + // Throw the error status a `code` to be used in the catch block. + throw {code: result.status}; + } + } catch (err) { + if (err.code !== undefined) { + addFailure({...entry, message: err.code}); + } else { + addFailure({...entry, message: err}) + } + } + })); + + if (failures.length === 0) { + console.info(green(' ✓ All websites defined in the contributors.json passed loading check.')); + } else { + console.group(red(`${failures.length} url(s) were unable to load:`)); + failures.forEach((failure) => { + const key = failure.key.padEnd(padding.key); + const website = failure.website.padEnd(padding.website); + const message = failure.message; + console.log(`${key} ${website} Error: ${message}`); + }); + console.groupEnd(); + } +})(); diff --git a/dev-infra/ng-dev.js b/dev-infra/ng-dev.js index ecab957b60b3..2846b3f33c47 100755 --- a/dev-infra/ng-dev.js +++ b/dev-infra/ng-dev.js @@ -5180,23 +5180,17 @@ function buildReleaseOutput(stampForRelease = false) { */ /** Yargs command builder for configuring the `ng-dev release build` command. */ function builder$7(argv) { - return argv - .option('json', { + return argv.option('json', { type: 'boolean', description: 'Whether the built packages should be printed to stdout as JSON.', default: false, - }) - .option('stampForRelease', { - type: 'boolean', - description: 'Whether the built packages should be stamped for release.', - default: false, }); } /** Yargs command handler for building a release. */ function handler$7(args) { return tslib.__awaiter(this, void 0, void 0, function* () { const { npmPackages } = getReleaseConfig(); - let builtPackages = yield buildReleaseOutput(args.stampForRelease); + let builtPackages = yield buildReleaseOutput(true); // If package building failed, print an error and exit with an error code. if (builtPackages === null) { error(red(` ✘ Could not build release output. Please check output above.`)); diff --git a/dev-infra/release/build/cli.ts b/dev-infra/release/build/cli.ts index 9b281c76bce1..6125dad1a9eb 100644 --- a/dev-infra/release/build/cli.ts +++ b/dev-infra/release/build/cli.ts @@ -17,28 +17,21 @@ import {buildReleaseOutput} from './index'; /** Command line options for building a release. */ export interface ReleaseBuildOptions { json: boolean; - stampForRelease: boolean; } /** Yargs command builder for configuring the `ng-dev release build` command. */ function builder(argv: Argv): Argv { - return argv - .option('json', { - type: 'boolean', - description: 'Whether the built packages should be printed to stdout as JSON.', - default: false, - }) - .option('stampForRelease', { - type: 'boolean', - description: 'Whether the built packages should be stamped for release.', - default: false, - }); + return argv.option('json', { + type: 'boolean', + description: 'Whether the built packages should be printed to stdout as JSON.', + default: false, + }); } /** Yargs command handler for building a release. */ async function handler(args: Arguments) { const {npmPackages} = getReleaseConfig(); - let builtPackages = await buildReleaseOutput(args.stampForRelease); + let builtPackages = await buildReleaseOutput(true); // If package building failed, print an error and exit with an error code. if (builtPackages === null) { diff --git a/package.json b/package.json index 86e7c5be0141..b37dda6e2cf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "12.1.0-next.2", + "version": "12.1.0-next.3", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", diff --git a/packages/benchpress/test/metric/multi_metric_spec.ts b/packages/benchpress/test/metric/multi_metric_spec.ts index 347152d4fc00..bc3ab5ca3356 100644 --- a/packages/benchpress/test/metric/multi_metric_spec.ts +++ b/packages/benchpress/test/metric/multi_metric_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, Metric, MultiMetric} from '../../index'; (function() { @@ -22,29 +20,27 @@ function createMetric(ids: any[]) { } describe('multi metric', () => { - it('should merge descriptions', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => { - expect(m.describe()).toEqual({'m1': 'describe', 'm2': 'describe'}); - async.done(); - }); - })); + it('should merge descriptions', done => { + createMetric(['m1', 'm2']).then((m) => { + expect(m.describe()).toEqual({'m1': 'describe', 'm2': 'describe'}); + done(); + }); + }); - it('should merge all beginMeasure calls', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => m.beginMeasure()).then((values) => { - expect(values).toEqual(['m1_beginMeasure', 'm2_beginMeasure']); - async.done(); - }); - })); + it('should merge all beginMeasure calls', done => { + createMetric(['m1', 'm2']).then((m) => m.beginMeasure()).then((values) => { + expect(values).toEqual(['m1_beginMeasure', 'm2_beginMeasure']); + done(); + }); + }); [false, true].forEach((restartFlag) => { - it(`should merge all endMeasure calls for restart=${restartFlag}`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createMetric(['m1', 'm2']).then((m) => m.endMeasure(restartFlag)).then((values) => { - expect(values).toEqual({'m1': {'restart': restartFlag}, 'm2': {'restart': restartFlag}}); - async.done(); - }); - })); + it(`should merge all endMeasure calls for restart=${restartFlag}`, done => { + createMetric(['m1', 'm2']).then((m) => m.endMeasure(restartFlag)).then((values) => { + expect(values).toEqual({'m1': {'restart': restartFlag}, 'm2': {'restart': restartFlag}}); + done(); + }); + }); }); }); })(); diff --git a/packages/benchpress/test/metric/perflog_metric_spec.ts b/packages/benchpress/test/metric/perflog_metric_spec.ts index 20e6469318f3..b93046ac89d5 100644 --- a/packages/benchpress/test/metric/perflog_metric_spec.ts +++ b/packages/benchpress/test/metric/perflog_metric_spec.ts @@ -7,7 +7,6 @@ */ import {StaticProvider} from '@angular/core'; -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; import {Injector, Metric, Options, PerfLogEvent, PerfLogFeatures, PerflogMetric, WebDriverExtension} from '../../index'; import {TraceEventFactory} from '../trace_event_factory'; @@ -130,152 +129,145 @@ describe('perflog metric', () => { }); describe('beginMeasure', () => { - it('should not force gc and mark the timeline', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric([[]], null!); - metric.beginMeasure().then((_) => { - expect(commandLog).toEqual([['timeBegin', 'benchpress0']]); - - async.done(); - }); - })); - - it('should force gc and mark the timeline', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric([[]], null!, {forceGc: true}); - metric.beginMeasure().then((_) => { - expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]); - - async.done(); - }); - })); + it('should not force gc and mark the timeline', done => { + const metric = createMetric([[]], null!); + metric.beginMeasure().then((_) => { + expect(commandLog).toEqual([['timeBegin', 'benchpress0']]); + + done(); + }); + }); + + it('should force gc and mark the timeline', done => { + const metric = createMetric([[]], null!, {forceGc: true}); + metric.beginMeasure().then((_) => { + expect(commandLog).toEqual([['gc'], ['timeBegin', 'benchpress0']]); + + done(); + }); + }); }); describe('endMeasure', () => { - it('should mark and aggregate events in between the marks', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null!); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog' - ]); - expect(data['scriptTime']).toBe(2); - - async.done(); - }); - })); - - it('should mark and aggregate events since navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), - eventFactory.start('script', 8), eventFactory.end('script', 9), - eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null!); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(1); - - async.done(); - }); - })); - - it('should ignore navigationStart if ignoreNavigation is set', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), - eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), - eventFactory.start('script', 8), eventFactory.end('script', 9), - eventFactory.markEnd('benchpress0', 10) - ]]; - const metric = createMetric(events, null!, {ignoreNavigation: true}); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(3); - - async.done(); - }); - })); - - it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [ - eventFactory.markStart('benchpress0', 0), - eventFactory.markEnd('benchpress0', 1), - eventFactory.markStart('benchpress1', 2), - ], - [eventFactory.markEnd('benchpress1', 3)] - ]; - const metric = createMetric(events, null!); - metric.beginMeasure() - .then((_) => metric.endMeasure(true)) - .then((_) => metric.endMeasure(true)) - .then((_) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' - ]); - - async.done(); - }); - })); - - it('should loop and aggregate until the end mark is present', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)], - [eventFactory.end('script', 2)], - [ - eventFactory.start('script', 3), eventFactory.end('script', 5), - eventFactory.markEnd('benchpress0', 10) - ] - ]; - const metric = createMetric(events, null!); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog', - ['setTimeout', 100], 'readPerfLog', ['setTimeout', 100], 'readPerfLog' - ]); - expect(data['scriptTime']).toBe(3); - - async.done(); - }); - })); - - it('should store events after the end mark for the next call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const events = [ - [ - eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1), - eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1), - eventFactory.end('script', 2) - ], - [ - eventFactory.start('script', 3), eventFactory.end('script', 5), - eventFactory.markEnd('benchpress1', 6) - ] - ]; - const metric = createMetric(events, null!); - metric.beginMeasure() - .then((_) => metric.endMeasure(true)) - .then((data) => { - expect(data['scriptTime']).toBe(0); - return metric.endMeasure(true); - }) - .then((data) => { - expect(commandLog).toEqual([ - ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' - ]); - expect(data['scriptTime']).toBe(3); - - async.done(); - }); - })); + it('should mark and aggregate events in between the marks', done => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(2); + + done(); + }); + }); + + it('should mark and aggregate events since navigationStart', done => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), + eventFactory.start('script', 8), eventFactory.end('script', 9), + eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(1); + + done(); + }); + }); + + it('should ignore navigationStart if ignoreNavigation is set', done => { + const events = [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4), + eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7), + eventFactory.start('script', 8), eventFactory.end('script', 9), + eventFactory.markEnd('benchpress0', 10) + ]]; + const metric = createMetric(events, null!, {ignoreNavigation: true}); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(3); + + done(); + }); + }); + + it('should restart timing', done => { + const events = [ + [ + eventFactory.markStart('benchpress0', 0), + eventFactory.markEnd('benchpress0', 1), + eventFactory.markStart('benchpress1', 2), + ], + [eventFactory.markEnd('benchpress1', 3)] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure() + .then((_) => metric.endMeasure(true)) + .then((_) => metric.endMeasure(true)) + .then((_) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' + ]); + + done(); + }); + }); + + it('should loop and aggregate until the end mark is present', done => { + const events = [ + [eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 1)], + [eventFactory.end('script', 2)], + [ + eventFactory.start('script', 3), eventFactory.end('script', 5), + eventFactory.markEnd('benchpress0', 10) + ] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', null], 'readPerfLog', + ['setTimeout', 100], 'readPerfLog', ['setTimeout', 100], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(3); + + done(); + }); + }); + + it('should store events after the end mark for the next call', done => { + const events = [ + [ + eventFactory.markStart('benchpress0', 0), eventFactory.markEnd('benchpress0', 1), + eventFactory.markStart('benchpress1', 1), eventFactory.start('script', 1), + eventFactory.end('script', 2) + ], + [ + eventFactory.start('script', 3), eventFactory.end('script', 5), + eventFactory.markEnd('benchpress1', 6) + ] + ]; + const metric = createMetric(events, null!); + metric.beginMeasure() + .then((_) => metric.endMeasure(true)) + .then((data) => { + expect(data['scriptTime']).toBe(0); + return metric.endMeasure(true); + }) + .then((data) => { + expect(commandLog).toEqual([ + ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['timeEnd', 'benchpress1', 'benchpress2'], 'readPerfLog' + ]); + expect(data['scriptTime']).toBe(3); + + done(); + }); + }); describe('with forced gc', () => { let events: PerfLogEvent[][]; @@ -290,29 +282,28 @@ describe('perflog metric', () => { ]]; }); - it('should measure forced gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric(events, null!, {forceGc: true}); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(commandLog).toEqual([ - ['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], - 'readPerfLog', ['gc'], ['timeEnd', 'benchpress1', null], 'readPerfLog' - ]); - expect(data['forcedGcTime']).toBe(3); - expect(data['forcedGcAmount']).toBe(1.5); - - async.done(); - }); - })); - - it('should restart after the forced gc if needed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric(events, null!, {forceGc: true}); - metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => { - expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']); - - async.done(); - }); - })); + it('should measure forced gc', done => { + const metric = createMetric(events, null!, {forceGc: true}); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(commandLog).toEqual([ + ['gc'], ['timeBegin', 'benchpress0'], ['timeEnd', 'benchpress0', 'benchpress1'], + 'readPerfLog', ['gc'], ['timeEnd', 'benchpress1', null], 'readPerfLog' + ]); + expect(data['forcedGcTime']).toBe(3); + expect(data['forcedGcAmount']).toBe(1.5); + + done(); + }); + }); + + it('should restart after the forced gc if needed', done => { + const metric = createMetric(events, null!, {forceGc: true}); + metric.beginMeasure().then((_) => metric.endMeasure(true)).then((data) => { + expect(commandLog[5]).toEqual(['timeEnd', 'benchpress1', 'benchpress2']); + + done(); + }); + }); }); }); @@ -335,352 +326,314 @@ describe('perflog metric', () => { } describe('frame metrics', () => { - it('should calculate mean frame time', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 3), eventFactory.instant('frame', 4), - eventFactory.markEnd('frameCapture', 5) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.mean']).toBe(((3 - 1) + (4 - 3)) / 2); - async.done(); - }); - })); - - it('should throw if no start event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.instant('frame', 4), eventFactory.markEnd('frameCapture', 5)], { - captureFrames: true - }).catch((err): any => { - expect(() => { - throw err; - }).toThrowError('missing start event for frame capture'); - async.done(); - }); - })); - - it('should throw if no end event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [eventFactory.markStart('frameCapture', 3), eventFactory.instant('frame', 4)], - {captureFrames: true}) - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('missing end event for frame capture'); - async.done(); - }); - })); - - it('should throw if trying to capture twice', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 3), - eventFactory.markStart('frameCapture', 4) - ], - {captureFrames: true}) - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('can capture frames only once per benchmark run'); - async.done(); - }); - })); - - it('should throw if trying to capture when frame capture is disabled', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.markStart('frameCapture', 3)]).catch((err) => { - expect(() => { - throw err; - }) - .toThrowError( - 'found start event for frame capture, but frame capture was not requested in benchpress'); - async.done(); - return null; - }); - })); - - it('should throw if frame capture is enabled, but nothing is captured', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([], {captureFrames: true}).catch((err): any => { - expect(() => { - throw err; - }).toThrowError('frame capture requested in benchpress, but no start event was found'); - async.done(); - }); - })); - - it('should calculate best and worst frame time', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 9), eventFactory.instant('frame', 15), - eventFactory.instant('frame', 18), eventFactory.instant('frame', 28), - eventFactory.instant('frame', 32), eventFactory.markEnd('frameCapture', 10) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.worst']).toBe(10); - expect(data['frameTime.best']).toBe(3); - async.done(); - }); - })); - - it('should calculate percentage of smoothness to be good', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 2), eventFactory.instant('frame', 3), - eventFactory.markEnd('frameCapture', 4) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.smooth']).toBe(1.0); - async.done(); - }); - })); - - it('should calculate percentage of smoothness to be bad', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), - eventFactory.instant('frame', 2), eventFactory.instant('frame', 22), - eventFactory.instant('frame', 23), eventFactory.instant('frame', 24), - eventFactory.markEnd('frameCapture', 4) - ], - {captureFrames: true}) - .then((data) => { - expect(data['frameTime.smooth']).toBe(0.75); - async.done(); - }); - })); + it('should calculate mean frame time', done => { + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 3), eventFactory.instant('frame', 4), + eventFactory.markEnd('frameCapture', 5) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.mean']).toBe(((3 - 1) + (4 - 3)) / 2); + done(); + }); + }); + + it('should throw if no start event', done => { + aggregate([eventFactory.instant('frame', 4), eventFactory.markEnd('frameCapture', 5)], { + captureFrames: true + }).catch((err): any => { + expect(() => { + throw err; + }).toThrowError('missing start event for frame capture'); + done(); + }); + }); + + it('should throw if no end event', done => { + aggregate([eventFactory.markStart('frameCapture', 3), eventFactory.instant('frame', 4)], { + captureFrames: true + }).catch((err): any => { + expect(() => { + throw err; + }).toThrowError('missing end event for frame capture'); + done(); + }); + }); + + it('should throw if trying to capture twice', done => { + aggregate( + [eventFactory.markStart('frameCapture', 3), eventFactory.markStart('frameCapture', 4)], + {captureFrames: true}) + .catch((err): any => { + expect(() => { + throw err; + }).toThrowError('can capture frames only once per benchmark run'); + done(); + }); + }); + + it('should throw if trying to capture when frame capture is disabled', done => { + aggregate([eventFactory.markStart('frameCapture', 3)]).catch((err) => { + expect(() => { + throw err; + }) + .toThrowError( + 'found start event for frame capture, but frame capture was not requested in benchpress'); + done(); + return null; + }); + }); + + it('should throw if frame capture is enabled, but nothing is captured', done => { + aggregate([], {captureFrames: true}).catch((err): any => { + expect(() => { + throw err; + }).toThrowError('frame capture requested in benchpress, but no start event was found'); + done(); + }); + }); + + it('should calculate best and worst frame time', done => { + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 9), eventFactory.instant('frame', 15), + eventFactory.instant('frame', 18), eventFactory.instant('frame', 28), + eventFactory.instant('frame', 32), eventFactory.markEnd('frameCapture', 10) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.worst']).toBe(10); + expect(data['frameTime.best']).toBe(3); + done(); + }); + }); + + it('should calculate percentage of smoothness to be good', done => { + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 2), eventFactory.instant('frame', 3), + eventFactory.markEnd('frameCapture', 4) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.smooth']).toBe(1.0); + done(); + }); + }); + + it('should calculate percentage of smoothness to be bad', done => { + aggregate( + [ + eventFactory.markStart('frameCapture', 0), eventFactory.instant('frame', 1), + eventFactory.instant('frame', 2), eventFactory.instant('frame', 22), + eventFactory.instant('frame', 23), eventFactory.instant('frame', 24), + eventFactory.markEnd('frameCapture', 4) + ], + {captureFrames: true}) + .then((data) => { + expect(data['frameTime.smooth']).toBe(0.75); + done(); + }); + }); + }); + + it('should report a single interval', done => { + aggregate([eventFactory.start('script', 0), eventFactory.end('script', 5)]).then((data) => { + expect(data['scriptTime']).toBe(5); + done(); + }); + }); + + it('should sum up multiple intervals', done => { + aggregate([ + eventFactory.start('script', 0), eventFactory.end('script', 5), + eventFactory.start('script', 10), eventFactory.end('script', 17) + ]).then((data) => { + expect(data['scriptTime']).toBe(12); + done(); + }); + }); + + it('should ignore not started intervals', done => { + aggregate([eventFactory.end('script', 10)]).then((data) => { + expect(data['scriptTime']).toBe(0); + done(); + }); + }); + + it('should ignore not ended intervals', done => { + aggregate([eventFactory.start('script', 10)]).then((data) => { + expect(data['scriptTime']).toBe(0); + done(); + }); + }); + + it('should ignore nested intervals', done => { + aggregate([ + eventFactory.start('script', 0), eventFactory.start('script', 5), + eventFactory.end('script', 10), eventFactory.end('script', 17) + ]).then((data) => { + expect(data['scriptTime']).toBe(17); + done(); + }); + }); + + it('should ignore events from different processed as the start mark', done => { + const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); + const metric = createMetric( + [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), + eventFactory.end('script', 5, null), otherProcessEventFactory.start('script', 10, null), + otherProcessEventFactory.end('script', 17, null), + eventFactory.markEnd('benchpress0', 20) + ]], + null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['scriptTime']).toBe(5); + done(); + }); + }); + + it('should mark a run as invalid if the start and end marks are different', done => { + const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); + const metric = createMetric( + [[ + eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), + eventFactory.end('script', 5, null), otherProcessEventFactory.start('script', 10, null), + otherProcessEventFactory.end('script', 17, null), + otherProcessEventFactory.markEnd('benchpress0', 20) + ]], + null!); + metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { + expect(data['invalid']).toBe(1); + done(); + }); + }); + + it('should support scriptTime metric', done => { + aggregate([eventFactory.start('script', 0), eventFactory.end('script', 5)]).then((data) => { + expect(data['scriptTime']).toBe(5); + done(); + }); + }); + + it('should support renderTime metric', done => { + aggregate([eventFactory.start('render', 0), eventFactory.end('render', 5)]).then((data) => { + expect(data['renderTime']).toBe(5); + done(); + }); + }); + + it('should support gcTime/gcAmount metric', done => { + aggregate([ + eventFactory.start('gc', 0, {'usedHeapSize': 2500}), + eventFactory.end('gc', 5, {'usedHeapSize': 1000}) + ]).then((data) => { + expect(data['gcTime']).toBe(5); + expect(data['gcAmount']).toBe(1.5); + expect(data['majorGcTime']).toBe(0); + done(); + }); + }); + + it('should support majorGcTime metric', done => { + aggregate([ + eventFactory.start('gc', 0, {'usedHeapSize': 2500}), + eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true}) + ]).then((data) => { + expect(data['gcTime']).toBe(5); + expect(data['majorGcTime']).toBe(5); + done(); + }); }); - it('should report a single interval', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5) - ]).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); - })); - - it('should sum up multiple intervals', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5), - eventFactory.start('script', 10), eventFactory.end('script', 17) - ]).then((data) => { - expect(data['scriptTime']).toBe(12); - async.done(); - }); - })); - - it('should ignore not started intervals', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.end('script', 10)]).then((data) => { - expect(data['scriptTime']).toBe(0); - async.done(); - }); - })); - - it('should ignore not ended intervals', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([eventFactory.start('script', 10)]).then((data) => { - expect(data['scriptTime']).toBe(0); - async.done(); - }); - })); - - it('should ignore nested intervals', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.start('script', 5), - eventFactory.end('script', 10), eventFactory.end('script', 17) - ]).then((data) => { - expect(data['scriptTime']).toBe(17); - async.done(); - }); - })); - - it('should ignore events from different processed as the start mark', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); - const metric = createMetric( - [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), - eventFactory.end('script', 5, null), - otherProcessEventFactory.start('script', 10, null), - otherProcessEventFactory.end('script', 17, null), - eventFactory.markEnd('benchpress0', 20) - ]], - null!); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); - })); - - it('should mark a run as invalid if the start and end marks are different', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const otherProcessEventFactory = new TraceEventFactory('timeline', 'pid1'); - const metric = createMetric( - [[ - eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 0, null), - eventFactory.end('script', 5, null), - otherProcessEventFactory.start('script', 10, null), - otherProcessEventFactory.end('script', 17, null), - otherProcessEventFactory.markEnd('benchpress0', 20) - ]], - null!); - metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => { - expect(data['invalid']).toBe(1); - async.done(); - }); - })); - - it('should support scriptTime metric', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.end('script', 5) - ]).then((data) => { - expect(data['scriptTime']).toBe(5); - async.done(); - }); - })); - - it('should support renderTime metric', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('render', 0), eventFactory.end('render', 5) - ]).then((data) => { - expect(data['renderTime']).toBe(5); - async.done(); - }); - })); - - it('should support gcTime/gcAmount metric', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('gc', 0, {'usedHeapSize': 2500}), - eventFactory.end('gc', 5, {'usedHeapSize': 1000}) - ]).then((data) => { - expect(data['gcTime']).toBe(5); - expect(data['gcAmount']).toBe(1.5); - expect(data['majorGcTime']).toBe(0); - async.done(); - }); - })); - - it('should support majorGcTime metric', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('gc', 0, {'usedHeapSize': 2500}), - eventFactory.end('gc', 5, {'usedHeapSize': 1000, 'majorGc': true}) - ]).then((data) => { - expect(data['gcTime']).toBe(5); - expect(data['majorGcTime']).toBe(5); - async.done(); - }); - })); - - it('should support pureScriptTime = scriptTime-gcTime-renderTime', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.start('script', 0), eventFactory.start('gc', 1, {'usedHeapSize': 1000}), - eventFactory.end('gc', 4, {'usedHeapSize': 0}), eventFactory.start('render', 4), - eventFactory.end('render', 5), eventFactory.end('script', 6) - ]).then((data) => { - expect(data['scriptTime']).toBe(6); - expect(data['pureScriptTime']).toBe(2); - async.done(); - }); - })); + it('should support pureScriptTime = scriptTime-gcTime-renderTime', done => { + aggregate([ + eventFactory.start('script', 0), eventFactory.start('gc', 1, {'usedHeapSize': 1000}), + eventFactory.end('gc', 4, {'usedHeapSize': 0}), eventFactory.start('render', 4), + eventFactory.end('render', 5), eventFactory.end('script', 6) + ]).then((data) => { + expect(data['scriptTime']).toBe(6); + expect(data['pureScriptTime']).toBe(2); + done(); + }); + }); describe('receivedData', () => { - it('should report received data since last navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.instant('receivedData', 0, {'encodedDataLength': 1}), - eventFactory.instant('navigationStart', 1), - eventFactory.instant('receivedData', 2, {'encodedDataLength': 2}), - eventFactory.instant('navigationStart', 3), - eventFactory.instant('receivedData', 4, {'encodedDataLength': 4}), - eventFactory.instant('receivedData', 5, {'encodedDataLength': 8}) - ], - {receivedData: true}) - .then((data) => { - expect(data['receivedData']).toBe(12); - async.done(); - }); - })); + it('should report received data since last navigationStart', done => { + aggregate( + [ + eventFactory.instant('receivedData', 0, {'encodedDataLength': 1}), + eventFactory.instant('navigationStart', 1), + eventFactory.instant('receivedData', 2, {'encodedDataLength': 2}), + eventFactory.instant('navigationStart', 3), + eventFactory.instant('receivedData', 4, {'encodedDataLength': 4}), + eventFactory.instant('receivedData', 5, {'encodedDataLength': 8}) + ], + {receivedData: true}) + .then((data) => { + expect(data['receivedData']).toBe(12); + done(); + }); + }); }); describe('requestCount', () => { - it('should report count of requests sent since last navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.instant('sendRequest', 0), eventFactory.instant('navigationStart', 1), - eventFactory.instant('sendRequest', 2), eventFactory.instant('navigationStart', 3), - eventFactory.instant('sendRequest', 4), eventFactory.instant('sendRequest', 5) - ], - {requestCount: true}) - .then((data) => { - expect(data['requestCount']).toBe(2); - async.done(); - }); - })); + it('should report count of requests sent since last navigationStart', done => { + aggregate( + [ + eventFactory.instant('sendRequest', 0), eventFactory.instant('navigationStart', 1), + eventFactory.instant('sendRequest', 2), eventFactory.instant('navigationStart', 3), + eventFactory.instant('sendRequest', 4), eventFactory.instant('sendRequest', 5) + ], + {requestCount: true}) + .then((data) => { + expect(data['requestCount']).toBe(2); + done(); + }); + }); }); describe('microMetrics', () => { - it('should report micro metrics', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('mm1', 0), - eventFactory.markEnd('mm1', 5), - ], - {microMetrics: {'mm1': 'micro metric 1'}}) - .then((data) => { - expect(data['mm1']).toBe(5.0); - async.done(); - }); - })); - - it('should ignore micro metrics that were not specified', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate([ - eventFactory.markStart('mm1', 0), - eventFactory.markEnd('mm1', 5), - ]).then((data) => { - expect(data['mm1']).toBeFalsy(); - async.done(); - }); - })); - - it('should report micro metric averages', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - aggregate( - [ - eventFactory.markStart('mm1*20', 0), - eventFactory.markEnd('mm1*20', 5), - ], - {microMetrics: {'mm1': 'micro metric 1'}}) - .then((data) => { - expect(data['mm1']).toBe(5 / 20); - async.done(); - }); - })); + it('should report micro metrics', done => { + aggregate( + [ + eventFactory.markStart('mm1', 0), + eventFactory.markEnd('mm1', 5), + ], + {microMetrics: {'mm1': 'micro metric 1'}}) + .then((data) => { + expect(data['mm1']).toBe(5.0); + done(); + }); + }); + + it('should ignore micro metrics that were not specified', done => { + aggregate([ + eventFactory.markStart('mm1', 0), + eventFactory.markEnd('mm1', 5), + ]).then((data) => { + expect(data['mm1']).toBeFalsy(); + done(); + }); + }); + + it('should report micro metric averages', done => { + aggregate( + [ + eventFactory.markStart('mm1*20', 0), + eventFactory.markEnd('mm1*20', 5), + ], + {microMetrics: {'mm1': 'micro metric 1'}}) + .then((data) => { + expect(data['mm1']).toBe(5 / 20); + done(); + }); + }); }); }); }); diff --git a/packages/benchpress/test/metric/user_metric_spec.ts b/packages/benchpress/test/metric/user_metric_spec.ts index cb4e17b81d0f..419caca3d495 100644 --- a/packages/benchpress/test/metric/user_metric_spec.ts +++ b/packages/benchpress/test/metric/user_metric_spec.ts @@ -7,7 +7,6 @@ */ import {Injector, StaticProvider} from '@angular/core'; -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; import {Options, PerfLogEvent, PerfLogFeatures, UserMetric, WebDriverAdapter} from '../../index'; @@ -42,23 +41,22 @@ describe('user metric', () => { }); describe('endMeasure', () => { - it('should stop measuring when all properties have numeric values', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const metric = createMetric( - [[]], new PerfLogFeatures(), - {userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}}); - metric.beginMeasure().then(() => metric.endMeasure(true)).then(values => { - expect(values['loadTime']).toBe(25); - expect(values['content']).toBe(250); - async.done(); - }); + it('should stop measuring when all properties have numeric values', done => { + const metric = createMetric( + [[]], new PerfLogFeatures(), + {userMetrics: {'loadTime': 'time to load', 'content': 'time to see content'}}); + metric.beginMeasure().then(() => metric.endMeasure(true)).then(values => { + expect(values['loadTime']).toBe(25); + expect(values['content']).toBe(250); + done(); + }); - wdAdapter.data['loadTime'] = 25; - // Wait before setting 2nd property. - setTimeout(() => { - wdAdapter.data['content'] = 250; - }, 50); - }), 600); + wdAdapter.data['loadTime'] = 25; + // Wait before setting 2nd property. + setTimeout(() => { + wdAdapter.data['content'] = 250; + }, 50); + }, 600); }); }); })(); diff --git a/packages/benchpress/test/reporter/console_reporter_spec.ts b/packages/benchpress/test/reporter/console_reporter_spec.ts index d042a992eeb0..c5cb5c98a8cb 100644 --- a/packages/benchpress/test/reporter/console_reporter_spec.ts +++ b/packages/benchpress/test/reporter/console_reporter_spec.ts @@ -7,7 +7,6 @@ */ import {StaticProvider} from '@angular/core'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {ConsoleReporter, Injector, MeasureValues, SampleDescription} from '../../index'; diff --git a/packages/benchpress/test/reporter/json_file_reporter_spec.ts b/packages/benchpress/test/reporter/json_file_reporter_spec.ts index 6fc2657ba79d..613991cc286f 100644 --- a/packages/benchpress/test/reporter/json_file_reporter_spec.ts +++ b/packages/benchpress/test/reporter/json_file_reporter_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} from '../../index'; { @@ -37,37 +35,35 @@ import {Injector, JsonFileReporter, MeasureValues, Options, SampleDescription} f return Injector.create(providers).get(JsonFileReporter); } - it('should write all data into a file', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createReporter({ - sampleId: 'someId', - descriptions: [{'a': 2}], - path: 'somePath', - metrics: {'a': 'script time', 'b': 'render time'} - }) - .reportSample( - [mv(0, 0, {'a': 3, 'b': 6})], - [mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]); - const regExp = /somePath\/someId_\d+\.json/; - expect(loggedFile['filename'].match(regExp) != null).toBe(true); - const parsedContent = JSON.parse(loggedFile['content']) as {[key: string]: any}; - expect(parsedContent).toEqual({ - 'description': { - 'id': 'someId', - 'description': {'a': 2}, - 'metrics': {'a': 'script time', 'b': 'render time'} - }, - 'stats': {'a': '4.00+-25%', 'b': '7.50+-20%'}, - 'completeSample': [ - {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}} - ], - 'validSample': [ - {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}}, - {'timeStamp': '1970-01-01T00:00:00.001Z', 'runIndex': 1, 'values': {'a': 5, 'b': 9}} - ] - }); - async.done(); - })); + it('should write all data into a file', done => { + createReporter({ + sampleId: 'someId', + descriptions: [{'a': 2}], + path: 'somePath', + metrics: {'a': 'script time', 'b': 'render time'} + }) + .reportSample( + [mv(0, 0, {'a': 3, 'b': 6})], + [mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]); + const regExp = /somePath\/someId_\d+\.json/; + expect(loggedFile['filename'].match(regExp) != null).toBe(true); + const parsedContent = JSON.parse(loggedFile['content']) as {[key: string]: any}; + expect(parsedContent).toEqual({ + 'description': { + 'id': 'someId', + 'description': {'a': 2}, + 'metrics': {'a': 'script time', 'b': 'render time'} + }, + 'stats': {'a': '4.00+-25%', 'b': '7.50+-20%'}, + 'completeSample': + [{'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}}], + 'validSample': [ + {'timeStamp': '1970-01-01T00:00:00.000Z', 'runIndex': 0, 'values': {'a': 3, 'b': 6}}, + {'timeStamp': '1970-01-01T00:00:00.001Z', 'runIndex': 1, 'values': {'a': 5, 'b': 9}} + ] + }); + done(); + }); }); } diff --git a/packages/benchpress/test/reporter/multi_reporter_spec.ts b/packages/benchpress/test/reporter/multi_reporter_spec.ts index 3f10fbaa5963..494d9098362e 100644 --- a/packages/benchpress/test/reporter/multi_reporter_spec.ts +++ b/packages/benchpress/test/reporter/multi_reporter_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, MeasureValues, MultiReporter, Reporter} from '../../index'; (function() { @@ -22,30 +20,29 @@ function createReporters(ids: any[]) { } describe('multi reporter', () => { - it('should reportMeasureValues to all', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const mv = new MeasureValues(0, new Date(), {}); - createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => { - expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); - async.done(); - }); - })); - - it('should reportSample to call', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const completeSample = - [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})]; - const validSample = [completeSample[1]]; - - createReporters(['m1', 'm2']) - .then((r) => r.reportSample(completeSample, validSample)) - .then((values) => { - expect(values).toEqual([ - {'id': 'm1', 'completeSample': completeSample, 'validSample': validSample}, - {'id': 'm2', 'completeSample': completeSample, 'validSample': validSample} - ]); - async.done(); - }); - })); + it('should reportMeasureValues to all', done => { + const mv = new MeasureValues(0, new Date(), {}); + createReporters(['m1', 'm2']).then((r) => r.reportMeasureValues(mv)).then((values) => { + expect(values).toEqual([{'id': 'm1', 'values': mv}, {'id': 'm2', 'values': mv}]); + done(); + }); + }); + + it('should reportSample to call', done => { + const completeSample = + [new MeasureValues(0, new Date(), {}), new MeasureValues(1, new Date(), {})]; + const validSample = [completeSample[1]]; + + createReporters(['m1', 'm2']) + .then((r) => r.reportSample(completeSample, validSample)) + .then((values) => { + expect(values).toEqual([ + {'id': 'm1', 'completeSample': completeSample, 'validSample': validSample}, + {'id': 'm2', 'completeSample': completeSample, 'validSample': validSample} + ]); + done(); + }); + }); }); })(); diff --git a/packages/benchpress/test/runner_spec.ts b/packages/benchpress/test/runner_spec.ts index 7cda672e3321..d304fa356bdb 100644 --- a/packages/benchpress/test/runner_spec.ts +++ b/packages/benchpress/test/runner_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, Metric, Options, Runner, SampleDescription, Sampler, SampleState, Validator, WebDriverAdapter} from '../index'; { @@ -35,83 +33,75 @@ import {Injector, Metric, Options, Runner, SampleDescription, Sampler, SampleSta return runner; } - it('should set SampleDescription.id', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createRunner() - .sample({id: 'someId'}) - .then((_) => injector.get(SampleDescription)) - .then((desc) => { - expect(desc.id).toBe('someId'); - async.done(); - }); - })); + it('should set SampleDescription.id', done => { + createRunner() + .sample({id: 'someId'}) + .then((_) => injector.get(SampleDescription)) + .then((desc) => { + expect(desc.id).toBe('someId'); + done(); + }); + }); - it('should merge SampleDescription.description', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createRunner([{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 1}}]) - .sample({ - id: 'someId', - providers: [{provide: Options.SAMPLE_DESCRIPTION, useValue: {'b': 2}}] - }) - .then((_) => injector.get(SampleDescription)) - .then((desc) => { - expect(desc.description) - .toEqual( - {'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11}); - async.done(); - }); - })); + it('should merge SampleDescription.description', done => { + createRunner([{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 1}}]) + .sample({ + id: 'someId', + providers: [{provide: Options.SAMPLE_DESCRIPTION, useValue: {'b': 2}}] + }) + .then((_) => injector.get(SampleDescription)) + .then((desc) => { + expect(desc.description) + .toEqual({'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11}); + done(); + }); + }); - it('should fill SampleDescription.metrics from the Metric', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createRunner() - .sample({id: 'someId'}) - .then((_) => injector.get(SampleDescription)) - .then((desc) => { - expect(desc.metrics).toEqual({'m1': 'some metric'}); - async.done(); - }); - })); + it('should fill SampleDescription.metrics from the Metric', done => { + createRunner() + .sample({id: 'someId'}) + .then((_) => injector.get(SampleDescription)) + .then((desc) => { + expect(desc.metrics).toEqual({'m1': 'some metric'}); + done(); + }); + }); - it('should provide Options.EXECUTE', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const execute = () => {}; - createRunner().sample({id: 'someId', execute: execute}).then((_) => { - expect(injector.get(Options.EXECUTE)).toEqual(execute); - async.done(); - }); - })); + it('should provide Options.EXECUTE', done => { + const execute = () => {}; + createRunner().sample({id: 'someId', execute: execute}).then((_) => { + expect(injector.get(Options.EXECUTE)).toEqual(execute); + done(); + }); + }); - it('should provide Options.PREPARE', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const prepare = () => {}; - createRunner().sample({id: 'someId', prepare: prepare}).then((_) => { - expect(injector.get(Options.PREPARE)).toEqual(prepare); - async.done(); - }); - })); + it('should provide Options.PREPARE', done => { + const prepare = () => {}; + createRunner().sample({id: 'someId', prepare: prepare}).then((_) => { + expect(injector.get(Options.PREPARE)).toEqual(prepare); + done(); + }); + }); - it('should provide Options.MICRO_METRICS', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createRunner().sample({id: 'someId', microMetrics: {'a': 'b'}}).then((_) => { - expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'}); - async.done(); - }); - })); + it('should provide Options.MICRO_METRICS', done => { + createRunner().sample({id: 'someId', microMetrics: {'a': 'b'}}).then((_) => { + expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'}); + done(); + }); + }); - it('should overwrite providers per sample call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createRunner([{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 1}}]) - .sample({ - id: 'someId', - providers: [{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 2}}] - }) - .then((_) => injector.get(SampleDescription)) - .then((desc) => { - expect(desc.description['a']).toBe(2); - async.done(); - }); - })); + it('should overwrite providers per sample call', done => { + createRunner([{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 1}}]) + .sample({ + id: 'someId', + providers: [{provide: Options.DEFAULT_DESCRIPTION, useValue: {'a': 2}}] + }) + .then((_) => injector.get(SampleDescription)) + .then((desc) => { + expect(desc.description['a']).toBe(2); + done(); + }); + }); }); } diff --git a/packages/benchpress/test/sampler_spec.ts b/packages/benchpress/test/sampler_spec.ts index feeea4253f27..8ca4f2a06703 100644 --- a/packages/benchpress/test/sampler_spec.ts +++ b/packages/benchpress/test/sampler_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, WebDriverAdapter} from '../index'; { @@ -47,59 +45,57 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, sampler = Injector.create(providers).get(Sampler); } - it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const log: any[] = []; - let count = 0; - const driver = new MockDriverAdapter([], (callback: Function) => { - const result = callback(); - log.push(result); - return Promise.resolve(result); - }); - createSampler({ - driver: driver, - validator: createCountingValidator(2), - prepare: () => count++, - execute: () => count++, - }); - sampler.sample().then((_) => { - expect(count).toBe(4); - expect(log).toEqual([0, 1, 2, 3]); - async.done(); - }); - })); + it('should call the prepare and execute callbacks using WebDriverAdapter.waitFor', done => { + const log: any[] = []; + let count = 0; + const driver = new MockDriverAdapter([], (callback: Function) => { + const result = callback(); + log.push(result); + return Promise.resolve(result); + }); + createSampler({ + driver: driver, + validator: createCountingValidator(2), + prepare: () => count++, + execute: () => count++, + }); + sampler.sample().then((_) => { + expect(count).toBe(4); + expect(log).toEqual([0, 1, 2, 3]); + done(); + }); + }); - it('should call prepare, beginMeasure, execute, endMeasure for every iteration', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let workCount = 0; - const log: any[] = []; - createSampler({ - metric: createCountingMetric(log), - validator: createCountingValidator(2), - prepare: () => { - log.push(`p${workCount++}`); - }, - execute: () => { - log.push(`w${workCount++}`); - } - }); - sampler.sample().then((_) => { - expect(log).toEqual([ - 'p0', - ['beginMeasure'], - 'w1', - ['endMeasure', false, {'script': 0}], - 'p2', - ['beginMeasure'], - 'w3', - ['endMeasure', false, {'script': 1}], - ]); - async.done(); - }); - })); + it('should call prepare, beginMeasure, execute, endMeasure for every iteration', done => { + let workCount = 0; + const log: any[] = []; + createSampler({ + metric: createCountingMetric(log), + validator: createCountingValidator(2), + prepare: () => { + log.push(`p${workCount++}`); + }, + execute: () => { + log.push(`w${workCount++}`); + } + }); + sampler.sample().then((_) => { + expect(log).toEqual([ + 'p0', + ['beginMeasure'], + 'w1', + ['endMeasure', false, {'script': 0}], + 'p2', + ['beginMeasure'], + 'w3', + ['endMeasure', false, {'script': 1}], + ]); + done(); + }); + }); it('should call execute, endMeasure for every iteration if there is no prepare callback', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { const log: any[] = []; let workCount = 0; createSampler({ @@ -118,93 +114,90 @@ import {Injector, MeasureValues, Metric, Options, Reporter, Sampler, Validator, 'w1', ['endMeasure', true, {'script': 1}], ]); - async.done(); + done(); }); - })); + }); - it('should only collect metrics for execute and ignore metrics from prepare', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let scriptTime = 0; - let iterationCount = 1; - createSampler({ - validator: createCountingValidator(2), - metric: new MockMetric( - [], - () => { - const result = Promise.resolve({'script': scriptTime}); - scriptTime = 0; - return result; - }), - prepare: () => { - scriptTime = 1 * iterationCount; - }, - execute: () => { - scriptTime = 10 * iterationCount; - iterationCount++; - } - }); - sampler.sample().then((state) => { - expect(state.completeSample.length).toBe(2); - expect(state.completeSample[0]).toEqual(mv(0, 1000, {'script': 10})); - expect(state.completeSample[1]).toEqual(mv(1, 1001, {'script': 20})); - async.done(); - }); - })); + it('should only collect metrics for execute and ignore metrics from prepare', done => { + let scriptTime = 0; + let iterationCount = 1; + createSampler({ + validator: createCountingValidator(2), + metric: new MockMetric( + [], + () => { + const result = Promise.resolve({'script': scriptTime}); + scriptTime = 0; + return result; + }), + prepare: () => { + scriptTime = 1 * iterationCount; + }, + execute: () => { + scriptTime = 10 * iterationCount; + iterationCount++; + } + }); + sampler.sample().then((state) => { + expect(state.completeSample.length).toBe(2); + expect(state.completeSample[0]).toEqual(mv(0, 1000, {'script': 10})); + expect(state.completeSample[1]).toEqual(mv(1, 1001, {'script': 20})); + done(); + }); + }); - it('should call the validator for every execution and store the valid sample', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const log: any[] = []; - const validSample = [mv(null!, null!, {})]; + it('should call the validator for every execution and store the valid sample', done => { + const log: any[] = []; + const validSample = [mv(null!, null!, {})]; - createSampler({ - metric: createCountingMetric(), - validator: createCountingValidator(2, validSample, log), - execute: EMPTY_EXECUTE - }); - sampler.sample().then((state) => { - expect(state.validSample).toBe(validSample); - // TODO(tbosch): Why does this fail?? - // expect(log).toEqual([ - // ['validate', [{'script': 0}], null], - // ['validate', [{'script': 0}, {'script': 1}], validSample] - // ]); + createSampler({ + metric: createCountingMetric(), + validator: createCountingValidator(2, validSample, log), + execute: EMPTY_EXECUTE + }); + sampler.sample().then((state) => { + expect(state.validSample).toBe(validSample); + // TODO(tbosch): Why does this fail?? + // expect(log).toEqual([ + // ['validate', [{'script': 0}], null], + // ['validate', [{'script': 0}, {'script': 1}], validSample] + // ]); - expect(log.length).toBe(2); - expect(log[0]).toEqual(['validate', [mv(0, 1000, {'script': 0})], null]); - expect(log[1]).toEqual( - ['validate', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample]); + expect(log.length).toBe(2); + expect(log[0]).toEqual(['validate', [mv(0, 1000, {'script': 0})], null]); + expect(log[1]).toEqual( + ['validate', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample]); - async.done(); - }); - })); + done(); + }); + }); - it('should report the metric values', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const log: any[] = []; - const validSample = [mv(null!, null!, {})]; - createSampler({ - validator: createCountingValidator(2, validSample), - metric: createCountingMetric(), - reporter: new MockReporter(log), - execute: EMPTY_EXECUTE - }); - sampler.sample().then((_) => { - // TODO(tbosch): Why does this fail?? - // expect(log).toEqual([ - // ['reportMeasureValues', 0, {'script': 0}], - // ['reportMeasureValues', 1, {'script': 1}], - // ['reportSample', [{'script': 0}, {'script': 1}], validSample] - // ]); - expect(log.length).toBe(3); - expect(log[0]).toEqual(['reportMeasureValues', mv(0, 1000, {'script': 0})]); - expect(log[1]).toEqual(['reportMeasureValues', mv(1, 1001, {'script': 1})]); - expect(log[2]).toEqual([ - 'reportSample', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample - ]); + it('should report the metric values', done => { + const log: any[] = []; + const validSample = [mv(null!, null!, {})]; + createSampler({ + validator: createCountingValidator(2, validSample), + metric: createCountingMetric(), + reporter: new MockReporter(log), + execute: EMPTY_EXECUTE + }); + sampler.sample().then((_) => { + // TODO(tbosch): Why does this fail?? + // expect(log).toEqual([ + // ['reportMeasureValues', 0, {'script': 0}], + // ['reportMeasureValues', 1, {'script': 1}], + // ['reportSample', [{'script': 0}, {'script': 1}], validSample] + // ]); + expect(log.length).toBe(3); + expect(log[0]).toEqual(['reportMeasureValues', mv(0, 1000, {'script': 0})]); + expect(log[1]).toEqual(['reportMeasureValues', mv(1, 1001, {'script': 1})]); + expect(log[2]).toEqual([ + 'reportSample', [mv(0, 1000, {'script': 0}), mv(1, 1001, {'script': 1})], validSample + ]); - async.done(); - }); - })); + done(); + }); + }); }); } diff --git a/packages/benchpress/test/statistic_spec.ts b/packages/benchpress/test/statistic_spec.ts index 2255c5488d90..b2f3fba7b593 100644 --- a/packages/benchpress/test/statistic_spec.ts +++ b/packages/benchpress/test/statistic_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {Statistic} from '../src/statistic'; { diff --git a/packages/benchpress/test/validator/regression_slope_validator_spec.ts b/packages/benchpress/test/validator/regression_slope_validator_spec.ts index d6c20a3e996f..7ea096910967 100644 --- a/packages/benchpress/test/validator/regression_slope_validator_spec.ts +++ b/packages/benchpress/test/validator/regression_slope_validator_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, MeasureValues, RegressionSlopeValidator} from '../../index'; { diff --git a/packages/benchpress/test/validator/size_validator_spec.ts b/packages/benchpress/test/validator/size_validator_spec.ts index 5e7c11c05ab7..852c7c57fc3f 100644 --- a/packages/benchpress/test/validator/size_validator_spec.ts +++ b/packages/benchpress/test/validator/size_validator_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, MeasureValues, SizeValidator} from '../../index'; { diff --git a/packages/benchpress/test/web_driver_extension_spec.ts b/packages/benchpress/test/web_driver_extension_spec.ts index f9c0b274d7cd..9303524c75ce 100644 --- a/packages/benchpress/test/web_driver_extension_spec.ts +++ b/packages/benchpress/test/web_driver_extension_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, Options, WebDriverExtension} from '../index'; (function() { @@ -28,21 +26,19 @@ function createExtension(ids: any[], caps: any) { } describe('WebDriverExtension.provideFirstSupported', () => { - it('should provide the extension that matches the capabilities', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'}).then((m) => { - expect(m.id).toEqual('m2'); - async.done(); - }); - })); + it('should provide the extension that matches the capabilities', done => { + createExtension(['m1', 'm2', 'm3'], {'browser': 'm2'}).then((m) => { + expect(m.id).toEqual('m2'); + done(); + }); + }); - it('should throw if there is no match', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension(['m1'], {'browser': 'm2'}).catch((err) => { - expect(err != null).toBe(true); - async.done(); - }); - })); + it('should throw if there is no match', done => { + createExtension(['m1'], {'browser': 'm2'}).catch((err) => { + expect(err != null).toBe(true); + done(); + }); + }); }); })(); diff --git a/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts b/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts index cce37e50e2be..f9da9e31e8c7 100644 --- a/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts +++ b/packages/benchpress/test/webdriver/chrome_driver_extension_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, fit, inject, it} from '@angular/core/testing/src/testing_internal'; - import {ChromeDriverExtension, Injector, Options, WebDriverAdapter, WebDriverExtension} from '../../index'; import {TraceEventFactory} from '../trace_event_factory'; @@ -54,344 +52,323 @@ import {TraceEventFactory} from '../trace_event_factory'; return extension; } - it('should force gc via window.gc()', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().gc().then((_) => { - expect(log).toEqual([['executeScript', 'window.gc()']]); - async.done(); - }); - })); + it('should force gc via window.gc()', done => { + createExtension().gc().then((_) => { + expect(log).toEqual([['executeScript', 'window.gc()']]); + done(); + }); + }); it('should clear the perf logs and mark the timeline via performance.mark() on the first call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { createExtension().timeBegin('someName').then(() => { expect(log).toEqual([ ['logs', 'performance'], ['executeScript', `performance.mark('someName-bpstart');`] ]); - async.done(); - }); - })); - - it('should mark the timeline via performance.mark() on the second call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const ext = createExtension(); - ext.timeBegin('someName') - .then((_) => { - log.splice(0, log.length); - ext.timeBegin('someName'); - }) - .then(() => { - expect(log).toEqual([['executeScript', `performance.mark('someName-bpstart');`]]); - async.done(); - }); - })); - - it('should mark the timeline via performance.mark()', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().timeEnd('someName', null).then((_) => { - expect(log).toEqual([['executeScript', `performance.mark('someName-bpend');`]]); - async.done(); + done(); }); - })); + }); + + it('should mark the timeline via performance.mark() on the second call', done => { + const ext = createExtension(); + ext.timeBegin('someName') + .then((_) => { + log.splice(0, log.length); + ext.timeBegin('someName'); + }) + .then(() => { + expect(log).toEqual([['executeScript', `performance.mark('someName-bpstart');`]]); + done(); + }); + }); - it('should mark the timeline via performance.mark() with start and end of a test', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().timeEnd('name1', 'name2').then((_) => { - expect(log).toEqual([ - ['executeScript', `performance.mark('name1-bpend');performance.mark('name2-bpstart');`] - ]); - async.done(); - }); - })); - - it('should normalize times to ms and forward ph and pid event properties', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.complete('script', 1.1, 5.5, null), - ]); - async.done(); - }); - })); - - it('should normalize "tdur" to "dur"', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null); - event['tdur'] = 5500; - createExtension([event]).readPerfLog().then((events) => { - expect(events).toEqual([ - normEvents.complete('script', 1.1, 5.5, null), - ]); - async.done(); - }); - })); - - it('should report FunctionCall events as "script"', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chromeTimelineV8Events.start('FunctionCall', 0)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('script', 0), - ]); - async.done(); - }); - })); - - it('should report EvaluateScript events as "script"', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('script', 0), - ]); - async.done(); - }); - })); - - it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([ - chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}), - chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}), - ]) - .readPerfLog() - .then((events) => { - expect(events.length).toEqual(2); - expect(events[0]).toEqual( - normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false})); - expect(events[1]).toEqual( - normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false})); - async.done(); - }); - })); - - it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [ - chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}), - chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}), - ], - ) - .readPerfLog() - .then((events) => { - expect(events.length).toEqual(2); - expect(events[0]).toEqual( - normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true})); - expect(events[1]).toEqual( - normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true})); - async.done(); - }); - })); + it('should mark the timeline via performance.mark()', done => { + createExtension().timeEnd('someName', null).then((_) => { + expect(log).toEqual([['executeScript', `performance.mark('someName-bpend');`]]); + done(); + }); + }); + + it('should mark the timeline via performance.mark() with start and end of a test', done => { + createExtension().timeEnd('name1', 'name2').then((_) => { + expect(log).toEqual([ + ['executeScript', `performance.mark('name1-bpend');performance.mark('name2-bpstart');`] + ]); + done(); + }); + }); + + it('should normalize times to ms and forward ph and pid event properties', done => { + createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.complete('script', 1.1, 5.5, null), + ]); + done(); + }); + }); + + it('should normalize "tdur" to "dur"', done => { + const event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null); + event['tdur'] = 5500; + createExtension([event]).readPerfLog().then((events) => { + expect(events).toEqual([ + normEvents.complete('script', 1.1, 5.5, null), + ]); + done(); + }); + }); + + it('should report FunctionCall events as "script"', done => { + createExtension([chromeTimelineV8Events.start('FunctionCall', 0)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('script', 0), + ]); + done(); + }); + }); + + it('should report EvaluateScript events as "script"', done => { + createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('script', 0), + ]); + done(); + }); + }); + + it('should report minor gc', done => { + createExtension([ + chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}), + chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}), + ]) + .readPerfLog() + .then((events) => { + expect(events.length).toEqual(2); + expect(events[0]).toEqual( + normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false})); + expect(events[1]).toEqual( + normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false})); + done(); + }); + }); + + it('should report major gc', done => { + createExtension( + [ + chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}), + chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}), + ], + ) + .readPerfLog() + .then((events) => { + expect(events.length).toEqual(2); + expect(events[0]).toEqual( + normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true})); + expect(events[1]).toEqual( + normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true})); + done(); + }); + }); ['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => { - it(`should report ${recordType} as "render"`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [ - chrome45TimelineEvents.start(recordType, 1234), - chrome45TimelineEvents.end(recordType, 2345) - ], - ) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('render', 1.234), - normEvents.end('render', 2.345), - ]); - async.done(); - }); - })); + it(`should report ${recordType} as "render"`, done => { + createExtension( + [ + chrome45TimelineEvents.start(recordType, 1234), + chrome45TimelineEvents.end(recordType, 2345) + ], + ) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('render', 1.234), + normEvents.end('render', 2.345), + ]); + done(); + }); + }); }); - it(`should report UpdateLayoutTree as "render"`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [ - chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234), - chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345) - ], - ) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('render', 1.234), - normEvents.end('render', 2.345), - ]); - async.done(); - }); - })); - - it('should ignore FunctionCalls from webdriver', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chromeTimelineV8Events.start( - 'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([]); - async.done(); - }); - })); - - it('should ignore FunctionCalls with empty scriptName', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([]); - async.done(); - }); - })); - - it('should report navigationStart', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([chromeBlinkUserTimingEvents.instant('navigationStart', 1234)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([normEvents.instant('navigationStart', 1.234)]); - async.done(); - }); - })); - - it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [chrome45TimelineEvents.instant( - 'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], - ) - .readPerfLog() - .then((events) => { - expect(events).toEqual( - [normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]); - async.done(); - }); - })); - - it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [chrome45TimelineEvents.instant( - 'ResourceSendRequest', 1234, - {'data': {'url': 'http://here', 'requestMethod': 'GET'}})], - ) - .readPerfLog() - .then((events) => { - expect(events).toEqual([normEvents.instant( - 'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]); - async.done(); - }); - })); + it(`should report UpdateLayoutTree as "render"`, done => { + createExtension( + [ + chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234), + chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345) + ], + ) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('render', 1.234), + normEvents.end('render', 2.345), + ]); + done(); + }); + }); + + it('should ignore FunctionCalls from webdriver', done => { + createExtension([chromeTimelineV8Events.start( + 'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([]); + done(); + }); + }); + + it('should ignore FunctionCalls with empty scriptName', done => { + createExtension( + [chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([]); + done(); + }); + }); + + it('should report navigationStart', done => { + createExtension([chromeBlinkUserTimingEvents.instant('navigationStart', 1234)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([normEvents.instant('navigationStart', 1.234)]); + done(); + }); + }); + + it('should report receivedData', done => { + createExtension( + [chrome45TimelineEvents.instant( + 'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], + ) + .readPerfLog() + .then((events) => { + expect(events).toEqual( + [normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]); + done(); + }); + }); + + it('should report sendRequest', done => { + createExtension( + [chrome45TimelineEvents.instant( + 'ResourceSendRequest', 1234, + {'data': {'url': 'http://here', 'requestMethod': 'GET'}})], + ) + .readPerfLog() + .then((events) => { + expect(events).toEqual([normEvents.instant( + 'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]); + done(); + }); + }); describe('readPerfLog (common)', () => { - it('should execute a dummy script before reading them', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - // TODO(tbosch): This seems to be a bug in ChromeDriver: - // Sometimes it does not report the newest events of the performance log - // to the WebDriver client unless a script is executed... - createExtension([]).readPerfLog().then((_) => { - expect(log).toEqual([['executeScript', '1+1'], ['logs', 'performance']]); - async.done(); - }); - })); + it('should execute a dummy script before reading them', done => { + // TODO(tbosch): This seems to be a bug in ChromeDriver: + // Sometimes it does not report the newest events of the performance log + // to the WebDriver client unless a script is executed... + createExtension([]).readPerfLog().then((_) => { + expect(log).toEqual([['executeScript', '1+1'], ['logs', 'performance']]); + done(); + }); + }); ['Rasterize', 'CompositeLayers'].forEach((recordType) => { - it(`should report ${recordType} as "render"`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [ - chromeTimelineEvents.start(recordType, 1234), - chromeTimelineEvents.end(recordType, 2345) - ], - ) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('render', 1.234), - normEvents.end('render', 2.345), - ]); - async.done(); - }); - })); + it(`should report ${recordType} as "render"`, done => { + createExtension( + [ + chromeTimelineEvents.start(recordType, 1234), + chromeTimelineEvents.end(recordType, 2345) + ], + ) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('render', 1.234), + normEvents.end('render', 2.345), + ]); + done(); + }); + }); }); describe('frame metrics', () => { - it('should report ImplThreadRenderingStats as frame event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([benchmarkEvents.instant( - 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, - {'data': {'frame_count': 1}})]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.instant('frame', 1.1), - ]); - async.done(); - }); - })); - - it('should not report ImplThreadRenderingStats with zero frames', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([benchmarkEvents.instant( - 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, - {'data': {'frame_count': 0}})]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([]); - async.done(); - }); - })); - - it('should throw when ImplThreadRenderingStats contains more than one frame', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([benchmarkEvents.instant( - 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, - {'data': {'frame_count': 2}})]) - .readPerfLog() - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('multi-frame render stats not supported'); - async.done(); - }); - })); + it('should report ImplThreadRenderingStats as frame event', done => { + createExtension([benchmarkEvents.instant( + 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, + {'data': {'frame_count': 1}})]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.instant('frame', 1.1), + ]); + done(); + }); + }); + + it('should not report ImplThreadRenderingStats with zero frames', done => { + createExtension([benchmarkEvents.instant( + 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, + {'data': {'frame_count': 0}})]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([]); + done(); + }); + }); + + it('should throw when ImplThreadRenderingStats contains more than one frame', done => { + createExtension([benchmarkEvents.instant( + 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100, + {'data': {'frame_count': 2}})]) + .readPerfLog() + .catch((err): any => { + expect(() => { + throw err; + }).toThrowError('multi-frame render stats not supported'); + done(); + }); + }); }); - it('should report begin timestamps', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([blinkEvents.create('S', 'someName', 1000)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([normEvents.markStart('someName', 1.0)]); - async.done(); - }); - })); - - it('should report end timestamps', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([blinkEvents.create('F', 'someName', 1000)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([normEvents.markEnd('someName', 1.0)]); - async.done(); - }); - })); - - it('should throw an error on buffer overflow', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension( - [ - chromeTimelineEvents.start('FunctionCall', 1234), - ], - CHROME45_USER_AGENT, 'Tracing.bufferUsage') - .readPerfLog() - .catch((err): any => { - expect(() => { - throw err; - }).toThrowError('The DevTools trace buffer filled during the test!'); - async.done(); - }); - })); + it('should report begin timestamps', done => { + createExtension([blinkEvents.create('S', 'someName', 1000)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([normEvents.markStart('someName', 1.0)]); + done(); + }); + }); + + it('should report end timestamps', done => { + createExtension([blinkEvents.create('F', 'someName', 1000)]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([normEvents.markEnd('someName', 1.0)]); + done(); + }); + }); + + it('should throw an error on buffer overflow', done => { + createExtension( + [ + chromeTimelineEvents.start('FunctionCall', 1234), + ], + CHROME45_USER_AGENT, 'Tracing.bufferUsage') + .readPerfLog() + .catch((err): any => { + expect(() => { + throw err; + }).toThrowError('The DevTools trace buffer filled during the test!'); + done(); + }); + }); it('should match chrome browsers', () => { expect(createExtension().supports({'browserName': 'chrome'})).toBe(true); diff --git a/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts b/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts index 1c5006b46b9d..9d7b9c198b5b 100644 --- a/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts +++ b/packages/benchpress/test/webdriver/ios_driver_extension_spec.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; - import {Injector, IOsDriverExtension, WebDriverAdapter, WebDriverExtension} from '../../index'; import {TraceEventFactory} from '../trace_event_factory'; @@ -37,102 +35,91 @@ import {TraceEventFactory} from '../trace_event_factory'; expect(() => createExtension().gc()).toThrowError('Force GC is not supported on iOS'); }); - it('should mark the timeline via console.time()', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().timeBegin('someName').then((_) => { - expect(log).toEqual([['executeScript', `console.time('someName');`]]); - async.done(); - }); - })); - - it('should mark the timeline via console.timeEnd()', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().timeEnd('someName', null).then((_) => { - expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]); - async.done(); - }); - })); - - it('should mark the timeline via console.time() and console.timeEnd()', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension().timeEnd('name1', 'name2').then((_) => { - expect(log).toEqual( - [['executeScript', `console.timeEnd('name1');console.time('name2');`]]); - async.done(); - }); - })); + it('should mark the timeline via console.time()', done => { + createExtension().timeBegin('someName').then((_) => { + expect(log).toEqual([['executeScript', `console.time('someName');`]]); + done(); + }); + }); + + it('should mark the timeline via console.timeEnd()', done => { + createExtension().timeEnd('someName', null).then((_) => { + expect(log).toEqual([['executeScript', `console.timeEnd('someName');`]]); + done(); + }); + }); + + it('should mark the timeline via console.time() and console.timeEnd()', done => { + createExtension().timeEnd('name1', 'name2').then((_) => { + expect(log).toEqual([['executeScript', `console.timeEnd('name1');console.time('name2');`]]); + done(); + }); + }); describe('readPerfLog', () => { - it('should execute a dummy script before reading them', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - // TODO(tbosch): This seems to be a bug in ChromeDriver: - // Sometimes it does not report the newest events of the performance log - // to the WebDriver client unless a script is executed... - createExtension([]).readPerfLog().then((_) => { - expect(log).toEqual([['executeScript', '1+1'], ['logs', 'performance']]); - async.done(); - }); - })); - - it('should report FunctionCall records as "script"', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([durationRecord('FunctionCall', 1, 5)]).readPerfLog().then((events) => { - expect(events).toEqual([normEvents.start('script', 1), normEvents.end('script', 5)]); - async.done(); - }); - })); - - it('should ignore FunctionCalls from webdriver', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([internalScriptRecord(1, 5)]).readPerfLog().then((events) => { - expect(events).toEqual([]); - async.done(); - }); - })); - - it('should report begin time', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([timeBeginRecord('someName', 12)]).readPerfLog().then((events) => { - expect(events).toEqual([normEvents.markStart('someName', 12)]); - async.done(); - }); - })); - - it('should report end timestamps', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([timeEndRecord('someName', 12)]).readPerfLog().then((events) => { - expect(events).toEqual([normEvents.markEnd('someName', 12)]); - async.done(); - }); - })); + it('should execute a dummy script before reading them', done => { + // TODO(tbosch): This seems to be a bug in ChromeDriver: + // Sometimes it does not report the newest events of the performance log + // to the WebDriver client unless a script is executed... + createExtension([]).readPerfLog().then((_) => { + expect(log).toEqual([['executeScript', '1+1'], ['logs', 'performance']]); + done(); + }); + }); + + it('should report FunctionCall records as "script"', done => { + createExtension([durationRecord('FunctionCall', 1, 5)]).readPerfLog().then((events) => { + expect(events).toEqual([normEvents.start('script', 1), normEvents.end('script', 5)]); + done(); + }); + }); + + it('should ignore FunctionCalls from webdriver', done => { + createExtension([internalScriptRecord(1, 5)]).readPerfLog().then((events) => { + expect(events).toEqual([]); + done(); + }); + }); + + it('should report begin time', done => { + createExtension([timeBeginRecord('someName', 12)]).readPerfLog().then((events) => { + expect(events).toEqual([normEvents.markStart('someName', 12)]); + done(); + }); + }); + + it('should report end timestamps', done => { + createExtension([timeEndRecord('someName', 12)]).readPerfLog().then((events) => { + expect(events).toEqual([normEvents.markEnd('someName', 12)]); + done(); + }); + }); ['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint', 'Rasterize', 'CompositeLayers'] .forEach((recordType) => { - it(`should report ${recordType}`, - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([durationRecord(recordType, 0, 1)]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('render', 0), - normEvents.end('render', 1), - ]); - async.done(); - }); - })); + it(`should report ${recordType}`, done => { + createExtension([durationRecord(recordType, 0, 1)]).readPerfLog().then((events) => { + expect(events).toEqual([ + normEvents.start('render', 0), + normEvents.end('render', 1), + ]); + done(); + }); + }); }); - it('should walk children', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - createExtension([durationRecord('FunctionCall', 1, 5, [timeBeginRecord('someName', 2)])]) - .readPerfLog() - .then((events) => { - expect(events).toEqual([ - normEvents.start('script', 1), normEvents.markStart('someName', 2), - normEvents.end('script', 5) - ]); - async.done(); - }); - })); + it('should walk children', done => { + createExtension([durationRecord('FunctionCall', 1, 5, [timeBeginRecord('someName', 2)])]) + .readPerfLog() + .then((events) => { + expect(events).toEqual([ + normEvents.start('script', 1), normEvents.markStart('someName', 2), + normEvents.end('script', 5) + ]); + done(); + }); + }); it('should match safari browsers', () => { expect(createExtension().supports({'browserName': 'safari'})).toBe(true); diff --git a/packages/common/http/test/client_spec.ts b/packages/common/http/test/client_spec.ts index 193c5f823343..fb0e3e7c13ae 100644 --- a/packages/common/http/test/client_spec.ts +++ b/packages/common/http/test/client_spec.ts @@ -9,7 +9,6 @@ import {HttpClient} from '@angular/common/http/src/client'; import {HttpErrorResponse, HttpEventType, HttpResponse, HttpStatusCode} from '@angular/common/http/src/response'; import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend'; -import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal'; import {toArray} from 'rxjs/operators'; { diff --git a/packages/common/http/test/jsonp_spec.ts b/packages/common/http/test/jsonp_spec.ts index 10b0e6574d4d..0abcf88e2217 100644 --- a/packages/common/http/test/jsonp_spec.ts +++ b/packages/common/http/test/jsonp_spec.ts @@ -9,7 +9,6 @@ import {JSONP_ERR_NO_CALLBACK, JSONP_ERR_WRONG_METHOD, JSONP_ERR_WRONG_RESPONSE_TYPE, JsonpClientBackend} from '@angular/common/http/src/jsonp'; import {HttpRequest} from '@angular/common/http/src/request'; import {HttpErrorResponse, HttpEventType} from '@angular/common/http/src/response'; -import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal'; import {toArray} from 'rxjs/operators'; import {MockDocument} from './jsonp_mock'; diff --git a/packages/common/http/test/request_spec.ts b/packages/common/http/test/request_spec.ts index f1800092e1fd..7fa9577f8ec9 100644 --- a/packages/common/http/test/request_spec.ts +++ b/packages/common/http/test/request_spec.ts @@ -10,7 +10,6 @@ import {HttpContext} from '@angular/common/http/src/context'; import {HttpHeaders} from '@angular/common/http/src/headers'; import {HttpParams} from '@angular/common/http/src/params'; import {HttpRequest} from '@angular/common/http/src/request'; -import {ddescribe, describe, it} from '@angular/core/testing/src/testing_internal'; const TEST_URL = 'https://angular.io/'; const TEST_STRING = `I'm a body!`; diff --git a/packages/common/http/test/response_spec.ts b/packages/common/http/test/response_spec.ts index c9d31174d5d6..0b1bb174826e 100644 --- a/packages/common/http/test/response_spec.ts +++ b/packages/common/http/test/response_spec.ts @@ -8,7 +8,6 @@ import {HttpHeaders} from '@angular/common/http/src/headers'; import {HttpResponse, HttpStatusCode} from '@angular/common/http/src/response'; -import {describe, it} from '@angular/core/testing/src/testing_internal'; { describe('HttpResponse', () => { diff --git a/packages/common/http/test/xhr_spec.ts b/packages/common/http/test/xhr_spec.ts index e8a462a0b181..9c513f3f1bb4 100644 --- a/packages/common/http/test/xhr_spec.ts +++ b/packages/common/http/test/xhr_spec.ts @@ -9,7 +9,6 @@ import {HttpRequest} from '@angular/common/http/src/request'; import {HttpDownloadProgressEvent, HttpErrorResponse, HttpEvent, HttpEventType, HttpHeaderResponse, HttpResponse, HttpResponseBase, HttpStatusCode, HttpUploadProgressEvent} from '@angular/common/http/src/response'; import {HttpXhrBackend} from '@angular/common/http/src/xhr'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {Observable} from 'rxjs'; import {toArray} from 'rxjs/operators'; diff --git a/packages/common/src/directives/ng_for_of.ts b/packages/common/src/directives/ng_for_of.ts index c39daa3c82fa..34dc3783cf8e 100644 --- a/packages/common/src/directives/ng_for_of.ts +++ b/packages/common/src/directives/ng_for_of.ts @@ -141,21 +141,22 @@ export class NgForOf = NgIterable> implements DoCh this._ngForOfDirty = true; } /** - * A function that defines how to track changes for items in the iterable. + * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. * - * When items are added, moved, or removed in the iterable, - * the directive must re-render the appropriate DOM nodes. - * To minimize churn in the DOM, only nodes that have changed - * are re-rendered. + * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object + * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) + * as the key. * - * By default, the change detector assumes that - * the object instance identifies the node in the iterable. - * When this function is supplied, the directive uses - * the result of calling this function to identify the item node, - * rather than the identity of the object itself. + * `NgForOf` uses the computed key to associate items in an iterable with DOM elements + * it produces for these items. * - * The function receives two inputs, - * the iteration index and the associated node data. + * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an + * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a + * primary key), and this iterable could be updated with new object instances that still + * represent the same underlying entity (for example, when data is re-fetched from the server, + * and the iterable is recreated and re-rendered, but most of the data is still the same). + * + * @see `TrackByFunction` */ @Input() set ngForTrackBy(fn: TrackByFunction) { diff --git a/packages/common/test/cookie_spec.ts b/packages/common/test/cookie_spec.ts index 2a83cb7b9cfd..132724700ef1 100644 --- a/packages/common/test/cookie_spec.ts +++ b/packages/common/test/cookie_spec.ts @@ -17,7 +17,6 @@ */ import {parseCookieValue} from '@angular/common/src/cookie'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('cookies', () => { diff --git a/packages/common/test/i18n/format_number_spec.ts b/packages/common/test/i18n/format_number_spec.ts index be5320eea7ab..7043d4222480 100644 --- a/packages/common/test/i18n/format_number_spec.ts +++ b/packages/common/test/i18n/format_number_spec.ts @@ -12,7 +12,6 @@ import localeEn from '@angular/common/locales/en'; import localeEsUS from '@angular/common/locales/es-US'; import localeFr from '@angular/common/locales/fr'; import {ɵDEFAULT_LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; describe('Format number', () => { beforeAll(() => { diff --git a/packages/common/test/pipes/async_pipe_spec.ts b/packages/common/test/pipes/async_pipe_spec.ts index 19cf051ed80a..ef511532407e 100644 --- a/packages/common/test/pipes/async_pipe_spec.ts +++ b/packages/common/test/pipes/async_pipe_spec.ts @@ -7,15 +7,16 @@ */ import {AsyncPipe, ɵgetDOM as getDOM} from '@angular/common'; -import {EventEmitter} from '@angular/core'; -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; +import {ChangeDetectorRef, EventEmitter} from '@angular/core'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {Subscribable, Unsubscribable} from 'rxjs'; -import {SpyChangeDetectorRef} from '../spies'; - { describe('AsyncPipe', () => { + function getChangeDetectorRefSpy() { + return jasmine.createSpyObj('ChangeDetectorRef', ['markForCheck', 'detectChanges']); + } + describe('Observable', () => { // only expose methods from the Subscribable interface, to ensure that // the implementation does not rely on other methods: @@ -33,13 +34,13 @@ import {SpyChangeDetectorRef} from '../spies'; let emitter: EventEmitter; let subscribable: Subscribable; let pipe: AsyncPipe; - let ref: any; + let ref: ChangeDetectorRef&jasmine.SpyObj; const message = {}; beforeEach(() => { emitter = new EventEmitter(); subscribable = wrapSubscribable(emitter); - ref = new SpyChangeDetectorRef(); + ref = getChangeDetectorRefSpy(); pipe = new AsyncPipe(ref); }); @@ -48,32 +49,30 @@ import {SpyChangeDetectorRef} from '../spies'; expect(pipe.transform(subscribable)).toBe(null); }); - it('should return the latest available value', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(subscribable); - emitter.emit(message); + it('should return the latest available value', done => { + pipe.transform(subscribable); + emitter.emit(message); - setTimeout(() => { - expect(pipe.transform(subscribable)).toEqual(message); - async.done(); - }, 0); - })); + setTimeout(() => { + expect(pipe.transform(subscribable)).toEqual(message); + done(); + }, 0); + }); - it('should return same value when nothing has changed since the last call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(subscribable); - emitter.emit(message); + it('should return same value when nothing has changed since the last call', done => { + pipe.transform(subscribable); + emitter.emit(message); - setTimeout(() => { - pipe.transform(subscribable); - expect(pipe.transform(subscribable)).toBe(message); - async.done(); - }, 0); - })); + setTimeout(() => { + pipe.transform(subscribable); + expect(pipe.transform(subscribable)).toBe(message); + done(); + }, 0); + }); it('should dispose of the existing subscription when subscribing to a new observable', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { pipe.transform(subscribable); const newEmitter = new EventEmitter(); @@ -84,20 +83,19 @@ import {SpyChangeDetectorRef} from '../spies'; // this should not affect the pipe setTimeout(() => { expect(pipe.transform(newSubscribable)).toBe(null); - async.done(); + done(); }, 0); - })); + }); - it('should request a change detection check upon receiving a new value', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(subscribable); - emitter.emit(message); + it('should request a change detection check upon receiving a new value', done => { + pipe.transform(subscribable); + emitter.emit(message); - setTimeout(() => { - expect(ref.spy('markForCheck')).toHaveBeenCalled(); - async.done(); - }, 10); - })); + setTimeout(() => { + expect(ref.markForCheck).toHaveBeenCalled(); + done(); + }, 10); + }); it('should return value for unchanged NaN', () => { emitter.emit(null); @@ -115,23 +113,22 @@ import {SpyChangeDetectorRef} from '../spies'; expect(() => pipe.ngOnDestroy()).not.toThrow(); }); - it('should dispose of the existing subscription', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(subscribable); - pipe.ngOnDestroy(); - emitter.emit(message); + it('should dispose of the existing subscription', done => { + pipe.transform(subscribable); + pipe.ngOnDestroy(); + emitter.emit(message); - setTimeout(() => { - expect(pipe.transform(subscribable)).toBe(null); - async.done(); - }, 0); - })); + setTimeout(() => { + expect(pipe.transform(subscribable)).toBe(null); + done(); + }, 0); + }); }); }); describe('Subscribable', () => { it('should infer the type from the subscribable', () => { - const ref = new SpyChangeDetectorRef() as any; + const ref = getChangeDetectorRefSpy(); const pipe = new AsyncPipe(ref); const emitter = new EventEmitter<{name: 'T'}>(); // The following line will fail to compile if the type cannot be inferred. @@ -145,7 +142,7 @@ import {SpyChangeDetectorRef} from '../spies'; let resolve: (result: any) => void; let reject: (error: any) => void; let promise: Promise; - let ref: SpyChangeDetectorRef; + let ref: any; // adds longer timers for passing tests in IE const timer = (getDOM() && browserDetection.isIE) ? 50 : 10; @@ -154,8 +151,8 @@ import {SpyChangeDetectorRef} from '../spies'; resolve = res; reject = rej; }); - ref = new SpyChangeDetectorRef(); - pipe = new AsyncPipe(ref as any); + ref = getChangeDetectorRefSpy(); + pipe = new AsyncPipe(ref); }); describe('transform', () => { @@ -163,32 +160,30 @@ import {SpyChangeDetectorRef} from '../spies'; expect(pipe.transform(promise)).toBe(null); }); - it('should return the latest available value', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(promise); + it('should return the latest available value', done => { + pipe.transform(promise); - resolve(message); + resolve(message); - setTimeout(() => { - expect(pipe.transform(promise)).toEqual(message); - async.done(); - }, timer); - })); + setTimeout(() => { + expect(pipe.transform(promise)).toEqual(message); + done(); + }, timer); + }); - it('should return value when nothing has changed since the last call', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(promise); - resolve(message); + it('should return value when nothing has changed since the last call', done => { + pipe.transform(promise); + resolve(message); - setTimeout(() => { - pipe.transform(promise); - expect(pipe.transform(promise)).toBe(message); - async.done(); - }, timer); - })); + setTimeout(() => { + pipe.transform(promise); + expect(pipe.transform(promise)).toBe(message); + done(); + }, timer); + }); it('should dispose of the existing subscription when subscribing to a new promise', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { pipe.transform(promise); promise = new Promise(() => {}); @@ -198,41 +193,38 @@ import {SpyChangeDetectorRef} from '../spies'; setTimeout(() => { expect(pipe.transform(promise)).toBe(null); - async.done(); + done(); }, timer); - })); + }); - it('should request a change detection check upon receiving a new value', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const markForCheck = ref.spy('markForCheck'); - pipe.transform(promise); - resolve(message); + it('should request a change detection check upon receiving a new value', done => { + pipe.transform(promise); + resolve(message); - setTimeout(() => { - expect(markForCheck).toHaveBeenCalled(); - async.done(); - }, timer); - })); + setTimeout(() => { + expect(ref.markForCheck).toHaveBeenCalled(); + done(); + }, timer); + }); describe('ngOnDestroy', () => { it('should do nothing when no source', () => { expect(() => pipe.ngOnDestroy()).not.toThrow(); }); - it('should dispose of the existing source', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - pipe.transform(promise); - expect(pipe.transform(promise)).toBe(null); - resolve(message); + it('should dispose of the existing source', done => { + pipe.transform(promise); + expect(pipe.transform(promise)).toBe(null); + resolve(message); - setTimeout(() => { - expect(pipe.transform(promise)).toEqual(message); - pipe.ngOnDestroy(); - expect(pipe.transform(promise)).toBe(null); - async.done(); - }, timer); - })); + setTimeout(() => { + expect(pipe.transform(promise)).toEqual(message); + pipe.ngOnDestroy(); + expect(pipe.transform(promise)).toBe(null); + done(); + }, timer); + }); }); }); }); diff --git a/packages/common/test/pipes/i18n_plural_pipe_spec.ts b/packages/common/test/pipes/i18n_plural_pipe_spec.ts index 19757f367d14..a78defb346fa 100644 --- a/packages/common/test/pipes/i18n_plural_pipe_spec.ts +++ b/packages/common/test/pipes/i18n_plural_pipe_spec.ts @@ -8,7 +8,6 @@ import {I18nPluralPipe, NgLocalization} from '@angular/common'; import {PipeResolver} from '@angular/compiler/src/pipe_resolver'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; { diff --git a/packages/common/test/pipes/number_pipe_spec.ts b/packages/common/test/pipes/number_pipe_spec.ts index 249cc57eeedb..fcef65caef8d 100644 --- a/packages/common/test/pipes/number_pipe_spec.ts +++ b/packages/common/test/pipes/number_pipe_spec.ts @@ -14,7 +14,6 @@ import localeEn from '@angular/common/locales/en'; import localeEsUS from '@angular/common/locales/es-US'; import localeFr from '@angular/common/locales/fr'; import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('Number pipes', () => { diff --git a/packages/common/test/spies.ts b/packages/common/test/spies.ts deleted file mode 100644 index 1f5290bb456b..000000000000 --- a/packages/common/test/spies.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @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 - */ - -import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -export class SpyChangeDetectorRef extends SpyObject { - constructor() { - super(ChangeDetectorRef); - this.spy('markForCheck'); - } -} - -export class SpyNgControl extends SpyObject {} - -export class SpyValueAccessor extends SpyObject { - writeValue: any; -} diff --git a/packages/common/test/viewport_scroller_spec.ts b/packages/common/test/viewport_scroller_spec.ts index 68c0518e3627..404731f242dc 100644 --- a/packages/common/test/viewport_scroller_spec.ts +++ b/packages/common/test/viewport_scroller_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {BrowserViewportScroller, ViewportScroller} from '../src/viewport_scroller'; diff --git a/packages/compiler/test/i18n/integration_common.ts b/packages/compiler/test/i18n/integration_common.ts index 2cefca7946d7..63bfb6e0dd9d 100644 --- a/packages/compiler/test/i18n/integration_common.ts +++ b/packages/compiler/test/i18n/integration_common.ts @@ -11,14 +11,13 @@ import {Serializer} from '@angular/compiler/src/i18n'; import {MessageBundle} from '@angular/compiler/src/i18n/message_bundle'; import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; +import {ResourceLoader} from '@angular/compiler/src/resource_loader'; import {Component, DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {expect} from '@angular/platform-browser/testing/src/matchers'; -import {SpyResourceLoader} from '../spies'; - @Component({ selector: 'i18n-cmp', template: '', @@ -180,7 +179,7 @@ export const HTML = ` export async function configureCompiler(translationsToMerge: string, format: string) { TestBed.configureCompiler({ providers: [ - SpyResourceLoader.PROVIDE, + {provide: ResourceLoader, useValue: jasmine.createSpyObj('ResourceLoader', ['get'])}, FrLocalization.PROVIDE, {provide: TRANSLATIONS, useValue: translationsToMerge}, {provide: TRANSLATIONS_FORMAT, useValue: format}, diff --git a/packages/compiler/test/ng_module_resolver_mock_spec.ts b/packages/compiler/test/ng_module_resolver_mock_spec.ts index cb6ea2feeef2..487b7ed4e293 100644 --- a/packages/compiler/test/ng_module_resolver_mock_spec.ts +++ b/packages/compiler/test/ng_module_resolver_mock_spec.ts @@ -6,8 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Injector, NgModule} from '@angular/core'; -import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; +import {NgModule} from '@angular/core'; import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector'; import {MockNgModuleResolver} from '../testing'; @@ -16,9 +15,9 @@ import {MockNgModuleResolver} from '../testing'; describe('MockNgModuleResolver', () => { let ngModuleResolver: MockNgModuleResolver; - beforeEach(inject([Injector], (injector: Injector) => { + beforeEach(() => { ngModuleResolver = new MockNgModuleResolver(new JitReflector()); - })); + }); describe('NgModule overriding', () => { it('should fallback to the default NgModuleResolver when templates are not overridden', diff --git a/packages/compiler/test/resource_loader_mock_spec.ts b/packages/compiler/test/resource_loader_mock_spec.ts index 4d19081d99b3..6723fa5d2e64 100644 --- a/packages/compiler/test/resource_loader_mock_spec.ts +++ b/packages/compiler/test/resource_loader_mock_spec.ts @@ -7,7 +7,6 @@ */ import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock'; -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; { describe('MockResourceLoader', () => { @@ -42,41 +41,37 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang request.then(onResponse, onError); } - it('should return a response from the definitions', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const url = '/foo'; - const response = 'bar'; - resourceLoader.when(url, response); - expectResponse(resourceLoader.get(url), url, response, () => async.done()); - resourceLoader.flush(); - })); + it('should return a response from the definitions', done => { + const url = '/foo'; + const response = 'bar'; + resourceLoader.when(url, response); + expectResponse(resourceLoader.get(url), url, response, () => done()); + resourceLoader.flush(); + }); - it('should return an error from the definitions', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const url = '/foo'; - const response: string = null!; - resourceLoader.when(url, response); - expectResponse(resourceLoader.get(url), url, response, () => async.done()); - resourceLoader.flush(); - })); + it('should return an error from the definitions', done => { + const url = '/foo'; + const response: string = null!; + resourceLoader.when(url, response); + expectResponse(resourceLoader.get(url), url, response, () => done()); + resourceLoader.flush(); + }); - it('should return a response from the expectations', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const url = '/foo'; - const response = 'bar'; - resourceLoader.expect(url, response); - expectResponse(resourceLoader.get(url), url, response, () => async.done()); - resourceLoader.flush(); - })); + it('should return a response from the expectations', done => { + const url = '/foo'; + const response = 'bar'; + resourceLoader.expect(url, response); + expectResponse(resourceLoader.get(url), url, response, () => done()); + resourceLoader.flush(); + }); - it('should return an error from the expectations', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const url = '/foo'; - const response: string = null!; - resourceLoader.expect(url, response); - expectResponse(resourceLoader.get(url), url, response, () => async.done()); - resourceLoader.flush(); - })); + it('should return an error from the expectations', done => { + const url = '/foo'; + const response: string = null!; + resourceLoader.expect(url, response); + expectResponse(resourceLoader.get(url), url, response, () => done()); + resourceLoader.flush(); + }); it('should not reuse expectations', () => { const url = '/foo'; @@ -89,15 +84,14 @@ import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@ang }).toThrowError('Unexpected request /foo'); }); - it('should return expectations before definitions', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const url = '/foo'; - resourceLoader.when(url, 'when'); - resourceLoader.expect(url, 'expect'); - expectResponse(resourceLoader.get(url), url, 'expect'); - expectResponse(resourceLoader.get(url), url, 'when', () => async.done()); - resourceLoader.flush(); - })); + it('should return expectations before definitions', done => { + const url = '/foo'; + resourceLoader.when(url, 'when'); + resourceLoader.expect(url, 'expect'); + expectResponse(resourceLoader.get(url), url, 'expect'); + expectResponse(resourceLoader.get(url), url, 'when', () => done()); + resourceLoader.flush(); + }); it('should throw when there is no definitions or expectations', () => { resourceLoader.get('/foo'); diff --git a/packages/compiler/test/runtime_compiler_spec.ts b/packages/compiler/test/runtime_compiler_spec.ts index ce37f36e9fae..c0fbe40cd7aa 100644 --- a/packages/compiler/test/runtime_compiler_spec.ts +++ b/packages/compiler/test/runtime_compiler_spec.ts @@ -13,8 +13,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {MockDirectiveResolver} from '../testing'; -import {SpyResourceLoader} from './spies'; - @Component({selector: 'child-cmp'}) class ChildComp { } @@ -91,22 +89,20 @@ class SomeCompWithUrlTemplate { describe('RuntimeCompiler', () => { let compiler: Compiler; - let resourceLoader: SpyResourceLoader; + let resourceLoader: {get: jasmine.Spy}; let dirResolver: MockDirectiveResolver; - let injector: Injector; beforeEach(() => { - TestBed.configureCompiler({providers: [SpyResourceLoader.PROVIDE]}); + resourceLoader = jasmine.createSpyObj('ResourceLoader', ['get']); + TestBed.configureCompiler({providers: [{provide: ResourceLoader, useValue: resourceLoader}]}); }); beforeEach(fakeAsync(inject( [Compiler, ResourceLoader, DirectiveResolver, Injector], - (_compiler: Compiler, _resourceLoader: SpyResourceLoader, - _dirResolver: MockDirectiveResolver, _injector: Injector) => { + (_compiler: Compiler, _resourceLoader: any, _dirResolver: MockDirectiveResolver) => { compiler = _compiler; resourceLoader = _resourceLoader; dirResolver = _dirResolver; - injector = _injector; }))); describe('compileModuleAsync', () => { @@ -118,7 +114,7 @@ class SomeCompWithUrlTemplate { class SomeModule { } - resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello')); + resourceLoader.get.and.callFake(() => Promise.resolve('hello')); let ngModuleFactory: NgModuleFactory = undefined!; compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f); tick(); @@ -133,7 +129,7 @@ class SomeCompWithUrlTemplate { class SomeModule { } - resourceLoader.spy('get').and.callFake(() => Promise.resolve('')); + resourceLoader.get.and.callFake(() => Promise.resolve('')); expect(() => compiler.compileModuleSync(SomeModule)) .toThrowError(`Can't compile synchronously as ${ stringify(SomeCompWithUrlTemplate)} is still being loaded!`); @@ -145,7 +141,7 @@ class SomeCompWithUrlTemplate { class SomeModule { } - resourceLoader.spy('get').and.callFake(() => Promise.resolve('')); + resourceLoader.get.and.callFake(() => Promise.resolve('')); dirResolver.setDirective(SomeComp, new Component({selector: 'some-cmp', template: ''})); dirResolver.setDirective( ChildComp, new Component({selector: 'child-cmp', templateUrl: '/someTpl.html'})); @@ -163,7 +159,7 @@ class SomeCompWithUrlTemplate { class SomeModule { } - resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello')); + resourceLoader.get.and.callFake(() => Promise.resolve('hello')); compiler.compileModuleAsync(SomeModule); tick(); diff --git a/packages/compiler/test/schema/dom_element_schema_registry_spec.ts b/packages/compiler/test/schema/dom_element_schema_registry_spec.ts index 425a95c7a1fd..c4812c7b2474 100644 --- a/packages/compiler/test/schema/dom_element_schema_registry_spec.ts +++ b/packages/compiler/test/schema/dom_element_schema_registry_spec.ts @@ -8,7 +8,6 @@ import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SecurityContext} from '@angular/core'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {Element} from '../../src/ml_parser/ast'; diff --git a/packages/compiler/test/schema/trusted_types_sinks_spec.ts b/packages/compiler/test/schema/trusted_types_sinks_spec.ts index 49023439a770..16504814a6f1 100644 --- a/packages/compiler/test/schema/trusted_types_sinks_spec.ts +++ b/packages/compiler/test/schema/trusted_types_sinks_spec.ts @@ -7,7 +7,6 @@ */ import {isTrustedTypesSink} from '@angular/compiler/src/schema/trusted_types_sinks'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('isTrustedTypesSink', () => { diff --git a/packages/compiler/test/spies.ts b/packages/compiler/test/spies.ts deleted file mode 100644 index 579981722692..000000000000 --- a/packages/compiler/test/spies.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @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 - */ - -import {ResourceLoader} from '@angular/compiler/src/resource_loader'; - -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -export class SpyResourceLoader extends SpyObject { - public static PROVIDE = {provide: ResourceLoader, useClass: SpyResourceLoader, deps: []}; - constructor() { - super(ResourceLoader); - } -} diff --git a/packages/compiler/test/template_parser/template_preparser_spec.ts b/packages/compiler/test/template_parser/template_preparser_spec.ts index e43efd132207..f07caa14fb35 100644 --- a/packages/compiler/test/template_parser/template_preparser_spec.ts +++ b/packages/compiler/test/template_parser/template_preparser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {beforeEach, describe, expect, inject, it} from '../../../core/testing/src/testing_internal'; +import {inject} from '../../../core/testing'; import {Element} from '../../src/ml_parser/ast'; import {HtmlParser} from '../../src/ml_parser/html_parser'; import {PreparsedElement, PreparsedElementType, preparseElement} from '../../src/template_parser/template_preparser'; diff --git a/packages/compiler/test/url_resolver_spec.ts b/packages/compiler/test/url_resolver_spec.ts index c0b134956757..ffbd0f437941 100644 --- a/packages/compiler/test/url_resolver_spec.ts +++ b/packages/compiler/test/url_resolver_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {createOfflineCompileUrlResolver, UrlResolver} from '@angular/compiler/src/url_resolver'; -import {beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; +import {UrlResolver} from '@angular/compiler/src/url_resolver'; +import {inject} from '@angular/core/testing'; { describe('UrlResolver', () => { diff --git a/packages/core/src/change_detection/differs/iterable_differs.ts b/packages/core/src/change_detection/differs/iterable_differs.ts index edb6b1e3b087..e05f94cd56ea 100644 --- a/packages/core/src/change_detection/differs/iterable_differs.ts +++ b/packages/core/src/change_detection/differs/iterable_differs.ts @@ -113,14 +113,52 @@ export interface IterableChangeRecord { } /** - * An optional function passed into the `NgForOf` directive that defines how to track - * changes for items in an iterable. - * The function takes the iteration index and item ID. - * When supplied, Angular tracks changes by the return value of the function. + * A function optionally passed into the `NgForOf` directive to customize how `NgForOf` uniquely + * identifies items in an iterable. * + * `NgForOf` needs to uniquely identify items in the iterable to correctly perform DOM updates + * when items in the iterable are reordered, new items are added, or existing items are removed. + * + * + * In all of these scenarios it is usually desirable to only update the DOM elements associated + * with the items affected by the change. This behavior is important to: + * + * - preserve any DOM-specific UI state (like cursor position, focus, text selection) when the + * iterable is modified + * - enable animation of item addition, removal, and iterable reordering + * + * A common use for custom `trackBy` functions is when the model that `NgForOf` iterates over + * contains a property with a unique identifier. For example, given a model: + * + * ```ts + * class User { + * id: number; + * name: string; + * ... + * } + * ``` + * a custom `trackBy` function could look like the following: + * ```ts + * function userTrackBy(index, user) { + * return user.id; + * } + * ``` + * + * A custom `trackBy` function must have several properties: + * + * - be [idempotent](https://en.wikipedia.org/wiki/Idempotence) (be without side effects, and always + * return the same value for a given input) + * - return unique value for all unique inputs + * - be fast + * + * @see [`NgForOf#ngForTrackBy`](api/common/NgForOf#ngForTrackBy) * @publicApi */ export interface TrackByFunction { + /** + * @param index The index of the item within the iterable. + * @param item The item in the iterable. + */ (index: number, item: T): any; } diff --git a/packages/core/src/di/injectable.ts b/packages/core/src/di/injectable.ts index 7b1adf97c34d..e3f7f305179c 100644 --- a/packages/core/src/di/injectable.ts +++ b/packages/core/src/di/injectable.ts @@ -64,9 +64,15 @@ export interface InjectableDecorator { */ export interface Injectable { /** - * Determines which injectors will provide the injectable, - * by either associating it with an `@NgModule` or other `InjectorType`, - * or by specifying that this injectable should be provided in one of the following injectors: + * Determines which injectors will provide the injectable. + * + * - `Type` - associates the injectable with an `@NgModule` or other `InjectorType`, + * - 'null' : Equivalent to `undefined`. The injectable is not provided in any scope automatically + * and must be added to a `providers` array of an [@NgModule](api/core/NgModule#providers), + * [@Component](api/core/Directive#providers) or [@Directive](api/core/Directive#providers). + * + * The following options specify that this injectable should be provided in one of the following + * injectors: * - 'root' : The application-level injector in most apps. * - 'platform' : A special singleton platform injector shared by all * applications on the page. diff --git a/packages/core/src/metadata/do_boostrap.ts b/packages/core/src/metadata/do_boostrap.ts index 17190fceb2c9..09c1614afbf5 100644 --- a/packages/core/src/metadata/do_boostrap.ts +++ b/packages/core/src/metadata/do_boostrap.ts @@ -11,8 +11,8 @@ import {ApplicationRef} from '../application_ref'; /** * @description - * Hook for manual bootstrapping of the application instead of using bootstrap array in @NgModule - * annotation. + * Hook for manual bootstrapping of the application instead of using `bootstrap` array in @NgModule + * annotation. This hook is invoked only when the `bootstrap` array is empty or not provided. * * Reference to the current application is provided as a parameter. * diff --git a/packages/core/test/acceptance/bootstrap_spec.ts b/packages/core/test/acceptance/bootstrap_spec.ts index edd8eaa43d25..0f96eda3e0c6 100644 --- a/packages/core/test/acceptance/bootstrap_spec.ts +++ b/packages/core/test/acceptance/bootstrap_spec.ts @@ -7,7 +7,6 @@ */ import {ApplicationRef, COMPILER_OPTIONS, Component, destroyPlatform, NgModule, NgZone, TestabilityRegistry, ViewEncapsulation} from '@angular/core'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {onlyInIvy, withBody} from '@angular/private/testing'; diff --git a/packages/core/test/acceptance/discover_utils_spec.ts b/packages/core/test/acceptance/discover_utils_spec.ts index a1d589eabe75..513bb26065cc 100644 --- a/packages/core/test/acceptance/discover_utils_spec.ts +++ b/packages/core/test/acceptance/discover_utils_spec.ts @@ -14,7 +14,6 @@ import {isLView} from '@angular/core/src/render3/interfaces/type_checks'; import {CONTEXT} from '@angular/core/src/render3/interfaces/view'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {getElementStyles} from '@angular/core/testing/src/styling'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {onlyInIvy} from '@angular/private/testing'; import {getLContext} from '../../src/render3/context_discovery'; diff --git a/packages/core/test/acceptance/profiler_spec.ts b/packages/core/test/acceptance/profiler_spec.ts index c9b4177af418..1c07528cb557 100644 --- a/packages/core/test/acceptance/profiler_spec.ts +++ b/packages/core/test/acceptance/profiler_spec.ts @@ -8,7 +8,6 @@ import {ProfilerEvent, setProfiler} from '@angular/core/src/render3/profiler'; import {TestBed} from '@angular/core/testing'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {onlyInIvy} from '@angular/private/testing'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, DoCheck, ErrorHandler, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild} from '../../src/core'; diff --git a/packages/core/test/application_module_spec.ts b/packages/core/test/application_module_spec.ts index a802cb6aed18..d4bf51945a06 100644 --- a/packages/core/test/application_module_spec.ts +++ b/packages/core/test/application_module_spec.ts @@ -7,12 +7,12 @@ */ import {DEFAULT_CURRENCY_CODE, LOCALE_ID} from '@angular/core'; +import {inject} from '@angular/core/testing'; import {ivyEnabled} from '@angular/private/testing'; import {getLocaleId} from '../src/render3'; import {global} from '../src/util/global'; import {TestBed} from '../testing'; -import {describe, expect, inject, it} from '../testing/src/testing_internal'; { describe('Application module', () => { diff --git a/packages/core/test/change_detection/differs/iterable_differs_spec.ts b/packages/core/test/change_detection/differs/iterable_differs_spec.ts index 19cdd0e691d3..396d09b22990 100644 --- a/packages/core/test/change_detection/differs/iterable_differs_spec.ts +++ b/packages/core/test/change_detection/differs/iterable_differs_spec.ts @@ -9,8 +9,6 @@ import {Injector, IterableDiffer, IterableDifferFactory, IterableDiffers, NgModule, TrackByFunction} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {SpyIterableDifferFactory} from '../../spies'; - { describe('IterableDiffers', function() { let factory1: any; @@ -18,9 +16,10 @@ import {SpyIterableDifferFactory} from '../../spies'; let factory3: any; beforeEach(() => { - factory1 = new SpyIterableDifferFactory(); - factory2 = new SpyIterableDifferFactory(); - factory3 = new SpyIterableDifferFactory(); + const getFactory = () => jasmine.createSpyObj('IterableDifferFactory', ['supports']); + factory1 = getFactory(); + factory2 = getFactory(); + factory3 = getFactory(); }); it('should throw when no suitable implementation found', () => { @@ -30,17 +29,17 @@ import {SpyIterableDifferFactory} from '../../spies'; }); it('should return the first suitable implementation', () => { - factory1.spy('supports').and.returnValue(false); - factory2.spy('supports').and.returnValue(true); - factory3.spy('supports').and.returnValue(true); + factory1.supports.and.returnValue(false); + factory2.supports.and.returnValue(true); + factory3.supports.and.returnValue(true); const differs = IterableDiffers.create([factory1, factory2, factory3]); expect(differs.find('some object')).toBe(factory2); }); it('should copy over differs from the parent repo', () => { - factory1.spy('supports').and.returnValue(true); - factory2.spy('supports').and.returnValue(false); + factory1.supports.and.returnValue(true); + factory2.supports.and.returnValue(false); const parent = IterableDiffers.create([factory1]); const child = IterableDiffers.create([factory2], parent); diff --git a/packages/core/test/di/forward_ref_spec.ts b/packages/core/test/di/forward_ref_spec.ts index 81c0dd0364bf..cc156bf6373e 100644 --- a/packages/core/test/di/forward_ref_spec.ts +++ b/packages/core/test/di/forward_ref_spec.ts @@ -8,7 +8,6 @@ import {Type} from '@angular/core'; import {forwardRef, resolveForwardRef} from '@angular/core/src/di'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('forwardRef', function() { diff --git a/packages/core/test/di/injector_spec.ts b/packages/core/test/di/injector_spec.ts index a1dab42636c7..c72103b806df 100644 --- a/packages/core/test/di/injector_spec.ts +++ b/packages/core/test/di/injector_spec.ts @@ -8,7 +8,6 @@ import {Injector} from '@angular/core'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('Injector.NULL', () => { diff --git a/packages/core/test/dom/dom_adapter_spec.ts b/packages/core/test/dom/dom_adapter_spec.ts index 859b12a7160e..97172b6c50c3 100644 --- a/packages/core/test/dom/dom_adapter_spec.ts +++ b/packages/core/test/dom/dom_adapter_spec.ts @@ -7,7 +7,6 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {isTextNode} from '@angular/platform-browser/testing/src/browser_util'; { diff --git a/packages/core/test/dom/shim_spec.ts b/packages/core/test/dom/shim_spec.ts index deb135b11fe6..9f39856cef90 100644 --- a/packages/core/test/dom/shim_spec.ts +++ b/packages/core/test/dom/shim_spec.ts @@ -6,7 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; +// This isn't used for anything, but for some reason Bazel won't +// serve the file if there isn't at least one import. +import '@angular/core/testing'; { describe('Shim', () => { diff --git a/packages/core/test/event_emitter_spec.ts b/packages/core/test/event_emitter_spec.ts index e822a624851a..3715b0fc1b35 100644 --- a/packages/core/test/event_emitter_spec.ts +++ b/packages/core/test/event_emitter_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; import {filter} from 'rxjs/operators'; import {EventEmitter} from '../src/event_emitter'; @@ -19,51 +18,47 @@ import {EventEmitter} from '../src/event_emitter'; emitter = new EventEmitter(); }); - it('should call the next callback', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe({ - next: (value: any) => { - expect(value).toEqual(99); - async.done(); - } - }); - emitter.emit(99); - })); - - it('should call the throw callback', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe({ - next: () => {}, - error: (error: any) => { - expect(error).toEqual('Boom'); - async.done(); - } - }); - emitter.error('Boom'); - })); - - it('should work when no throw callback is provided', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe({ - next: () => {}, - error: (_: any) => { - async.done(); - } - }); - emitter.error('Boom'); - })); - - it('should call the return callback', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - emitter.subscribe({ - next: () => {}, - error: (_: any) => {}, - complete: () => { - async.done(); - } - }); - emitter.complete(); - })); + it('should call the next callback', done => { + emitter.subscribe({ + next: (value: any) => { + expect(value).toEqual(99); + done(); + } + }); + emitter.emit(99); + }); + + it('should call the throw callback', done => { + emitter.subscribe({ + next: () => {}, + error: (error: any) => { + expect(error).toEqual('Boom'); + done(); + } + }); + emitter.error('Boom'); + }); + + it('should work when no throw callback is provided', done => { + emitter.subscribe({ + next: () => {}, + error: (_: any) => { + done(); + } + }); + emitter.error('Boom'); + }); + + it('should call the return callback', done => { + emitter.subscribe({ + next: () => {}, + error: (_: any) => {}, + complete: () => { + done(); + } + }); + emitter.complete(); + }); it('should subscribe to the wrapper synchronously', () => { let called = false; @@ -77,27 +72,26 @@ import {EventEmitter} from '../src/event_emitter'; expect(called).toBe(true); }); - it('delivers next and error events synchronously', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const log: any[] /** TODO #9100 */ = []; - - emitter.subscribe({ - next: (x: any) => { - log.push(x); - expect(log).toEqual([1, 2]); - }, - error: (err: any) => { - log.push(err); - expect(log).toEqual([1, 2, 3, 4]); - async.done(); - } - }); - log.push(1); - emitter.emit(2); - log.push(3); - emitter.error(4); - log.push(5); - })); + it('delivers next and error events synchronously', done => { + const log: any[] /** TODO #9100 */ = []; + + emitter.subscribe({ + next: (x: any) => { + log.push(x); + expect(log).toEqual([1, 2]); + }, + error: (err: any) => { + log.push(err); + expect(log).toEqual([1, 2, 3, 4]); + done(); + } + }); + log.push(1); + emitter.emit(2); + log.push(3); + emitter.error(4); + log.push(5); + }); it('delivers next and complete events synchronously', () => { const log: any[] /** TODO #9100 */ = []; @@ -121,19 +115,18 @@ import {EventEmitter} from '../src/event_emitter'; expect(log).toEqual([1, 2, 3, 4, 5]); }); - it('delivers events asynchronously when forced to async mode', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const e = new EventEmitter(true); - const log: any[] /** TODO #9100 */ = []; - e.subscribe((x: any) => { - log.push(x); - expect(log).toEqual([1, 3, 2]); - async.done(); - }); - log.push(1); - e.emit(2); - log.push(3); - })); + it('delivers events asynchronously when forced to async mode', done => { + const e = new EventEmitter(true); + const log: any[] /** TODO #9100 */ = []; + e.subscribe((x: any) => { + log.push(x); + expect(log).toEqual([1, 3, 2]); + done(); + }); + log.push(1); + e.emit(2); + log.push(3); + }); it('reports whether it has subscribers', () => { const e = new EventEmitter(false); @@ -156,21 +149,17 @@ import {EventEmitter} from '../src/event_emitter'; expect(emitter.observers.length).toBe(0); }); - it('unsubscribing a subscriber invokes the dispose method', () => { - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const sub = emitter.subscribe(); - sub.add(() => async.done()); - sub.unsubscribe(); - }); + it('unsubscribing a subscriber invokes the dispose method', done => { + const sub = emitter.subscribe(); + sub.add(() => done()); + sub.unsubscribe(); }); it('unsubscribing a subscriber after applying operators with pipe() invokes the dispose method', - () => { - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const sub = emitter.pipe(filter(() => true)).subscribe(); - sub.add(() => async.done()); - sub.unsubscribe(); - }); + done => { + const sub = emitter.pipe(filter(() => true)).subscribe(); + sub.add(() => done()); + sub.unsubscribe(); }); it('error thrown inside an Rx chain propagates to the error handler and disposes the chain', diff --git a/packages/core/test/fake_async_spec.ts b/packages/core/test/fake_async_spec.ts index 54b494f84edb..705d34669ca5 100644 --- a/packages/core/test/fake_async_spec.ts +++ b/packages/core/test/fake_async_spec.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, tick} from '@angular/core/testing'; -import {beforeEach, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal'; +import {discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, inject, tick} from '@angular/core/testing'; +import {Log} from '@angular/core/testing/src/testing_internal'; import {EventManager} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; diff --git a/packages/core/test/linker/inheritance_integration_spec.ts b/packages/core/test/linker/inheritance_integration_spec.ts index 3b9efcf117c5..a02b610d272e 100644 --- a/packages/core/test/linker/inheritance_integration_spec.ts +++ b/packages/core/test/linker/inheritance_integration_spec.ts @@ -8,7 +8,6 @@ import {Component, Directive, HostBinding} from '@angular/core'; import {TestBed} from '@angular/core/testing'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; @Directive({selector: '[directiveA]'}) diff --git a/packages/core/test/linker/query_list_spec.ts b/packages/core/test/linker/query_list_spec.ts index c5b1fb70704a..9acfaf7ac8f4 100644 --- a/packages/core/test/linker/query_list_spec.ts +++ b/packages/core/test/linker/query_list_spec.ts @@ -10,7 +10,6 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {iterateListLike} from '@angular/core/src/change_detection/change_detection_util'; import {QueryList} from '@angular/core/src/linker/query_list'; import {fakeAsync, tick} from '@angular/core/testing'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; { describe('QueryList', () => { diff --git a/packages/core/test/linker/system_ng_module_factory_loader_spec.ts b/packages/core/test/linker/system_ng_module_factory_loader_spec.ts index a3b780e03825..516be3006ca5 100644 --- a/packages/core/test/linker/system_ng_module_factory_loader_spec.ts +++ b/packages/core/test/linker/system_ng_module_factory_loader_spec.ts @@ -9,7 +9,6 @@ import {Compiler, SystemJsNgModuleLoader} from '@angular/core'; import {global} from '@angular/core/src/util/global'; import {waitForAsync} from '@angular/core/testing'; -import {afterEach, beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {modifiedInIvy, onlyInIvy} from '@angular/private/testing'; function mockSystem(modules: {[module: string]: any}) { diff --git a/packages/core/test/render3/i18n/i18n_parse_spec.ts b/packages/core/test/render3/i18n/i18n_parse_spec.ts index 7608ac69aa72..d3317471af63 100644 --- a/packages/core/test/render3/i18n/i18n_parse_spec.ts +++ b/packages/core/test/render3/i18n/i18n_parse_spec.ts @@ -12,7 +12,6 @@ import {i18nStartFirstCreatePass} from '@angular/core/src/render3/i18n/i18n_pars import {getTIcu} from '@angular/core/src/render3/i18n/i18n_util'; import {I18nUpdateOpCodes, IcuType, TI18n} from '@angular/core/src/render3/interfaces/i18n'; import {HEADER_OFFSET, HOST} from '@angular/core/src/render3/interfaces/view'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {matchTI18n, matchTIcu} from '../matchers'; import {matchDebug} from '../utils'; import {ViewFixture} from '../view_fixture'; diff --git a/packages/core/test/render3/instructions/styling_spec.ts b/packages/core/test/render3/instructions/styling_spec.ts index 2e5abdf4ab79..6e64cdab19ec 100644 --- a/packages/core/test/render3/instructions/styling_spec.ts +++ b/packages/core/test/render3/instructions/styling_spec.ts @@ -17,7 +17,6 @@ import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils'; import {keyValueArraySet} from '@angular/core/src/util/array_utils'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; import {getElementClasses, getElementStyles} from '@angular/core/testing/src/styling'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {clearFirstUpdatePass, enterViewWithOneDiv, rewindBindingIndex} from './shared_spec'; diff --git a/packages/core/test/render3/util/attr_util_spec.ts b/packages/core/test/render3/util/attr_util_spec.ts index e8910f4875ed..8502b9ffd96a 100644 --- a/packages/core/test/render3/util/attr_util_spec.ts +++ b/packages/core/test/render3/util/attr_util_spec.ts @@ -9,7 +9,6 @@ import {AttributeMarker} from '@angular/core/src/render3'; import {TAttributes} from '@angular/core/src/render3/interfaces/node'; import {mergeHostAttribute, mergeHostAttrs} from '@angular/core/src/render3/util/attrs_utils'; -import {describe} from '@angular/core/testing/src/testing_internal'; describe('attr_util', () => { describe('mergeHostAttribute', () => { diff --git a/packages/core/test/sanitization/url_sanitizer_spec.ts b/packages/core/test/sanitization/url_sanitizer_spec.ts index 026dbb77c238..41330e631700 100644 --- a/packages/core/test/sanitization/url_sanitizer_spec.ts +++ b/packages/core/test/sanitization/url_sanitizer_spec.ts @@ -6,16 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import * as t from '@angular/core/testing/src/testing_internal'; - import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer'; { - t.describe('URL sanitizer', () => { + describe('URL sanitizer', () => { let logMsgs: string[]; let originalLog: (msg: any) => any; - t.beforeEach(() => { + beforeEach(() => { logMsgs = []; originalLog = console.warn; // Monkey patch DOM.log. console.warn = (msg: any) => logMsgs.push(msg); @@ -25,12 +23,12 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer console.warn = originalLog; }); - t.it('reports unsafe URLs', () => { - t.expect(_sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()'); - t.expect(logMsgs.join('\n')).toMatch(/sanitizing unsafe URL value/); + it('reports unsafe URLs', () => { + expect(_sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()'); + expect(logMsgs.join('\n')).toMatch(/sanitizing unsafe URL value/); }); - t.describe('valid URLs', () => { + describe('valid URLs', () => { const validUrls = [ '', 'http://abc', @@ -51,11 +49,11 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer 'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', ]; for (const url of validUrls) { - t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toEqual(url)); + it(`valid ${url}`, () => expect(_sanitizeUrl(url)).toEqual(url)); } }); - t.describe('invalid URLs', () => { + describe('invalid URLs', () => { const invalidUrls = [ 'javascript:evil()', 'JavaScript:abc', @@ -75,11 +73,11 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer 'data:application/x-msdownload;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', ]; for (const url of invalidUrls) { - t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toMatch(/^unsafe:/)); + it(`valid ${url}`, () => expect(_sanitizeUrl(url)).toMatch(/^unsafe:/)); } }); - t.describe('valid srcsets', () => { + describe('valid srcsets', () => { const validSrcsets = [ '', 'http://angular.io/images/test.png', @@ -102,17 +100,17 @@ import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer 'http://angular.io/images/test.png?maxage=234, http://angular.io/images/test.png?maxage=234', ]; for (const srcset of validSrcsets) { - t.it(`valid ${srcset}`, () => t.expect(sanitizeSrcset(srcset)).toEqual(srcset)); + it(`valid ${srcset}`, () => expect(sanitizeSrcset(srcset)).toEqual(srcset)); } }); - t.describe('invalid srcsets', () => { + describe('invalid srcsets', () => { const invalidSrcsets = [ 'ht:tp://angular.io/images/test.png', 'http://angular.io/images/test.png, ht:tp://angular.io/images/test.png', ]; for (const srcset of invalidSrcsets) { - t.it(`valid ${srcset}`, () => t.expect(sanitizeSrcset(srcset)).toMatch(/unsafe:/)); + it(`valid ${srcset}`, () => expect(sanitizeSrcset(srcset)).toMatch(/unsafe:/)); } }); }); diff --git a/packages/core/test/spies.ts b/packages/core/test/spies.ts deleted file mode 100644 index 557bf44fe89a..000000000000 --- a/packages/core/test/spies.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @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 - */ - -import {DomAdapter} from '@angular/common/src/dom_adapter'; -import {ElementRef} from '@angular/core'; -import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detection'; -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -export class SpyChangeDetectorRef extends SpyObject { - constructor() { - super(ChangeDetectorRef); - this.spy('detectChanges'); - this.spy('checkNoChanges'); - } -} - -export class SpyIterableDifferFactory extends SpyObject {} - -export class SpyElementRef extends SpyObject { - constructor() { - super(ElementRef); - } -} diff --git a/packages/core/test/test_bed_async_spec.ts b/packages/core/test/test_bed_async_spec.ts deleted file mode 100644 index e4f11a90809a..000000000000 --- a/packages/core/test/test_bed_async_spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @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 - */ - -import {TestBed} from '@angular/core/testing/src/test_bed'; -import {AsyncTestCompleter, ddescribe, describe, inject, it} from '@angular/core/testing/src/testing_internal'; - -describe('TestBed with async processing', () => { - beforeEach(() => { - TestBed.resetTestingModule(); - }); - - it('should allow injecting AsyncTestCompleter', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - async.done(); - })); -}); diff --git a/packages/core/test/testability/testability_spec.ts b/packages/core/test/testability/testability_spec.ts index d3415a85e890..63dd0bffc89d 100644 --- a/packages/core/test/testability/testability_spec.ts +++ b/packages/core/test/testability/testability_spec.ts @@ -11,7 +11,6 @@ import {Injectable} from '@angular/core/src/di'; import {PendingMacrotask, Testability, TestabilityRegistry} from '@angular/core/src/testability/testability'; import {NgZone} from '@angular/core/src/zone/ng_zone'; import {fakeAsync, flush, tick, waitForAsync} from '@angular/core/testing'; -import {beforeEach, describe, expect, it, SpyObject} from '@angular/core/testing/src/testing_internal'; import {scheduleMicroTask} from '../../src/util/microtask'; @@ -58,9 +57,9 @@ class MockNgZone extends NgZone { beforeEach(waitForAsync(() => { ngZone = new MockNgZone(); testability = new Testability(ngZone); - execute = new SpyObject().spy('execute'); - execute2 = new SpyObject().spy('execute'); - updateCallback = new SpyObject().spy('execute'); + execute = jasmine.createSpy('execute'); + execute2 = jasmine.createSpy('execute'); + updateCallback = jasmine.createSpy('execute'); })); describe('Pending count logic', () => { diff --git a/packages/core/test/testing_internal_spec.ts b/packages/core/test/testing_internal_spec.ts deleted file mode 100644 index b6252ad16493..000000000000 --- a/packages/core/test/testing_internal_spec.ts +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @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 - */ - -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -class TestObj { - prop: any; - constructor(prop: any) { - this.prop = prop; - } - someFunc(): number { - return -1; - } - someComplexFunc(a: any) { - return a; - } -} - -{ - describe('testing', () => { - describe('should respect custom equality tester', () => { - beforeEach(() => { - const equalIfMarried = (first: any, second: any) => { - return first === 'kevin' && second === 'patricia'; - }; - jasmine.addCustomEqualityTester(equalIfMarried); - }); - - it('for positive test', () => { - expect('kevin').toEqual('patricia'); - }); - - it('for negative test', () => { - expect('kevin').not.toEqual('kevin'); - }); - }); - - describe('equality', () => { - it('should structurally compare objects', () => { - const expected = new TestObj(new TestObj({'one': [1, 2]})); - const actual = new TestObj(new TestObj({'one': [1, 2]})); - const falseActual = new TestObj(new TestObj({'one': [1, 3]})); - - expect(actual).toEqual(expected); - expect(falseActual).not.toEqual(expected); - }); - }); - - describe('toEqual for Maps', () => { - it('should detect equality for same reference', () => { - const m1: Map = new Map(); - m1.set('a', 1); - expect(m1).toEqual(m1); - }); - - it('should detect equality for same content', () => { - const m1: Map = new Map(); - m1.set('a', 1); - const m2: Map = new Map(); - m2.set('a', 1); - expect(m1).toEqual(m2); - }); - - it('should detect missing entries', () => { - const m1: Map = new Map(); - m1.set('a', 1); - const m2: Map = new Map(); - expect(m1).not.toEqual(m2); - }); - - it('should detect different values', () => { - const m1: Map = new Map(); - m1.set('a', 1); - const m2: Map = new Map(); - m2.set('a', 2); - expect(m1).not.toEqual(m2); - }); - - it('should detect additional entries', () => { - const m1: Map = new Map(); - m1.set('a', 1); - const m2: Map = new Map(); - m2.set('a', 1); - m2.set('b', 2); - expect(m1).not.toEqual(m2); - }); - }); - - describe('spy objects', () => { - let spyObj: any; - - beforeEach(() => { - spyObj = new SpyObject(TestObj); - }); - - it('should return a new spy func with no calls', () => { - expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); - }); - - it('should record function calls', () => { - spyObj.spy('someFunc').and.callFake((a: any, b: any) => a + b); - - expect(spyObj.someFunc(1, 2)).toEqual(3); - expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(1, 2); - }); - - it('should match multiple function calls', () => { - spyObj.spy('someFunc'); - spyObj.someFunc(1, 2); - spyObj.someFunc(3, 4); - expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(1, 2); - expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(3, 4); - }); - - it('should match null arguments', () => { - spyObj.spy('someFunc'); - spyObj.someFunc(null, 'hello'); - expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(null, 'hello'); - }); - - it('should match using deep equality', () => { - spyObj.spy('someComplexFunc'); - spyObj.someComplexFunc([1]); - expect(spyObj.spy('someComplexFunc')).toHaveBeenCalledWith([1]); - }); - - it('should support stubs', () => { - const s = SpyObject.stub({'a': 1}, {'b': 2}); - expect(s.a()).toEqual(1); - expect(s.b()).toEqual(2); - }); - - it('should create spys for all methods', () => { - spyObj.spy('someFunc'); - expect(() => spyObj.someFunc()).not.toThrow(); - }); - }); - }); -} diff --git a/packages/core/test/zone/ng_zone_spec.ts b/packages/core/test/zone/ng_zone_spec.ts index 2ab461b0b17e..2cfd8fe0f9d8 100644 --- a/packages/core/test/zone/ng_zone_spec.ts +++ b/packages/core/test/zone/ng_zone_spec.ts @@ -7,8 +7,8 @@ */ import {EventEmitter, NgZone} from '@angular/core'; -import {fakeAsync, flushMicrotasks, waitForAsync} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it, Log, xit} from '@angular/core/testing/src/testing_internal'; +import {fakeAsync, flushMicrotasks, inject, waitForAsync} from '@angular/core/testing'; +import {Log} from '@angular/core/testing/src/testing_internal'; import {browserDetection} from '@angular/platform-browser/testing/src/browser_util'; import {global} from '../../src/util/global'; @@ -18,7 +18,6 @@ import {NoopNgZone} from '../../src/zone/ng_zone'; const needsLongerTimers = browserDetection.isSlow || browserDetection.isEdge; const resultTimer = 1000; -const testTimeout = browserDetection.isEdge ? 1200 : 500; // Schedules a macrotask (using a timer) function macroTask(fn: (...args: any[]) => void, timer = 1): void { // adds longer timers for passing tests in IE and Edge @@ -88,55 +87,53 @@ function runNgZoneNoLog(fn: () => any) { commonTests(); - it('should produce long stack traces', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - let resolve: (result: any) => void; - const promise: Promise = new Promise((res) => { - resolve = res; - }); + it('should produce long stack traces', done => { + macroTask(() => { + let resolve: (result: any) => void; + const promise: Promise = new Promise((res) => { + resolve = res; + }); - _zone.run(() => { - setTimeout(() => { - setTimeout(() => { - resolve(null); - throw new Error('ccc'); - }, 0); - }, 0); - }); + _zone.run(() => { + setTimeout(() => { + setTimeout(() => { + resolve(null); + throw new Error('ccc'); + }, 0); + }, 0); + }); - promise.then((_) => { - expect(_traces.length).toBe(1); - expect(_traces[0].length).toBeGreaterThan(1); - async.done(); - }); - }); - }), testTimeout); + promise.then((_) => { + expect(_traces.length).toBe(1); + expect(_traces[0].length).toBeGreaterThan(1); + done(); + }); + }); + }); - it('should produce long stack traces (when using microtasks)', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - let resolve: (result: any) => void; - const promise: Promise = new Promise((res) => { - resolve = res; - }); + it('should produce long stack traces (when using microtasks)', done => { + macroTask(() => { + let resolve: (result: any) => void; + const promise: Promise = new Promise((res) => { + resolve = res; + }); - _zone.run(() => { - scheduleMicroTask(() => { - scheduleMicroTask(() => { - resolve(null); - throw new Error('ddd'); - }); - }); - }); + _zone.run(() => { + scheduleMicroTask(() => { + scheduleMicroTask(() => { + resolve(null); + throw new Error('ddd'); + }); + }); + }); - promise.then((_) => { - expect(_traces.length).toBe(1); - expect(_traces[0].length).toBeGreaterThan(1); - async.done(); - }); - }); - }), testTimeout); + promise.then((_) => { + expect(_traces.length).toBe(1); + expect(_traces[0].length).toBeGreaterThan(1); + done(); + }); + }); + }); }); describe('short stack trace', () => { @@ -150,33 +147,32 @@ function runNgZoneNoLog(fn: () => any) { commonTests(); - it('should disable long stack traces', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - let resolve: (result: any) => void; - const promise: Promise = new Promise((res) => { - resolve = res; - }); + it('should disable long stack traces', done => { + macroTask(() => { + let resolve: (result: any) => void; + const promise: Promise = new Promise((res) => { + resolve = res; + }); - _zone.run(() => { - setTimeout(() => { - setTimeout(() => { - resolve(null); - throw new Error('ccc'); - }, 0); - }, 0); - }); + _zone.run(() => { + setTimeout(() => { + setTimeout(() => { + resolve(null); + throw new Error('ccc'); + }, 0); + }, 0); + }); - promise.then((_) => { - expect(_traces.length).toBe(1); - if (_traces[0] != null) { - // some browsers don't have stack traces. - expect(_traces[0].indexOf('---')).toEqual(-1); - } - async.done(); - }); - }); - }), testTimeout); + promise.then((_) => { + expect(_traces.length).toBe(1); + if (_traces[0] != null) { + // some browsers don't have stack traces. + expect(_traces[0].indexOf('---')).toEqual(-1); + } + done(); + }); + }); + }); }); }); @@ -287,173 +283,164 @@ function commonTests() { runNgZoneNoLog(() => { expect(NgZone.isInAngularZone()).toEqual(true); }); - }, testTimeout); + }); }); describe('run', () => { - it('should return the body return value from run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - expect(_zone.run(() => 6)).toEqual(6); - }); + it('should return the body return value from run', done => { + macroTask(() => { + expect(_zone.run(() => 6)).toEqual(6); + }); - macroTask(() => { - async.done(); - }); - }), testTimeout); + macroTask(() => { + done(); + }); + }); - it('should return the body return value from runTask', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - expect(_zone.runTask(() => 6)).toEqual(6); - }); + it('should return the body return value from runTask', done => { + macroTask(() => { + expect(_zone.runTask(() => 6)).toEqual(6); + }); - macroTask(() => { - async.done(); - }); - }), testTimeout); + macroTask(() => { + done(); + }); + }); - it('should call onUnstable and onMicrotaskEmpty', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => macroTask(_log.fn('run'))); - macroTask(() => { - expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); - async.done(); - }); - }), testTimeout); + it('should call onUnstable and onMicrotaskEmpty', done => { + runNgZoneNoLog(() => macroTask(_log.fn('run'))); + macroTask(() => { + expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); + done(); + }); + }); - it('should call onStable once at the end of event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - // The test is set up in a way that causes the zone loop to run onMicrotaskEmpty twice - // then verified that onStable is only called once at the end + it('should call onStable once at the end of event', done => { + // The test is set up in a way that causes the zone loop to run onMicrotaskEmpty twice + // then verified that onStable is only called once at the end - runNgZoneNoLog(() => macroTask(_log.fn('run'))); + runNgZoneNoLog(() => macroTask(_log.fn('run'))); - let times = 0; - _zone.onMicrotaskEmpty.subscribe({ - next: () => { - times++; - _log.add(`onMicrotaskEmpty ${times}`); - if (times < 2) { - // Scheduling a microtask causes a second digest - runNgZoneNoLog(() => { - scheduleMicroTask(() => {}); - }); - } - } - }); + let times = 0; + _zone.onMicrotaskEmpty.subscribe({ + next: () => { + times++; + _log.add(`onMicrotaskEmpty ${times}`); + if (times < 2) { + // Scheduling a microtask causes a second digest + runNgZoneNoLog(() => { + scheduleMicroTask(() => {}); + }); + } + } + }); - macroTask(() => { - expect(_log.result()) - .toEqual( - 'onUnstable; run; onMicrotaskEmpty; onMicrotaskEmpty 1; ' + - 'onMicrotaskEmpty; onMicrotaskEmpty 2; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_log.result()) + .toEqual( + 'onUnstable; run; onMicrotaskEmpty; onMicrotaskEmpty 1; ' + + 'onMicrotaskEmpty; onMicrotaskEmpty 2; onStable'); + done(); + }, resultTimer); + }); - it('should call standalone onStable', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => macroTask(_log.fn('run'))); + it('should call standalone onStable', done => { + runNgZoneNoLog(() => macroTask(_log.fn('run'))); - macroTask(() => { - expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); - - xit('should run subscriber listeners in the subscription zone (outside)', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - // Each subscriber fires a microtask outside the Angular zone. The test - // then verifies that those microtasks do not cause additional digests. - - let turnStart = false; - _zone.onUnstable.subscribe({ - next: () => { - if (turnStart) throw 'Should not call this more than once'; - _log.add('onUnstable'); - scheduleMicroTask(() => {}); - turnStart = true; - } - }); + macroTask(() => { + expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); - let turnDone = false; - _zone.onMicrotaskEmpty.subscribe({ - next: () => { - if (turnDone) throw 'Should not call this more than once'; - _log.add('onMicrotaskEmpty'); - scheduleMicroTask(() => {}); - turnDone = true; - } - }); + xit('should run subscriber listeners in the subscription zone (outside)', done => { + // Each subscriber fires a microtask outside the Angular zone. The test + // then verifies that those microtasks do not cause additional digests. + + let turnStart = false; + _zone.onUnstable.subscribe({ + next: () => { + if (turnStart) throw 'Should not call this more than once'; + _log.add('onUnstable'); + scheduleMicroTask(() => {}); + turnStart = true; + } + }); - let eventDone = false; - _zone.onStable.subscribe({ - next: () => { - if (eventDone) throw 'Should not call this more than once'; - _log.add('onStable'); - scheduleMicroTask(() => {}); - eventDone = true; - } - }); + let turnDone = false; + _zone.onMicrotaskEmpty.subscribe({ + next: () => { + if (turnDone) throw 'Should not call this more than once'; + _log.add('onMicrotaskEmpty'); + scheduleMicroTask(() => {}); + turnDone = true; + } + }); - macroTask(() => { - _zone.run(_log.fn('run')); - }); + let eventDone = false; + _zone.onStable.subscribe({ + next: () => { + if (eventDone) throw 'Should not call this more than once'; + _log.add('onStable'); + scheduleMicroTask(() => {}); + eventDone = true; + } + }); - macroTask(() => { - expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + _zone.run(_log.fn('run')); + }); - it('should run subscriber listeners in the subscription zone (inside)', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => macroTask(_log.fn('run'))); + macroTask(() => { + expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); - // the only practical use-case to run a callback inside the zone is - // change detection after "onMicrotaskEmpty". That's the only case tested. - let turnDone = false; - _zone.onMicrotaskEmpty.subscribe({ - next: () => { - _log.add('onMyMicrotaskEmpty'); - if (turnDone) return; - _zone.run(() => { - scheduleMicroTask(() => {}); - }); - turnDone = true; - } - }); + it('should run subscriber listeners in the subscription zone (inside)', done => { + runNgZoneNoLog(() => macroTask(_log.fn('run'))); + + // the only practical use-case to run a callback inside the zone is + // change detection after "onMicrotaskEmpty". That's the only case tested. + let turnDone = false; + _zone.onMicrotaskEmpty.subscribe({ + next: () => { + _log.add('onMyMicrotaskEmpty'); + if (turnDone) return; + _zone.run(() => { + scheduleMicroTask(() => {}); + }); + turnDone = true; + } + }); - macroTask(() => { - expect(_log.result()) - .toEqual( - 'onUnstable; run; onMicrotaskEmpty; onMyMicrotaskEmpty; ' + - 'onMicrotaskEmpty; onMyMicrotaskEmpty; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_log.result()) + .toEqual( + 'onUnstable; run; onMicrotaskEmpty; onMyMicrotaskEmpty; ' + + 'onMicrotaskEmpty; onMyMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); - it('should run async tasks scheduled inside onStable outside Angular zone', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => macroTask(_log.fn('run'))); + it('should run async tasks scheduled inside onStable outside Angular zone', done => { + runNgZoneNoLog(() => macroTask(_log.fn('run'))); - _zone.onStable.subscribe({ - next: () => { - NgZone.assertNotInAngularZone(); - _log.add('onMyTaskDone'); - } - }); + _zone.onStable.subscribe({ + next: () => { + NgZone.assertNotInAngularZone(); + _log.add('onMyTaskDone'); + } + }); - macroTask(() => { - expect(_log.result()) - .toEqual('onUnstable; run; onMicrotaskEmpty; onStable; onMyTaskDone'); - async.done(); - }); - }), testTimeout); + macroTask(() => { + expect(_log.result()).toEqual('onUnstable; run; onMicrotaskEmpty; onStable; onMyTaskDone'); + done(); + }); + }); it('should call onUnstable once before a turn and onMicrotaskEmpty once after the turn', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { runNgZoneNoLog(() => { macroTask(() => { _log.add('run start'); @@ -466,33 +453,32 @@ function commonTests() { // The microtask (async) is executed after the macrotask (run) expect(_log.result()) .toEqual('onUnstable; run start; run end; async; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); - it('should not run onUnstable and onMicrotaskEmpty for nested Zone.run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => { - macroTask(() => { - _log.add('start run'); - _zone.run(() => { - _log.add('nested run'); - scheduleMicroTask(_log.fn('nested run microtask')); - }); - _log.add('end run'); - }); - }); + it('should not run onUnstable and onMicrotaskEmpty for nested Zone.run', done => { + runNgZoneNoLog(() => { + macroTask(() => { + _log.add('start run'); + _zone.run(() => { + _log.add('nested run'); + scheduleMicroTask(_log.fn('nested run microtask')); + }); + _log.add('end run'); + }); + }); - macroTask(() => { - expect(_log.result()) - .toEqual( - 'onUnstable; start run; nested run; end run; nested run microtask; onMicrotaskEmpty; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_log.result()) + .toEqual( + 'onUnstable; start run; nested run; end run; nested run microtask; onMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); it('should not run onUnstable and onMicrotaskEmpty for nested Zone.run invoked from onMicrotaskEmpty', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { runNgZoneNoLog(() => macroTask(_log.fn('start run'))); _zone.onMicrotaskEmpty.subscribe({ @@ -507,74 +493,71 @@ function commonTests() { expect(_log.result()) .toEqual( 'onUnstable; start run; onMicrotaskEmpty; onMicrotaskEmpty:started; nested run; onMicrotaskEmpty:finished; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); - - it('should call onUnstable and onMicrotaskEmpty before and after each top-level run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => macroTask(_log.fn('run1'))); - runNgZoneNoLog(() => macroTask(_log.fn('run2'))); - - macroTask(() => { - expect(_log.result()) - .toEqual( - 'onUnstable; run1; onMicrotaskEmpty; onStable; onUnstable; run2; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); + + it('should call onUnstable and onMicrotaskEmpty before and after each top-level run', done => { + runNgZoneNoLog(() => macroTask(_log.fn('run1'))); + runNgZoneNoLog(() => macroTask(_log.fn('run2'))); + + macroTask(() => { + expect(_log.result()) + .toEqual( + 'onUnstable; run1; onMicrotaskEmpty; onStable; onUnstable; run2; onMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); - it('should call onUnstable and onMicrotaskEmpty before and after each turn', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let aResolve: (result: string) => void; - let aPromise: Promise; - let bResolve: (result: string) => void; - let bPromise: Promise; + it('should call onUnstable and onMicrotaskEmpty before and after each turn', done => { + let aResolve: (result: string) => void; + let aPromise: Promise; + let bResolve: (result: string) => void; + let bPromise: Promise; - runNgZoneNoLog(() => { - macroTask(() => { - aPromise = new Promise(res => { - aResolve = res; - }); - bPromise = new Promise(res => { - bResolve = res; - }); + runNgZoneNoLog(() => { + macroTask(() => { + aPromise = new Promise(res => { + aResolve = res; + }); + bPromise = new Promise(res => { + bResolve = res; + }); - _log.add('run start'); - aPromise.then(_log.fn('a then')); - bPromise.then(_log.fn('b then')); - }); - }); + _log.add('run start'); + aPromise.then(_log.fn('a then')); + bPromise.then(_log.fn('b then')); + }); + }); - runNgZoneNoLog(() => { - macroTask(() => { - aResolve('a'); - bResolve('b'); - }); - }); + runNgZoneNoLog(() => { + macroTask(() => { + aResolve('a'); + bResolve('b'); + }); + }); - macroTask(() => { - expect(_log.result()) - .toEqual( - 'onUnstable; run start; onMicrotaskEmpty; onStable; onUnstable; a then; b then; onMicrotaskEmpty; onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_log.result()) + .toEqual( + 'onUnstable; run start; onMicrotaskEmpty; onStable; onUnstable; a then; b then; onMicrotaskEmpty; onStable'); + done(); + }, resultTimer); + }); - it('should run a function outside of the angular zone', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - _zone.runOutsideAngular(_log.fn('run')); - }); + it('should run a function outside of the angular zone', done => { + macroTask(() => { + _zone.runOutsideAngular(_log.fn('run')); + }); - macroTask(() => { - expect(_log.result()).toEqual('run'); - async.done(); - }); - }), testTimeout); + macroTask(() => { + expect(_log.result()).toEqual('run'); + done(); + }); + }); it('should call onUnstable and onMicrotaskEmpty when an inner microtask is scheduled from outside angular', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { let resolve: (result: string|null) => void; let promise: Promise; @@ -608,13 +591,13 @@ function commonTests() { // Third VM Turn => execute the microtask (inside angular) // No onUnstable; because we don't own the task which started the turn. 'executedMicrotask; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); it('should call onUnstable only before executing a microtask scheduled in onMicrotaskEmpty ' + 'and not onMicrotaskEmpty after executing the task', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { runNgZoneNoLog(() => macroTask(_log.fn('run'))); let ran = false; @@ -642,13 +625,13 @@ function commonTests() { 'onUnstable; run; onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(end); ' + // Second microtaskDrain Turn => microtask enqueued from onMicrotaskEmpty 'executedMicrotask; onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(end); onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); it('should call onUnstable and onMicrotaskEmpty for a scheduleMicroTask in onMicrotaskEmpty triggered by ' + 'a scheduleMicroTask in run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { runNgZoneNoLog(() => { macroTask(() => { _log.add('scheduleMicroTask'); @@ -680,76 +663,75 @@ function commonTests() { 'onUnstable; scheduleMicroTask; run(executeMicrotask); onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(scheduleMicroTask); onMicrotaskEmpty(end); ' + // Second VM Turn => the microtask enqueued from onMicrotaskEmpty 'onMicrotaskEmpty(executeMicrotask); onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(end); onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); - it('should execute promises scheduled in onUnstable before promises scheduled in run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - runNgZoneNoLog(() => { - macroTask(() => { - _log.add('run start'); - resolvedPromise - .then((_) => { - _log.add('promise then'); - resolvedPromise.then(_log.fn('promise foo')); - return Promise.resolve(null); - }) - .then(_log.fn('promise bar')); - _log.add('run end'); - }); - }); + it('should execute promises scheduled in onUnstable before promises scheduled in run', done => { + runNgZoneNoLog(() => { + macroTask(() => { + _log.add('run start'); + resolvedPromise + .then((_) => { + _log.add('promise then'); + resolvedPromise.then(_log.fn('promise foo')); + return Promise.resolve(null); + }) + .then(_log.fn('promise bar')); + _log.add('run end'); + }); + }); - let donePromiseRan = false; - let startPromiseRan = false; + let donePromiseRan = false; + let startPromiseRan = false; - _zone.onUnstable.subscribe({ - next: () => { - _log.add('onUnstable(begin)'); - if (!startPromiseRan) { - _log.add('onUnstable(schedulePromise)'); - _zone.run(() => { - scheduleMicroTask(_log.fn('onUnstable(executePromise)')); - }); - startPromiseRan = true; - } - _log.add('onUnstable(end)'); - } - }); + _zone.onUnstable.subscribe({ + next: () => { + _log.add('onUnstable(begin)'); + if (!startPromiseRan) { + _log.add('onUnstable(schedulePromise)'); + _zone.run(() => { + scheduleMicroTask(_log.fn('onUnstable(executePromise)')); + }); + startPromiseRan = true; + } + _log.add('onUnstable(end)'); + } + }); - _zone.onMicrotaskEmpty.subscribe({ - next: () => { - _log.add('onMicrotaskEmpty(begin)'); - if (!donePromiseRan) { - _log.add('onMicrotaskEmpty(schedulePromise)'); - _zone.run(() => { - scheduleMicroTask(_log.fn('onMicrotaskEmpty(executePromise)')); - }); - donePromiseRan = true; - } - _log.add('onMicrotaskEmpty(end)'); - } - }); + _zone.onMicrotaskEmpty.subscribe({ + next: () => { + _log.add('onMicrotaskEmpty(begin)'); + if (!donePromiseRan) { + _log.add('onMicrotaskEmpty(schedulePromise)'); + _zone.run(() => { + scheduleMicroTask(_log.fn('onMicrotaskEmpty(executePromise)')); + }); + donePromiseRan = true; + } + _log.add('onMicrotaskEmpty(end)'); + } + }); - macroTask(() => { - expect(_log.result()) - .toEqual( - // First VM turn: enqueue a microtask in onUnstable - 'onUnstable; onUnstable(begin); onUnstable(schedulePromise); onUnstable(end); ' + - // First VM turn: execute the macrotask which enqueues microtasks - 'run start; run end; ' + - // First VM turn: execute enqueued microtasks - 'onUnstable(executePromise); promise then; promise foo; promise bar; onMicrotaskEmpty; ' + - // First VM turn: onTurnEnd, enqueue a microtask - 'onMicrotaskEmpty(begin); onMicrotaskEmpty(schedulePromise); onMicrotaskEmpty(end); ' + - // Second VM turn: execute the microtask from onTurnEnd - 'onMicrotaskEmpty(executePromise); onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(end); onStable'); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_log.result()) + .toEqual( + // First VM turn: enqueue a microtask in onUnstable + 'onUnstable; onUnstable(begin); onUnstable(schedulePromise); onUnstable(end); ' + + // First VM turn: execute the macrotask which enqueues microtasks + 'run start; run end; ' + + // First VM turn: execute enqueued microtasks + 'onUnstable(executePromise); promise then; promise foo; promise bar; onMicrotaskEmpty; ' + + // First VM turn: onTurnEnd, enqueue a microtask + 'onMicrotaskEmpty(begin); onMicrotaskEmpty(schedulePromise); onMicrotaskEmpty(end); ' + + // Second VM turn: execute the microtask from onTurnEnd + 'onMicrotaskEmpty(executePromise); onMicrotaskEmpty; onMicrotaskEmpty(begin); onMicrotaskEmpty(end); onStable'); + done(); + }, resultTimer); + }); it('should call onUnstable and onMicrotaskEmpty before and after each turn, respectively', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { let aResolve: (result: string|null) => void; let aPromise: Promise; let bResolve: (result: string|null) => void; @@ -790,12 +772,12 @@ function commonTests() { 'onUnstable; a then; onMicrotaskEmpty; onStable; ' + // Third VM turn 'onUnstable; b then; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); it('should call onUnstable and onMicrotaskEmpty before and after (respectively) all turns in a chain', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { runNgZoneNoLog(() => { macroTask(() => { _log.add('run start'); @@ -811,12 +793,12 @@ function commonTests() { expect(_log.result()) .toEqual( 'onUnstable; run start; run end; async1; async2; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); it('should call onUnstable and onMicrotaskEmpty for promises created outside of run body', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { let promise: Promise; runNgZoneNoLog(() => { @@ -835,29 +817,28 @@ function commonTests() { .toEqual( 'onUnstable; zone run; onMicrotaskEmpty; onStable; ' + 'onUnstable; promise then; onMicrotaskEmpty; onStable'); - async.done(); + done(); }, resultTimer); - }), testTimeout); + }); }); describe('exceptions', () => { - it('should call the on error callback when it is invoked via zone.runGuarded', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - macroTask(() => { - const exception = new Error('sync'); + it('should call the on error callback when it is invoked via zone.runGuarded', done => { + macroTask(() => { + const exception = new Error('sync'); - _zone.runGuarded(() => { - throw exception; - }); + _zone.runGuarded(() => { + throw exception; + }); - expect(_errors.length).toBe(1); - expect(_errors[0]).toBe(exception); - async.done(); - }); - }), testTimeout); + expect(_errors.length).toBe(1); + expect(_errors[0]).toBe(exception); + done(); + }); + }); it('should not call the on error callback but rethrow when it is invoked via zone.run', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { macroTask(() => { const exception = new Error('sync'); expect(() => _zone.run(() => { @@ -865,28 +846,27 @@ function commonTests() { })).toThrowError('sync'); expect(_errors.length).toBe(0); - async.done(); + done(); }); - }), testTimeout); + }); - it('should call onError for errors from microtasks', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const exception = new Error('async'); + it('should call onError for errors from microtasks', done => { + const exception = new Error('async'); - macroTask(() => { - _zone.run(() => { - scheduleMicroTask(() => { - throw exception; - }); - }); - }); + macroTask(() => { + _zone.run(() => { + scheduleMicroTask(() => { + throw exception; + }); + }); + }); - macroTask(() => { - expect(_errors.length).toBe(1); - expect(_errors[0]).toEqual(exception); - async.done(); - }, resultTimer); - }), testTimeout); + macroTask(() => { + expect(_errors.length).toBe(1); + expect(_errors[0]).toEqual(exception); + done(); + }, resultTimer); + }); }); describe('bugs', () => { @@ -1009,7 +989,7 @@ function commonTests() { global.setImmediate = patchedImmediate; }); - it('should run in requestAnimationFrame async', (done: DoneFn) => { + it('should run in requestAnimationFrame async', done => { let task: Task|undefined = undefined; coalesceZone.run(() => { task = Zone.current.scheduleEventTask('myEvent', () => { @@ -1025,7 +1005,7 @@ function commonTests() { }); it('should only emit onMicroTaskEmpty once within the same event loop for multiple event tasks', - (done: DoneFn) => { + done => { const tasks: Task[] = []; coalesceZone.run(() => { tasks.push(Zone.current.scheduleEventTask('myEvent', () => { @@ -1045,7 +1025,7 @@ function commonTests() { }); it('should only emit onMicroTaskEmpty once within the same event loop for ngZone.run in onMicrotaskEmpty subscription', - (done: DoneFn) => { + done => { const tasks: Task[] = []; coalesceZone.onMicrotaskEmpty.subscribe(() => { coalesceZone.run(() => {}); @@ -1071,7 +1051,7 @@ function commonTests() { }); it('should emit onMicroTaskEmpty once within the same event loop for not only event tasks, but event tasks are before other tasks', - (done: DoneFn) => { + done => { const tasks: Task[] = []; coalesceZone.run(() => { tasks.push(Zone.current.scheduleEventTask('myEvent', () => { @@ -1094,7 +1074,7 @@ function commonTests() { }); it('should emit multiple onMicroTaskEmpty within the same event loop for not only event tasks, but event tasks are after other tasks', - (done: DoneFn) => { + done => { const tasks: Task[] = []; coalesceZone.run(() => { tasks.push(Zone.current.scheduleMacroTask('myMacro', () => { @@ -1148,7 +1128,7 @@ function commonTests() { global.setImmediate = patchedImmediate; }); - it('should run in requestAnimationFrame async', (done: DoneFn) => { + it('should run in requestAnimationFrame async', done => { coalesceZone.run(() => {}); expect(logs).toEqual([]); nativeRequestAnimationFrame(() => { @@ -1158,7 +1138,7 @@ function commonTests() { }); it('should only emit onMicroTaskEmpty once within the same event loop for multiple ngZone.run', - (done: DoneFn) => { + done => { coalesceZone.run(() => {}); coalesceZone.run(() => {}); expect(logs).toEqual([]); @@ -1169,7 +1149,7 @@ function commonTests() { }); it('should only emit onMicroTaskEmpty once within the same event loop for ngZone.run in onMicrotaskEmpty subscription', - (done: DoneFn) => { + done => { coalesceZone.onMicrotaskEmpty.subscribe(() => { coalesceZone.run(() => {}); }); @@ -1183,7 +1163,7 @@ function commonTests() { }); it('should only emit onMicroTaskEmpty once within the same event loop for multiple tasks', - (done: DoneFn) => { + done => { const tasks: Task[] = []; coalesceZone.run(() => { tasks.push(Zone.current.scheduleMacroTask('myMacro', () => { diff --git a/packages/core/testing/src/async_test_completer.ts b/packages/core/testing/src/async_test_completer.ts deleted file mode 100644 index 2d2385a3debe..000000000000 --- a/packages/core/testing/src/async_test_completer.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @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 - */ - -/** - * Injectable completer that allows signaling completion of an asynchronous test. Used internally. - */ -export class AsyncTestCompleter { - // TODO(issue/24571): remove '!'. - private _resolve!: (result: any) => void; - // TODO(issue/24571): remove '!'. - private _reject!: (err: any) => void; - private _promise: Promise = new Promise((res, rej) => { - this._resolve = res; - this._reject = rej; - }); - done(value?: any) { - this._resolve(value); - } - - fail(error?: any, stackTrace?: string) { - this._reject(error); - } - - get promise(): Promise { - return this._promise; - } -} diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 68c413b23723..9859681c0771 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -8,7 +8,6 @@ import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectFlags, InjectionToken, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, ProviderToken, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵclearOverrides as clearOverrides, ɵDepFlags as DepFlags, ɵgetInjectableDef as getInjectableDef, ɵINJECTOR_SCOPE as INJECTOR_SCOPE, ɵivyEnabled as ivyEnabled, ɵNodeFlags as NodeFlags, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify, ɵɵInjectableDeclaration} from '@angular/core'; -import {AsyncTestCompleter} from './async_test_completer'; import {ComponentFixture} from './component_fixture'; import {MetadataOverride} from './metadata_override'; import {_getTestBedRender3, TestBedRender3} from './r3_test_bed'; @@ -660,32 +659,14 @@ function _getTestBedViewEngine(): TestBedViewEngine { * }) * ``` * - * Notes: - * - inject is currently a function because of some Traceur limitation the syntax should - * eventually - * becomes `it('...', @Inject (object: AClass, async: AsyncTestCompleter) => { ... });` - * * @publicApi */ export function inject(tokens: any[], fn: Function): () => any { const testBed = getTestBed(); - if (tokens.indexOf(AsyncTestCompleter) >= 0) { - // Not using an arrow function to preserve context passed from call site - return function(this: unknown) { - // Return an async test method that returns a Promise if AsyncTestCompleter is one of - // the injected tokens. - return testBed.compileComponents().then(() => { - const completer = testBed.inject(AsyncTestCompleter); - testBed.execute(tokens, fn, this); - return completer.promise; - }); - }; - } else { - // Not using an arrow function to preserve context passed from call site - return function(this: unknown) { - return testBed.execute(tokens, fn, this); - }; - } + // Not using an arrow function to preserve context passed from call site + return function(this: unknown) { + return testBed.execute(tokens, fn, this); + }; } /** diff --git a/packages/core/testing/src/testing_internal.ts b/packages/core/testing/src/testing_internal.ts index 5cd971d5ecd6..1307ecf157c4 100644 --- a/packages/core/testing/src/testing_internal.ts +++ b/packages/core/testing/src/testing_internal.ts @@ -6,213 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵisPromise as isPromise} from '@angular/core'; -import {global} from '@angular/core/src/util/global'; - -import {AsyncTestCompleter} from './async_test_completer'; -import {getTestBed, inject} from './test_bed'; - -export {AsyncTestCompleter} from './async_test_completer'; export {inject} from './test_bed'; export * from './logger'; export * from './ng_zone_mock'; - -export const proxy: ClassDecorator = (t: any) => t; - -const _global = (typeof window === 'undefined' ? global : window); - -export const afterEach: Function = _global.afterEach; -export const expect: (actual: T) => jasmine.Matchers = _global.expect; - -const jsmBeforeEach = _global.beforeEach; -const jsmDescribe = _global.describe; -const jsmDDescribe = _global.fdescribe; -const jsmXDescribe = _global.xdescribe; -const jsmIt = _global.it; -const jsmFIt = _global.fit; -const jsmXIt = _global.xit; - -const runnerStack: BeforeEachRunner[] = []; -jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000; -const globalTimeOut = jasmine.DEFAULT_TIMEOUT_INTERVAL; - -const testBed = getTestBed(); - -export type TestFn = ((done: DoneFn) => any)|(() => any); - -/** - * Mechanism to run `beforeEach()` functions of Angular tests. - * - * Note: Jasmine own `beforeEach` is used by this library to handle DI providers. - */ -class BeforeEachRunner { - private _fns: Array = []; - - constructor(private _parent: BeforeEachRunner) {} - - beforeEach(fn: Function): void { - this._fns.push(fn); - } - - run(): void { - if (this._parent) this._parent.run(); - this._fns.forEach((fn) => { - fn(); - }); - } -} - -// Reset the test providers before each test -jsmBeforeEach(() => { - testBed.resetTestingModule(); -}); - -function _describe(jsmFn: Function, ...args: any[]) { - const parentRunner = runnerStack.length === 0 ? null : runnerStack[runnerStack.length - 1]; - const runner = new BeforeEachRunner(parentRunner!); - runnerStack.push(runner); - const suite = jsmFn(...args); - runnerStack.pop(); - return suite; -} - -export function describe(...args: any[]): void { - return _describe(jsmDescribe, ...args); -} - -export function ddescribe(...args: any[]): void { - return _describe(jsmDDescribe, ...args); -} - -export function xdescribe(...args: any[]): void { - return _describe(jsmXDescribe, ...args); -} - -export function beforeEach(fn: Function): void { - if (runnerStack.length > 0) { - // Inside a describe block, beforeEach() uses a BeforeEachRunner - runnerStack[runnerStack.length - 1].beforeEach(fn); - } else { - // Top level beforeEach() are delegated to jasmine - jsmBeforeEach(fn); - } -} - -/** - * Allows overriding default providers defined in test_injector.js. - * - * The given function must return a list of DI providers. - * - * Example: - * - * beforeEachProviders(() => [ - * {provide: Compiler, useClass: MockCompiler}, - * {provide: SomeToken, useValue: myValue}, - * ]); - */ -export function beforeEachProviders(fn: Function): void { - jsmBeforeEach(() => { - const providers = fn(); - if (!providers) return; - testBed.configureTestingModule({providers: providers}); - }); -} - - -function _it(jsmFn: Function, testName: string, testFn: TestFn, testTimeout = 0): void { - if (runnerStack.length == 0) { - // This left here intentionally, as we should never get here, and it aids debugging. - // tslint:disable-next-line - debugger; - throw new Error('Empty Stack!'); - } - const runner = runnerStack[runnerStack.length - 1]; - const timeout = Math.max(globalTimeOut, testTimeout); - - jsmFn(testName, (done: DoneFn) => { - const completerProvider = { - provide: AsyncTestCompleter, - useFactory: () => { - // Mark the test as async when an AsyncTestCompleter is injected in an it() - return new AsyncTestCompleter(); - } - }; - testBed.configureTestingModule({providers: [completerProvider]}); - runner.run(); - - if (testFn.length === 0) { - // TypeScript doesn't infer the TestFn type without parameters here, so we - // need to manually cast it. - const retVal = (testFn as () => any)(); - if (isPromise(retVal)) { - // Asynchronous test function that returns a Promise - wait for completion. - retVal.then(done, done.fail); - } else { - // Synchronous test function - complete immediately. - done(); - } - } else { - // Asynchronous test function that takes in 'done' parameter. - testFn(done); - } - }, timeout); -} - -export function it(expectation: string, assertion: TestFn, timeout?: number): void { - return _it(jsmIt, expectation, assertion, timeout); -} - -export function fit(expectation: string, assertion: TestFn, timeout?: number): void { - return _it(jsmFIt, expectation, assertion, timeout); -} - -export function xit(expectation: string, assertion: TestFn, timeout?: number): void { - return _it(jsmXIt, expectation, assertion, timeout); -} - -export class SpyObject { - constructor(type?: any) { - if (type) { - for (const prop in type.prototype) { - let m: any = null; - try { - m = type.prototype[prop]; - } catch { - // As we are creating spys for abstract classes, - // these classes might have getters that throw when they are accessed. - // As we are only auto creating spys for methods, this - // should not matter. - } - if (typeof m === 'function') { - this.spy(prop); - } - } - } - } - - spy(name: string) { - if (!(this as any)[name]) { - (this as any)[name] = jasmine.createSpy(name); - } - return (this as any)[name]; - } - - prop(name: string, value: any) { - (this as any)[name] = value; - } - - static stub(object: any = null, config: any = null, overrides: any = null) { - if (!(object instanceof SpyObject)) { - overrides = config; - config = object; - object = new SpyObject(); - } - - const m = {...config, ...overrides}; - Object.keys(m).forEach(key => { - object.spy(key).and.returnValue(m[key]); - }); - return object; - } -} diff --git a/packages/docs/web_workers/web_workers.md b/packages/docs/web_workers/web_workers.md index 086dee9de981..17fb7eeac40c 100644 --- a/packages/docs/web_workers/web_workers.md +++ b/packages/docs/web_workers/web_workers.md @@ -167,10 +167,6 @@ Additionally, the [MessageBroker](#messagebroker) sits on top of the [MessageBus MessageBus is a low level abstraction that provides a language agnostic API for communicating with angular components across any runtime boundary such as `WebWorker <--> UI` communication, `UI <--> Server` communication, or `Window <--> Window` communication. -See the diagram below for a high level overview of how this code is structured: - -![WebWorker Diagram](http://stanford.edu/~jteplitz/ng_2_worker.png) - ## Running Code on the UI If your application needs to run code on the UI, there are a few options. The easiest way is to use a CustomElement in your view. You can then register this custom element from your html file and run code in response diff --git a/packages/forms/test/directives_spec.ts b/packages/forms/test/directives_spec.ts index 2b99f910e110..0b94bdc02bdf 100644 --- a/packages/forms/test/directives_spec.ts +++ b/packages/forms/test/directives_spec.ts @@ -8,11 +8,9 @@ import {SimpleChange} from '@angular/core'; import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, CheckboxControlValueAccessor, ControlValueAccessor, DefaultValueAccessor, FormArray, FormArrayName, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormGroupName, NgControl, NgForm, NgModel, NgModelGroup, SelectControlValueAccessor, SelectMultipleControlValueAccessor, ValidationErrors, Validator, Validators} from '@angular/forms'; import {selectValueAccessor} from '@angular/forms/src/directives/shared'; import {composeValidators} from '@angular/forms/src/validators'; -import {SpyNgControl, SpyValueAccessor} from './spies'; import {asyncValidator} from './util'; class DummyControlValueAccessor implements ControlValueAccessor { @@ -45,7 +43,7 @@ class CustomValidatorDirective implements Validator { let dir: NgControl; beforeEach(() => { - dir = new SpyNgControl(); + dir = {path: []} as any; }); it('should throw when given an empty array', () => { @@ -90,7 +88,7 @@ class CustomValidatorDirective implements Validator { }); it('should return custom accessor when provided', () => { - const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any; + const customAccessor: ControlValueAccessor = {} as any; const checkboxAccessor = new CheckboxControlValueAccessor(null!, null!); expect(selectValueAccessor(dir, [ defaultAccessor, customAccessor, checkboxAccessor @@ -98,7 +96,7 @@ class CustomValidatorDirective implements Validator { }); it('should return custom accessor when provided with select multiple', () => { - const customAccessor: ControlValueAccessor = new SpyValueAccessor() as any; + const customAccessor: ControlValueAccessor = {} as any; const selectMultipleAccessor = new SelectMultipleControlValueAccessor(null!, null!); expect(selectValueAccessor(dir, [ defaultAccessor, customAccessor, selectMultipleAccessor @@ -106,7 +104,7 @@ class CustomValidatorDirective implements Validator { }); it('should throw when more than one custom accessor is provided', () => { - const customAccessor: ControlValueAccessor = new SpyValueAccessor(); + const customAccessor: ControlValueAccessor = {} as any; expect(() => selectValueAccessor(dir, [customAccessor, customAccessor])).toThrowError(); }); }); diff --git a/packages/forms/test/form_array_spec.ts b/packages/forms/test/form_array_spec.ts index c810230d1723..5097be662156 100644 --- a/packages/forms/test/form_array_spec.ts +++ b/packages/forms/test/form_array_spec.ts @@ -7,7 +7,6 @@ */ import {fakeAsync, tick} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, ValidatorFn} from '@angular/forms'; import {Validators} from '@angular/forms/src/validators'; import {of} from 'rxjs'; @@ -856,64 +855,60 @@ describe('FormArray', () => { a = new FormArray([c1, c2]); }); - it('should fire an event after the value has been updated', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.valueChanges.subscribe({ - next: (value: any) => { - expect(a.value).toEqual(['new1', 'old2']); - expect(value).toEqual(['new1', 'old2']); - async.done(); - } - }); - c1.setValue('new1'); - })); + it('should fire an event after the value has been updated', done => { + a.valueChanges.subscribe({ + next: (value: any) => { + expect(a.value).toEqual(['new1', 'old2']); + expect(value).toEqual(['new1', 'old2']); + done(); + } + }); + c1.setValue('new1'); + }); - it('should fire an event after the control\'s observable fired an event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - let controlCallbackIsCalled = false; + it('should fire an event after the control\'s observable fired an event', done => { + let controlCallbackIsCalled = false; - c1.valueChanges.subscribe({ - next: (value: any) => { - controlCallbackIsCalled = true; - } - }); + c1.valueChanges.subscribe({ + next: (value: any) => { + controlCallbackIsCalled = true; + } + }); - a.valueChanges.subscribe({ - next: (value: any) => { - expect(controlCallbackIsCalled).toBe(true); - async.done(); - } - }); + a.valueChanges.subscribe({ + next: (value: any) => { + expect(controlCallbackIsCalled).toBe(true); + done(); + } + }); - c1.setValue('new1'); - })); + c1.setValue('new1'); + }); - it('should fire an event when a control is removed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual(['old1']); - async.done(); - } - }); + it('should fire an event when a control is removed', done => { + a.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual(['old1']); + done(); + } + }); - a.removeAt(1); - })); + a.removeAt(1); + }); - it('should fire an event when a control is added', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - a.removeAt(1); + it('should fire an event when a control is added', done => { + a.removeAt(1); - a.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual(['old1', 'old2']); - async.done(); - } - }); + a.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual(['old1', 'old2']); + done(); + } + }); - a.push(c2); - })); + a.push(c2); + }); }); describe('get', () => { diff --git a/packages/forms/test/form_builder_spec.ts b/packages/forms/test/form_builder_spec.ts index e0d0da0b03ff..0e0e100ab95f 100644 --- a/packages/forms/test/form_builder_spec.ts +++ b/packages/forms/test/form_builder_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ import {fakeAsync, tick} from '@angular/core/testing'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {FormBuilder, Validators} from '@angular/forms'; import {of} from 'rxjs'; diff --git a/packages/forms/test/form_control_spec.ts b/packages/forms/test/form_control_spec.ts index 7caff35c0bd3..d4344e8ce5f1 100644 --- a/packages/forms/test/form_control_spec.ts +++ b/packages/forms/test/form_control_spec.ts @@ -7,7 +7,6 @@ */ import {fakeAsync, tick} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {FormControl, FormGroup, Validators} from '@angular/forms'; import {FormArray} from '@angular/forms/src/model'; @@ -857,17 +856,16 @@ describe('FormControl', () => { c = new FormControl('old', Validators.required); }); - it('should fire an event after the value has been updated', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.valueChanges.subscribe({ - next: (value: any) => { - expect(c.value).toEqual('new'); - expect(value).toEqual('new'); - async.done(); - } - }); - c.setValue('new'); - })); + it('should fire an event after the value has been updated', done => { + c.valueChanges.subscribe({ + next: (value: any) => { + expect(c.value).toEqual('new'); + expect(value).toEqual('new'); + done(); + } + }); + c.setValue('new'); + }); it('should fire an event after the status has been updated to invalid', fakeAsync(() => { c.statusChanges.subscribe({ @@ -911,27 +909,25 @@ describe('FormControl', () => { })); // TODO: remove the if statement after making observable delivery sync - it('should update set errors and status before emitting an event', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.valueChanges.subscribe((value: any /** TODO #9100 */) => { - expect(c.valid).toEqual(false); - expect(c.errors).toEqual({'required': true}); - async.done(); - }); - c.setValue(''); - })); + it('should update set errors and status before emitting an event', done => { + c.valueChanges.subscribe((value: any /** TODO #9100 */) => { + expect(c.valid).toEqual(false); + expect(c.errors).toEqual({'required': true}); + done(); + }); + c.setValue(''); + }); - it('should return a cold observable', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - c.setValue('will be ignored'); - c.valueChanges.subscribe({ - next: (value: any) => { - expect(value).toEqual('new'); - async.done(); - } - }); - c.setValue('new'); - })); + it('should return a cold observable', done => { + c.setValue('will be ignored'); + c.valueChanges.subscribe({ + next: (value: any) => { + expect(value).toEqual('new'); + done(); + } + }); + c.setValue('new'); + }); }); describe('setErrors', () => { diff --git a/packages/forms/test/form_group_spec.ts b/packages/forms/test/form_group_spec.ts index 6a8578bef16d..830a3ae587e3 100644 --- a/packages/forms/test/form_group_spec.ts +++ b/packages/forms/test/form_group_spec.ts @@ -7,7 +7,6 @@ */ import {fakeAsync, tick, waitForAsync} from '@angular/core/testing'; -import {AsyncTestCompleter, beforeEach, describe, inject, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators} from '@angular/forms'; import {of} from 'rxjs'; @@ -822,20 +821,19 @@ describe('FormGroup', () => { // TODO(kara): update these tests to use fake Async - it('should fire a statusChange if child has async validation change', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const loggedValues: string[] = []; - group.statusChanges.subscribe({ - next: (status: string) => { - loggedValues.push(status); - if (loggedValues.length === 2) { - expect(loggedValues).toEqual(['PENDING', 'INVALID']); - } - async.done(); - } - }); - control.setValue(''); - })); + it('should fire a statusChange if child has async validation change', done => { + const loggedValues: string[] = []; + group.statusChanges.subscribe({ + next: (status: string) => { + loggedValues.push(status); + if (loggedValues.length === 2) { + expect(loggedValues).toEqual(['PENDING', 'INVALID']); + } + done(); + } + }); + control.setValue(''); + }); }); describe('getError', () => { diff --git a/packages/forms/test/reactive_integration_spec.ts b/packages/forms/test/reactive_integration_spec.ts index acbe4eb995c6..99de565ea266 100644 --- a/packages/forms/test/reactive_integration_spec.ts +++ b/packages/forms/test/reactive_integration_spec.ts @@ -9,7 +9,6 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {Component, Directive, forwardRef, Input, NgModule, OnDestroy, Type} from '@angular/core'; import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; -import {expect} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, AsyncValidator, AsyncValidatorFn, COMPOSITION_BUFFER_MODE, ControlValueAccessor, DefaultValueAccessor, FormArray, FormControl, FormControlDirective, FormControlName, FormGroup, FormGroupDirective, FormsModule, MaxValidator, MinValidator, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validator, Validators} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {dispatchEvent, sortedClassList} from '@angular/platform-browser/testing/src/browser_util'; @@ -5018,4 +5017,4 @@ class MinMaxFormControlComp { form!: FormGroup; min: number|string = 1; max: number|string = 10; -} \ No newline at end of file +} diff --git a/packages/forms/test/spies.ts b/packages/forms/test/spies.ts deleted file mode 100644 index d90f06f324df..000000000000 --- a/packages/forms/test/spies.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @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 - */ - -import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -export class SpyChangeDetectorRef extends SpyObject { - constructor() { - super(ChangeDetectorRef); - this.spy('markForCheck'); - } -} - -export class SpyNgControl extends SpyObject { - path = []; -} - -export class SpyValueAccessor extends SpyObject { - writeValue: any; -} diff --git a/packages/forms/test/validators_spec.ts b/packages/forms/test/validators_spec.ts index a42c14774ce8..c125836df16f 100644 --- a/packages/forms/test/validators_spec.ts +++ b/packages/forms/test/validators_spec.ts @@ -7,7 +7,6 @@ */ import {fakeAsync, tick} from '@angular/core/testing'; -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {AbstractControl, AsyncValidator, AsyncValidatorFn, FormArray, FormControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; import {Observable, of, timer} from 'rxjs'; import {first, map} from 'rxjs/operators'; diff --git a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts index ba1777b0a049..e950c202d836 100644 --- a/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts +++ b/packages/platform-browser-dynamic/test/resource_loader/resource_loader_impl_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {AsyncTestCompleter, beforeEach, describe, expect, inject, it} from '@angular/core/testing/src/testing_internal'; import {ResourceLoaderImpl} from '@angular/platform-browser-dynamic/src/resource_loader/resource_loader_impl'; if (isBrowser) { @@ -26,21 +25,19 @@ if (isBrowser) { resourceLoader = new ResourceLoaderImpl(); }); - it('should resolve the Promise with the file content on success', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - resourceLoader.get(url200).then((text) => { - expect(text.trim()).toEqual('

hey

'); - async.done(); - }); - }), 10000); + it('should resolve the Promise with the file content on success', done => { + resourceLoader.get(url200).then((text) => { + expect(text.trim()).toEqual('

hey

'); + done(); + }); + }, 10000); - it('should reject the Promise on failure', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - resourceLoader.get(url404).catch((e) => { - expect(e).toEqual(`Failed to load ${url404}`); - async.done(); - return null; - }); - }), 10000); + it('should reject the Promise on failure', done => { + resourceLoader.get(url404).catch((e) => { + expect(e).toEqual(`Failed to load ${url404}`); + done(); + return null; + }); + }, 10000); }); } diff --git a/packages/platform-browser/test/browser/bootstrap_spec.ts b/packages/platform-browser/test/browser/bootstrap_spec.ts index d49260773fb2..e8ae8b2b0bb6 100644 --- a/packages/platform-browser/test/browser/bootstrap_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_spec.ts @@ -12,7 +12,8 @@ import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref import {Console} from '@angular/core/src/console'; import {ComponentRef} from '@angular/core/src/linker/component_factory'; import {Testability, TestabilityRegistry} from '@angular/core/src/testability/testability'; -import {afterEach, AsyncTestCompleter, beforeEach, beforeEachProviders, describe, inject, it, Log} from '@angular/core/testing/src/testing_internal'; +import {inject, TestBed} from '@angular/core/testing'; +import {Log} from '@angular/core/testing/src/testing_internal'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -61,10 +62,6 @@ class HelloRootCmp4 { } } -@Component({selector: 'hello-app'}) -class HelloRootMissingTemplate { -} - @Directive({selector: 'hello-app'}) class HelloRootDirectiveIsNotCmp { } @@ -81,29 +78,6 @@ class HelloOnDestroyTickCmp implements OnDestroy { } } -@Component({selector: 'hello-app', templateUrl: './sometemplate.html'}) -class HelloUrlCmp { - greeting = 'hello'; -} - -@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}}) -class SomeDirective { - // TODO(issue/24571): remove '!'. - @Input() someDir!: string; -} - -@Pipe({name: 'somePipe'}) -class SomePipe { - transform(value: string): any { - return `transformed ${value}`; - } -} - -@Component({selector: 'hello-app', template: `
`}) -class HelloCmpUsingPlatformDirectiveAndPipe { - show: boolean = false; -} - @Component({selector: 'hello-app', template: 'hello world!'}) class HelloCmpUsingCustomElement { } @@ -150,8 +124,8 @@ function bootstrap( if (isNode) return; let compilerConsole: DummyConsole; - beforeEachProviders(() => { - return [Log]; + beforeEach(() => { + TestBed.configureTestingModule({providers: [Log]}); }); beforeEach(inject([DOCUMENT], (doc: any) => { @@ -175,35 +149,31 @@ function bootstrap( afterEach(destroyPlatform); - // TODO(misko): can't use `modifiedInIvy.it` because the `it` is somehow special here. - modifiedInIvy('bootstrapping non-Component throws in View Engine').isEnabled && - it('should throw if bootstrapped Directive is not a Component', - inject([AsyncTestCompleter], (done: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - expect( - () => bootstrap( - HelloRootDirectiveIsNotCmp, [{provide: ErrorHandler, useValue: errorHandler}])) - .toThrowError(`HelloRootDirectiveIsNotCmp cannot be used as an entry component.`); - done.done(); - })); - - // TODO(misko): can't use `onlyInIvy.it` because the `it` is somehow special here. - onlyInIvy('bootstrapping non-Component rejects Promise in Ivy').isEnabled && - it('should throw if bootstrapped Directive is not a Component', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - bootstrap(HelloRootDirectiveIsNotCmp, [ - {provide: ErrorHandler, useValue: errorHandler} - ]).catch((error: Error) => { - expect(error).toEqual( - new Error(`HelloRootDirectiveIsNotCmp cannot be used as an entry component.`)); - async.done(); - }); - })); + modifiedInIvy('bootstrapping non-Component throws in View Engine') + .it('should throw if bootstrapped Directive is not a Component', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + expect( + () => bootstrap( + HelloRootDirectiveIsNotCmp, [{provide: ErrorHandler, useValue: errorHandler}])) + .toThrowError(`HelloRootDirectiveIsNotCmp cannot be used as an entry component.`); + done(); + }); + + onlyInIvy('bootstrapping non-Component rejects Promise in Ivy') + .it('should throw if bootstrapped Directive is not a Component', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + bootstrap(HelloRootDirectiveIsNotCmp, [ + {provide: ErrorHandler, useValue: errorHandler} + ]).catch((error: Error) => { + expect(error).toEqual( + new Error(`HelloRootDirectiveIsNotCmp cannot be used as an entry component.`)); + done(); + }); + }); it('should retrieve sanitizer', inject([Injector], (injector: Injector) => { const sanitizer: Sanitizer|null = injector.get(Sanitizer, null); @@ -218,92 +188,88 @@ function bootstrap( } })); - it('should throw if no element is found', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - bootstrap(NonExistentComp, [ - {provide: ErrorHandler, useValue: errorHandler} - ]).then(null, (reason) => { - expect(reason.message) - .toContain('The selector "non-existent" did not match any elements'); - async.done(); - return null; - }); - })); + it('should throw if no element is found', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + bootstrap(NonExistentComp, [ + {provide: ErrorHandler, useValue: errorHandler} + ]).then(null, (reason) => { + expect(reason.message).toContain('The selector "non-existent" did not match any elements'); + done(); + return null; + }); + }); - it('should throw if no provider', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; + it('should throw if no provider', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; - class IDontExist {} + class IDontExist {} - @Component({selector: 'cmp', template: 'Cmp'}) - class CustomCmp { - constructor(iDontExist: IDontExist) {} - } + @Component({selector: 'cmp', template: 'Cmp'}) + class CustomCmp { + constructor(iDontExist: IDontExist) {} + } - @Component({ - selector: 'hello-app', - template: '', - }) - class RootCmp { - } + @Component({ + selector: 'hello-app', + template: '', + }) + class RootCmp { + } - @NgModule({declarations: [CustomCmp], exports: [CustomCmp]}) - class CustomModule { - } + @NgModule({declarations: [CustomCmp], exports: [CustomCmp]}) + class CustomModule { + } - bootstrap(RootCmp, [{provide: ErrorHandler, useValue: errorHandler}], [], [ - CustomModule - ]).then(null, (e: Error) => { - let errorMsg: string; - if (ivyEnabled) { - errorMsg = `R3InjectorError(TestModule)[IDontExist -> IDontExist -> IDontExist]: \n`; - } else { - errorMsg = `StaticInjectorError(TestModule)[CustomCmp -> IDontExist]: \n` + - ' StaticInjectorError(Platform: core)[CustomCmp -> IDontExist]: \n' + - ' NullInjectorError: No provider for IDontExist!'; - } - expect(e.message).toContain(errorMsg); - async.done(); - return null; - }); - })); + bootstrap(RootCmp, [{provide: ErrorHandler, useValue: errorHandler}], [], [ + CustomModule + ]).then(null, (e: Error) => { + let errorMsg: string; + if (ivyEnabled) { + errorMsg = `R3InjectorError(TestModule)[IDontExist -> IDontExist -> IDontExist]: \n`; + } else { + errorMsg = `StaticInjectorError(TestModule)[CustomCmp -> IDontExist]: \n` + + ' StaticInjectorError(Platform: core)[CustomCmp -> IDontExist]: \n' + + ' NullInjectorError: No provider for IDontExist!'; + } + expect(e.message).toContain(errorMsg); + done(); + return null; + }); + }); if (getDOM().supportsDOMEvents) { - it('should forward the error to promise when bootstrap fails', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - - const refPromise = - bootstrap(NonExistentComp, [{provide: ErrorHandler, useValue: errorHandler}]); - refPromise.then(null, (reason: any) => { - expect(reason.message) - .toContain('The selector "non-existent" did not match any elements'); - async.done(); - }); - })); - - it('should invoke the default exception handler when bootstrap fails', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const logger = new MockConsole(); - const errorHandler = new ErrorHandler(); - (errorHandler as any)._console = logger as any; - - const refPromise = - bootstrap(NonExistentComp, [{provide: ErrorHandler, useValue: errorHandler}]); - refPromise.then(null, (reason) => { - expect(logger.res[0].join('#')) - .toContain('ERROR#Error: The selector "non-existent" did not match any elements'); - async.done(); - return null; - }); - })); + it('should forward the error to promise when bootstrap fails', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + + const refPromise = + bootstrap(NonExistentComp, [{provide: ErrorHandler, useValue: errorHandler}]); + refPromise.then(null, (reason: any) => { + expect(reason.message) + .toContain('The selector "non-existent" did not match any elements'); + done(); + }); + }); + + it('should invoke the default exception handler when bootstrap fails', done => { + const logger = new MockConsole(); + const errorHandler = new ErrorHandler(); + (errorHandler as any)._console = logger as any; + + const refPromise = + bootstrap(NonExistentComp, [{provide: ErrorHandler, useValue: errorHandler}]); + refPromise.then(null, (reason) => { + expect(logger.res[0].join('#')) + .toContain('ERROR#Error: The selector "non-existent" did not match any elements'); + done(); + return null; + }); + }); } it('should create an injector promise', () => { @@ -311,26 +277,25 @@ function bootstrap( expect(refPromise).toEqual(jasmine.any(Promise)); }); - it('should set platform name to browser', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise = bootstrap(HelloRootCmp, testProviders); - refPromise.then((ref) => { - expect(isPlatformBrowser(ref.injector.get(PLATFORM_ID))).toBe(true); - async.done(); - }); - })); + it('should set platform name to browser', done => { + const refPromise = bootstrap(HelloRootCmp, testProviders); + refPromise.then((ref) => { + expect(isPlatformBrowser(ref.injector.get(PLATFORM_ID))).toBe(true); + done(); + }, done.fail); + }); - it('should display hello world', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise = bootstrap(HelloRootCmp, testProviders); - refPromise.then((ref) => { - expect(el).toHaveText('hello world!'); - expect(el.getAttribute('ng-version')).toEqual(VERSION.full); - async.done(); - }); - })); + it('should display hello world', done => { + const refPromise = bootstrap(HelloRootCmp, testProviders); + refPromise.then((ref) => { + expect(el).toHaveText('hello world!'); + expect(el.getAttribute('ng-version')).toEqual(VERSION.full); + done(); + }, done.fail); + }); it('should throw a descriptive error if BrowserModule is installed again via a lazily loaded module', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { @NgModule({imports: [BrowserModule]}) class AsyncModule { } @@ -343,158 +308,155 @@ function bootstrap( `BrowserModule has already been loaded. If you need access to common directives such as NgIf and NgFor from a lazy loaded module, import CommonModule instead.`); }); }) - .then(() => async.done(), err => async.fail(err)); - })); - - it('should support multiple calls to bootstrap', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise1 = bootstrap(HelloRootCmp, testProviders); - const refPromise2 = bootstrap(HelloRootCmp2, testProviders); - Promise.all([refPromise1, refPromise2]).then((refs) => { - expect(el).toHaveText('hello world!'); - expect(el2).toHaveText('hello world, again!'); - async.done(); - }); - })); + .then(() => done(), err => done.fail(err)); + }); + + it('should support multiple calls to bootstrap', done => { + const refPromise1 = bootstrap(HelloRootCmp, testProviders); + const refPromise2 = bootstrap(HelloRootCmp2, testProviders); + Promise.all([refPromise1, refPromise2]).then((refs) => { + expect(el).toHaveText('hello world!'); + expect(el2).toHaveText('hello world, again!'); + done(); + }, done.fail); + }); it('should not crash if change detection is invoked when the root component is disposed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { bootstrap(HelloOnDestroyTickCmp, testProviders).then((ref) => { expect(() => ref.destroy()).not.toThrow(); - async.done(); - }); - })); - - it('should unregister change detectors when components are disposed', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - bootstrap(HelloRootCmp, testProviders).then((ref) => { - const appRef = ref.injector.get(ApplicationRef); - ref.destroy(); - expect(() => appRef.tick()).not.toThrow(); - async.done(); + done(); }); - })); + }); + + it('should unregister change detectors when components are disposed', done => { + bootstrap(HelloRootCmp, testProviders).then((ref) => { + const appRef = ref.injector.get(ApplicationRef); + ref.destroy(); + expect(() => appRef.tick()).not.toThrow(); + done(); + }, done.fail); + }); - it('should make the provided bindings available to the application component', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise = bootstrap( - HelloRootCmp3, [testProviders, {provide: 'appBinding', useValue: 'BoundValue'}]); + it('should make the provided bindings available to the application component', done => { + const refPromise = bootstrap( + HelloRootCmp3, [testProviders, {provide: 'appBinding', useValue: 'BoundValue'}]); - refPromise.then((ref) => { - expect(ref.injector.get('appBinding')).toEqual('BoundValue'); - async.done(); - }); - })); + refPromise.then((ref) => { + expect(ref.injector.get('appBinding')).toEqual('BoundValue'); + done(); + }, done.fail); + }); - it('should not override locale provided during bootstrap', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise = - bootstrap(HelloRootCmp, [testProviders], [{provide: LOCALE_ID, useValue: 'fr-FR'}]); + it('should not override locale provided during bootstrap', done => { + const refPromise = + bootstrap(HelloRootCmp, [testProviders], [{provide: LOCALE_ID, useValue: 'fr-FR'}]); - refPromise.then(ref => { - expect(ref.injector.get(LOCALE_ID)).toEqual('fr-FR'); - async.done(); - }); - })); + refPromise.then(ref => { + expect(ref.injector.get(LOCALE_ID)).toEqual('fr-FR'); + done(); + }, done.fail); + }); it('should avoid cyclic dependencies when root component requires Lifecycle through DI', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { const refPromise = bootstrap(HelloRootCmp4, testProviders); refPromise.then((ref) => { const appRef = ref.injector.get(ApplicationRef); expect(appRef).toBeDefined(); - async.done(); - }); - })); + done(); + }, done.fail); + }); + + it('should run platform initializers', done => { + inject([Log], (log: Log) => { + const p = createPlatformFactory(platformBrowserDynamic, 'someName', [ + {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init1'), multi: true}, + {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init2'), multi: true} + ])(); + + @NgModule({ + imports: [BrowserModule], + providers: [ + {provide: APP_INITIALIZER, useValue: log.fn('app_init1'), multi: true}, + {provide: APP_INITIALIZER, useValue: log.fn('app_init2'), multi: true} + ] + }) + class SomeModule { + ngDoBootstrap() {} + } - it('should run platform initializers', - inject([Log, AsyncTestCompleter], (log: Log, async: AsyncTestCompleter) => { - const p = createPlatformFactory(platformBrowserDynamic, 'someName', [ - {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init1'), multi: true}, - {provide: PLATFORM_INITIALIZER, useValue: log.fn('platform_init2'), multi: true} - ])(); - - @NgModule({ - imports: [BrowserModule], - providers: [ - {provide: APP_INITIALIZER, useValue: log.fn('app_init1'), multi: true}, - {provide: APP_INITIALIZER, useValue: log.fn('app_init2'), multi: true} - ] - }) - class SomeModule { - ngDoBootstrap() {} - } + expect(log.result()).toEqual('platform_init1; platform_init2'); + log.clear(); + p.bootstrapModule(SomeModule).then(() => { + expect(log.result()).toEqual('app_init1; app_init2'); + done(); + }, done.fail); + })(); + }); - expect(log.result()).toEqual('platform_init1; platform_init2'); - log.clear(); - p.bootstrapModule(SomeModule).then(() => { - expect(log.result()).toEqual('app_init1; app_init2'); - async.done(); - }); - })); + it('should remove styles when transitioning from a server render', done => { + @Component({ + selector: 'root', + template: 'root', + }) + class RootCmp { + } - it('should remove styles when transitioning from a server render', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - @Component({ - selector: 'root', - template: 'root', - }) - class RootCmp { - } + @NgModule({ + bootstrap: [RootCmp], + declarations: [RootCmp], + imports: [BrowserModule.withServerTransition({appId: 'my-app'})], + }) + class TestModule { + } - @NgModule({ - bootstrap: [RootCmp], - declarations: [RootCmp], - imports: [BrowserModule.withServerTransition({appId: 'my-app'})], - }) - class TestModule { - } + // First, set up styles to be removed. + const dom = getDOM(); + const platform = platformBrowserDynamic(); + const document = platform.injector.get(DOCUMENT); + const style = dom.createElement('style', document); + style.setAttribute('ng-transition', 'my-app'); + document.head.appendChild(style); + + const root = dom.createElement('root', document); + document.body.appendChild(root); + + platform.bootstrapModule(TestModule).then(() => { + const styles: HTMLElement[] = + Array.prototype.slice.apply(document.getElementsByTagName('style') || []); + styles.forEach(style => { + expect(style.getAttribute('ng-transition')).not.toBe('my-app'); + }); + done(); + }, done.fail); + }); - // First, set up styles to be removed. - const dom = getDOM(); - const platform = platformBrowserDynamic(); - const document = platform.injector.get(DOCUMENT); - const style = dom.createElement('style', document); - style.setAttribute('ng-transition', 'my-app'); - document.head.appendChild(style); - - const root = dom.createElement('root', document); - document.body.appendChild(root); - - platform.bootstrapModule(TestModule).then(() => { - const styles: HTMLElement[] = - Array.prototype.slice.apply(document.getElementsByTagName('style') || []); - styles.forEach(style => { - expect(style.getAttribute('ng-transition')).not.toBe('my-app'); - }); - async.done(); - }); - })); + it('should register each application with the testability registry', done => { + const refPromise1: Promise> = bootstrap(HelloRootCmp, testProviders); + const refPromise2: Promise> = bootstrap(HelloRootCmp2, testProviders); - it('should register each application with the testability registry', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise1: Promise> = bootstrap(HelloRootCmp, testProviders); - const refPromise2: Promise> = bootstrap(HelloRootCmp2, testProviders); - - Promise.all([refPromise1, refPromise2]).then((refs: ComponentRef[]) => { - const registry = refs[0].injector.get(TestabilityRegistry); - const testabilities = - [refs[0].injector.get(Testability), refs[1].injector.get(Testability)]; - Promise.all(testabilities).then((testabilities: Testability[]) => { - expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]); - expect(registry.findTestabilityInTree(el2)).toEqual(testabilities[1]); - async.done(); - }); - }); - })); + Promise.all([refPromise1, refPromise2]).then((refs: ComponentRef[]) => { + const registry = refs[0].injector.get(TestabilityRegistry); + const testabilities = + [refs[0].injector.get(Testability), refs[1].injector.get(Testability)]; - it('should allow to pass schemas', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - bootstrap(HelloCmpUsingCustomElement, testProviders).then((compRef) => { - expect(el).toHaveText('hello world!'); - async.done(); - }); - })); + Promise.all(testabilities).then((testabilities: Testability[]) => { + expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]); + expect(registry.findTestabilityInTree(el2)).toEqual(testabilities[1]); + + done(); + }, done.fail); + }, done.fail); + }); + + it('should allow to pass schemas', done => { + bootstrap(HelloCmpUsingCustomElement, testProviders).then(() => { + expect(el).toHaveText('hello world!'); + done(); + }, done.fail); + }); describe('change detection', () => { const log: string[] = []; @@ -529,7 +491,7 @@ function bootstrap( } it('should be triggered for all bootstrapped components in case change happens in one of them', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + done => { @NgModule({ imports: [BrowserModule], declarations: [CompA, CompB], @@ -551,31 +513,30 @@ function bootstrap( expect(log).toContain('CompA:ngDoCheck'); expect(log).toContain('CompB:ngDoCheck'); - async.done(); - }); - })); - - - it('should work in isolation for each component bootstrapped individually', - inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { - const refPromise1 = bootstrap(CompA); - const refPromise2 = bootstrap(CompB); - Promise.all([refPromise1, refPromise2]).then((refs) => { - log.length = 0; - el.querySelectorAll('#button-a')[0].click(); - expect(log).toContain('CompA:onClick'); - expect(log).toContain('CompA:ngDoCheck'); - expect(log).not.toContain('CompB:ngDoCheck'); + done(); + }, done.fail); + }); - log.length = 0; - el2.querySelectorAll('#button-b')[0].click(); - expect(log).toContain('CompB:onClick'); - expect(log).toContain('CompB:ngDoCheck'); - expect(log).not.toContain('CompA:ngDoCheck'); - async.done(); - }); - })); + it('should work in isolation for each component bootstrapped individually', done => { + const refPromise1 = bootstrap(CompA); + const refPromise2 = bootstrap(CompB); + Promise.all([refPromise1, refPromise2]).then((refs) => { + log.length = 0; + el.querySelectorAll('#button-a')[0].click(); + expect(log).toContain('CompA:onClick'); + expect(log).toContain('CompA:ngDoCheck'); + expect(log).not.toContain('CompB:ngDoCheck'); + + log.length = 0; + el2.querySelectorAll('#button-b')[0].click(); + expect(log).toContain('CompB:onClick'); + expect(log).toContain('CompB:ngDoCheck'); + expect(log).not.toContain('CompA:ngDoCheck'); + + done(); + }, done.fail); + }); }); }); } diff --git a/packages/platform-browser/test/browser/tools/spies.ts b/packages/platform-browser/test/browser/tools/spies.ts deleted file mode 100644 index c7c3c0d47633..000000000000 --- a/packages/platform-browser/test/browser/tools/spies.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @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 - */ - -import {Injector, ɵglobal as global} from '@angular/core'; -import {ApplicationRef} from '@angular/core/src/application_ref'; -import {SpyObject} from '@angular/core/testing/src/testing_internal'; - -export class SpyApplicationRef extends SpyObject { - constructor() { - super(ApplicationRef); - } -} - -export class SpyComponentRef extends SpyObject { - injector: any /** TODO #9100 */; - constructor() { - super(); - this.injector = - Injector.create([{provide: ApplicationRef, useClass: SpyApplicationRef, deps: []}]); - } -} - -export function callNgProfilerTimeChangeDetection(config?: any /** TODO #9100 */): void { - (global).ng.profiler.timeChangeDetection(config); -} diff --git a/packages/platform-browser/test/browser/tools/tools_spec.ts b/packages/platform-browser/test/browser/tools/tools_spec.ts index 3d26ddefe486..8c078a81382d 100644 --- a/packages/platform-browser/test/browser/tools/tools_spec.ts +++ b/packages/platform-browser/test/browser/tools/tools_spec.ts @@ -6,15 +6,22 @@ * found in the LICENSE file at https://angular.io/license */ +import {ApplicationRef, Injector, ɵglobal as global} from '@angular/core'; import {disableDebugTools, enableDebugTools} from '@angular/platform-browser'; -import {callNgProfilerTimeChangeDetection, SpyComponentRef} from './spies'; - { describe('profiler', () => { if (isNode) return; + beforeEach(() => { - enableDebugTools((new SpyComponentRef())); + enableDebugTools({ + injector: Injector.create([{ + provide: ApplicationRef, + useValue: jasmine.createSpyObj( + 'ApplicationRef', ['bootstrap', 'tick', 'attachView', 'detachView']), + deps: [] + }]) + } as any); }); afterEach(() => { @@ -30,3 +37,7 @@ import {callNgProfilerTimeChangeDetection, SpyComponentRef} from './spies'; }); }); } + +export function callNgProfilerTimeChangeDetection(config?: any /** TODO #9100 */): void { + (global).ng.profiler.timeChangeDetection(config); +} diff --git a/packages/platform-browser/test/dom/events/event_manager_spec.ts b/packages/platform-browser/test/dom/events/event_manager_spec.ts index 0f08c19018c9..24014ffb7c4b 100644 --- a/packages/platform-browser/test/dom/events/event_manager_spec.ts +++ b/packages/platform-browser/test/dom/events/event_manager_spec.ts @@ -8,7 +8,6 @@ import {ɵgetDOM as getDOM} from '@angular/common'; import {NgZone} from '@angular/core/src/zone/ng_zone'; -import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {DomEventsPlugin} from '@angular/platform-browser/src/dom/events/dom_events'; import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager'; import {createMouseEvent, el} from '../../../testing/src/browser_util'; diff --git a/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts b/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts index 715cc5b48279..a6e1007c809e 100644 --- a/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts +++ b/packages/platform-browser/test/dom/events/hammer_gestures_spec.ts @@ -7,7 +7,6 @@ */ import {NgZone} from '@angular/core'; import {fakeAsync, inject, tick} from '@angular/core/testing'; -import {afterEach, beforeEach, describe, expect, it,} from '@angular/core/testing/src/testing_internal'; import {EventManager} from '@angular/platform-browser'; import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-browser/src/dom/events/hammer_gestures'; @@ -58,7 +57,7 @@ import {HammerGestureConfig, HammerGesturesPlugin,} from '@angular/platform-brow let originalHammerGlobal: any; // Fake Hammer instance ("mc") used to test the underlying event registration. - let fakeHammerInstance: {on: () => void, off: () => void}; + let fakeHammerInstance: {on: jasmine.Spy, off: jasmine.Spy}; // Inject the NgZone so that we can make it available to the plugin through a fake // EventManager. diff --git a/packages/platform-browser/test/dom/events/key_events_spec.ts b/packages/platform-browser/test/dom/events/key_events_spec.ts index f29d909759bf..b422577a6b9b 100644 --- a/packages/platform-browser/test/dom/events/key_events_spec.ts +++ b/packages/platform-browser/test/dom/events/key_events_spec.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {describe, expect, it} from '@angular/core/testing/src/testing_internal'; import {KeyEventsPlugin} from '@angular/platform-browser/src/dom/events/key_events'; { diff --git a/packages/platform-browser/test/dom/shared_styles_host_spec.ts b/packages/platform-browser/test/dom/shared_styles_host_spec.ts index ac00e735d12d..2891d54ccdac 100644 --- a/packages/platform-browser/test/dom/shared_styles_host_spec.ts +++ b/packages/platform-browser/test/dom/shared_styles_host_spec.ts @@ -7,7 +7,6 @@ */ import {ɵgetDOM as getDOM} from '@angular/common'; -import {beforeEach, describe, it} from '@angular/core/testing/src/testing_internal'; import {DomSharedStylesHost} from '@angular/platform-browser/src/dom/shared_styles_host'; import {expect} from '@angular/platform-browser/testing/src/matchers'; diff --git a/packages/platform-browser/test/security/dom_sanitization_service_spec.ts b/packages/platform-browser/test/security/dom_sanitization_service_spec.ts index ee9ecb6a0a18..147d0756a206 100644 --- a/packages/platform-browser/test/security/dom_sanitization_service_spec.ts +++ b/packages/platform-browser/test/security/dom_sanitization_service_spec.ts @@ -7,15 +7,14 @@ */ import {SecurityContext} from '@angular/core'; -import * as t from '@angular/core/testing/src/testing_internal'; import {DomSanitizerImpl} from '@angular/platform-browser/src/security/dom_sanitization_service'; { - t.describe('DOM Sanitization Service', () => { - t.it('accepts resource URL values for resource contexts', () => { + describe('DOM Sanitization Service', () => { + it('accepts resource URL values for resource contexts', () => { const svc = new DomSanitizerImpl(null); const resourceUrl = svc.bypassSecurityTrustResourceUrl('http://hello/world'); - t.expect(svc.sanitize(SecurityContext.URL, resourceUrl)).toBe('http://hello/world'); + expect(svc.sanitize(SecurityContext.URL, resourceUrl)).toBe('http://hello/world'); }); }); } diff --git a/packages/platform-browser/test/testing_public_spec.ts b/packages/platform-browser/test/testing_public_spec.ts index f1e5c8430ff7..253507fbe333 100644 --- a/packages/platform-browser/test/testing_public_spec.ts +++ b/packages/platform-browser/test/testing_public_spec.ts @@ -335,9 +335,9 @@ const bTok = new InjectionToken('b'); }); describe('components with template url', () => { - beforeEach(waitForAsync(() => { + beforeEach(waitForAsync(async () => { TestBed.configureTestingModule({declarations: [CompWithUrlTemplate]}); - TestBed.compileComponents(); + await TestBed.compileComponents(); })); isBrowser && diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 094c4de6684d..bd76ed543284 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -10,7 +10,6 @@ import {CommonModule, Location} from '@angular/common'; import {SpyLocation} from '@angular/common/testing'; import {ChangeDetectionStrategy, Component, Injectable, NgModule, NgModuleFactoryLoader, NgModuleRef, NgZone, OnDestroy, ViewChild, ɵConsole as Console, ɵNoopNgZone as NoopNgZone} from '@angular/core'; import {ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; -import {describe} from '@angular/core/testing/src/testing_internal'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, CanActivate, CanDeactivate, ChildActivationEnd, ChildActivationStart, DefaultUrlSerializer, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, Navigation, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, ParamMap, Params, PreloadAllModules, PreloadingStrategy, PRIMARY_OUTLET, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouteReuseStrategy, RouterEvent, RouterLink, RouterLinkWithHref, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlSerializer, UrlTree} from '@angular/router'; diff --git a/scripts/release/publish-latest b/scripts/release/publish-latest index 57b2d34273e0..3a82453361a6 100755 --- a/scripts/release/publish-latest +++ b/scripts/release/publish-latest @@ -20,5 +20,5 @@ $BAZEL build --config=release $NPM_PACKAGE_LABELS # publish all packages in sequence to make it easier to spot any errors or warnings for packageLabel in $NPM_PACKAGE_LABELS; do echo "publishing $packageLabel" - $BAZEL run --config=release -- ${packageLabel}.publish --access public --tag latest + $BAZEL run --config=release -- ${packageLabel}.publish --access public --tag latest --registry https://wombat-dressing-room.appspot.com done diff --git a/scripts/release/publish-next b/scripts/release/publish-next index 5f49d1b79ef5..d7d95c447455 100755 --- a/scripts/release/publish-next +++ b/scripts/release/publish-next @@ -20,5 +20,5 @@ $BAZEL build --config=release $NPM_PACKAGE_LABELS # publish all packages in sequence to make it easier to spot any errors or warnings for packageLabel in $NPM_PACKAGE_LABELS; do echo "publishing $packageLabel" - $BAZEL run --config=release -- ${packageLabel}.publish --access public --tag next + $BAZEL run --config=release -- ${packageLabel}.publish --access public --tag next --registry https://wombat-dressing-room.appspot.com done diff --git a/test-main.js b/test-main.js index b935f6bf39db..66f74c22d47e 100644 --- a/test-main.js +++ b/test-main.js @@ -9,7 +9,7 @@ // Tun on full stack traces in errors to help debugging Error.stackTraceLimit = Infinity; -jasmine.DEFAULT_TIMEOUT_INTERVAL = 100; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000; // Cancel Karma's synchronous start, // we will call `__karma__.start()` later, once all the specs are loaded.