Skip to content

Commit

Permalink
test: update acceptance core tests to work with es2015 (#44505)
Browse files Browse the repository at this point in the history
Updates the acceptance core tests to work with ES2015 devmode output.
There were two issues surfacing:

* The NodeJS test execution failed because Domino does not handle
  destructuring syntax properly. This is because `Node.children` is not
  an iterable.

* `forwardRef` does not work with ES2015 and TypeScript's decorator
  downlevel emit. This is a known limitation we work around in Angular
  applications by either compiling tests with the Angular compiler, or
  by running a custom decorator downlevel transform (like in the CLI).

PR Close #44505
  • Loading branch information
devversion authored and atscott committed Jan 5, 2022
1 parent cefe9f0 commit b734ceb
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 64 deletions.
25 changes: 24 additions & 1 deletion packages/core/test/acceptance/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ts_library")
load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ng_module", "ts_library")

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

SPEC_FILES_WITH_FORWARD_REFS = [
"di_forward_ref_spec.ts",
]

ts_library(
name = "acceptance_lib",
testonly = True,
srcs = glob(
["**/*.ts"],
exclude = SPEC_FILES_WITH_FORWARD_REFS,
),
# Visible to //:saucelabs_unit_tests
visibility = ["//:__pkg__"],
Expand Down Expand Up @@ -35,11 +40,28 @@ ts_library(
],
)

# Note: The `forward_ref` example tests are built through this `ng_module` sub-target.
# This is done so that DI decorator/type metadata is processed manually by the compiler
# ahead of time. We cannot rely on the official TypeScript decorator downlevel emit (for JIT),
# as the output is not compatible with `forwardRef` and ES2015+. More details here:
# https://github.com/angular/angular/commit/323651bd38909b0f4226bcb6c8f5abafa91cf9d9.
# https://github.com/microsoft/TypeScript/issues/27519.
ng_module(
name = "forward_ref_test_lib",
testonly = True,
srcs = SPEC_FILES_WITH_FORWARD_REFS,
deps = [
"//packages/core",
"//packages/core/testing",
],
)

jasmine_node_test(
name = "acceptance",
bootstrap = ["//tools/testing:node_es2015"],
deps = [
":acceptance_lib",
":forward_ref_test_lib",
"//packages/zone.js/lib:zone_d_ts",
"@npm//base64-js",
"@npm//source-map",
Expand All @@ -50,5 +72,6 @@ karma_web_test_suite(
name = "acceptance_web",
deps = [
":acceptance_lib",
":forward_ref_test_lib",
],
)
67 changes: 67 additions & 0 deletions packages/core/test/acceptance/di_forward_ref_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @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 {Component, Directive, forwardRef, Host, Inject, ViewChild} from '@angular/core';
import {TestBed} from '@angular/core/testing';

// **NOTE**: More details on why tests relying on `forwardRef` are put into this
// file can be found in the `BUILD.bazel` file declaring the forward ref test target.

describe('di with forwardRef', () => {
describe('directive injection', () => {
it('should throw if directives try to inject each other', () => {
@Directive({selector: '[dirB]'})
class DirectiveB {
constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {}
}

@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(siblingDir: DirectiveB) {}
}

@Component({template: '<div dirA dirB></div>'})
class MyComp {
}

TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
expect(() => TestBed.createComponent(MyComp))
.toThrowError(
'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200');
});

describe('flags', () => {
describe('@Host', () => {
it('should find host component on the host itself', () => {
@Directive({selector: '[dirComp]'})
class DirectiveComp {
constructor(@Inject(forwardRef(() => MyComp)) @Host() public comp: MyComp) {}
}

@Component({selector: 'my-comp', template: '<div dirComp></div>'})
class MyComp {
@ViewChild(DirectiveComp) dirComp!: DirectiveComp;
}

@Component({template: '<my-comp></my-comp>', jit: true})
class MyApp {
@ViewChild(MyComp) myComp!: MyComp;
}

TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();

const myComp = fixture.componentInstance.myComp;
const dirComp = myComp.dirComp;
expect(dirComp.comp).toBe(myComp);
});
});
});
});
});
56 changes: 5 additions & 51 deletions packages/core/test/acceptance/di_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,27 +684,6 @@ describe('di', () => {
expect(cmp.componentInstance.testB.a).toBeNull();
});

it('should throw if directives try to inject each other', () => {
@Directive({selector: '[dirB]'})
class DirectiveB {
constructor(@Inject(forwardRef(() => DirectiveA)) siblingDir: DirectiveA) {}
}

@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(siblingDir: DirectiveB) {}
}

@Component({template: '<div dirA dirB></div>'})
class MyComp {
}

TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
expect(() => TestBed.createComponent(MyComp))
.toThrowError(
'NG0200: Circular dependency in DI detected for DirectiveA. Find more at https://angular.io/errors/NG0200');
});

it('should throw if directive tries to inject itself', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
Expand Down Expand Up @@ -1781,31 +1760,6 @@ describe('di', () => {
expect(dirString.s).toBe('Foo');
});

it('should find host component on the host itself', () => {
@Directive({selector: '[dirComp]'})
class DirectiveComp {
constructor(@Inject(forwardRef(() => MyComp)) @Host() public comp: MyComp) {}
}

@Component({selector: 'my-comp', template: '<div dirComp></div>'})
class MyComp {
@ViewChild(DirectiveComp) dirComp!: DirectiveComp;
}

@Component({template: '<my-comp></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp!: MyComp;
}

TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();

const myComp = fixture.componentInstance.myComp;
const dirComp = myComp.dirComp;
expect(dirComp.comp).toBe(myComp);
});

it('should not find providers on the host itself', () => {
@Component({
selector: 'my-comp',
Expand Down Expand Up @@ -1882,19 +1836,19 @@ describe('di', () => {
});

it('should not find component above the host', () => {
@Component({template: '<my-comp></my-comp>'})
class MyApp {
}

@Directive({selector: '[dirComp]'})
class DirectiveComp {
constructor(@Inject(forwardRef(() => MyApp)) @Host() public comp: MyApp) {}
constructor(@Host() public comp: MyApp) {}
}

@Component({selector: 'my-comp', template: '<div dirComp></div>'})
class MyComp {
}

@Component({template: '<my-comp></my-comp>'})
class MyApp {
}

TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
expect(() => TestBed.createComponent(MyApp))
.toThrowError(
Expand Down
13 changes: 11 additions & 2 deletions packages/core/test/acceptance/property_binding_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,12 @@ describe('property bindings', () => {
const fixture = TestBed.createComponent(App);
const myDir = fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir);
const myDirB = fixture.debugElement.query(By.directive(MyDirB)).injector.get(MyDirB);
const [buttonEl, listboxEl] = fixture.nativeElement.children;
const fixtureElements = fixture.nativeElement.children;

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const buttonEl = fixtureElements[0];
const listboxEl = fixtureElements[1];

fixture.detectChanges();

expect(buttonEl.getAttribute('role')).toBe('button');
Expand Down Expand Up @@ -581,7 +586,11 @@ describe('property bindings', () => {

expect(fixture.nativeElement.children.length).toBe(2);

const [comp1, comp2] = fixture.nativeElement.children;
const compElements = fixture.nativeElement.children;

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const comp1 = compElements[0];
const comp2 = compElements[1];

expect(comp1.tagName).toBe('COMP');
expect(comp2.tagName).toBe('COMP');
Expand Down
38 changes: 28 additions & 10 deletions packages/core/test/acceptance/styling_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ describe('styling', () => {

TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const staticDiv = fixture.nativeElement.querySelectorAll('div')[0];

const [staticDiv] = fixture.nativeElement.querySelectorAll('div');
expect(getSortedClassName(staticDiv)).toEqual('STATIC');
expect(getSortedStyle(staticDiv)).toEqual('color: blue;');
});
Expand Down Expand Up @@ -335,7 +335,12 @@ describe('styling', () => {
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();

const [div1, div2] = fixture.nativeElement.querySelectorAll('div');
const divs = fixture.nativeElement.querySelectorAll('div');

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const div1 = divs[0];
const div2 = divs[1];

// Static value `class="s1"` is always written to the DOM.
expect(div1.className).toEqual('s1');
expect(div1.getAttribute('shadow-class')).toEqual('s1 d1');
Expand Down Expand Up @@ -367,7 +372,12 @@ describe('styling', () => {
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();

const [divStatic, divBinding] = fixture.nativeElement.querySelectorAll('div');
const divs = fixture.nativeElement.querySelectorAll('div');

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const divStatic = divs[0];
const divBinding = divs[1];

expectClass(divStatic).toEqual({'DIRECTIVE': true, 's1': true});
expect(divStatic.getAttribute('shadow-class')).toEqual('s1');

Expand Down Expand Up @@ -398,7 +408,12 @@ describe('styling', () => {
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();

const [divStatic, divBinding] = fixture.nativeElement.querySelectorAll('div');
const divs = fixture.nativeElement.querySelectorAll('div');

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const divStatic = divs[0];
const divBinding = divs[1];

expectStyle(divStatic).toEqual({'color': 'red', 'width': '1px'});
expect(divStatic.getAttribute('shadow-style')).toEqual('width: 1px;');

Expand Down Expand Up @@ -2451,11 +2466,10 @@ describe('styling', () => {

const items = fixture.nativeElement.querySelectorAll('.item');
expect(items.length).toEqual(4);
const [a, b, c, d] = items;
expect(a.style.height).toEqual('0px');
expect(b.style.height).toEqual('100px');
expect(c.style.height).toEqual('200px');
expect(d.style.height).toEqual('300px');
expect(items[0].style.height).toEqual('0px');
expect(items[1].style.height).toEqual('100px');
expect(items[2].style.height).toEqual('200px');
expect(items[3].style.height).toEqual('300px');

const section = fixture.nativeElement.querySelector('section');
const p = fixture.nativeElement.querySelector('p');
Expand Down Expand Up @@ -3342,7 +3356,11 @@ describe('styling', () => {
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();

const [div1, div2] = fixture.nativeElement.querySelectorAll('div') as HTMLDivElement[];
const divs = fixture.nativeElement.querySelectorAll('div') as HTMLDivElement[];

// TODO: Use destructuring once Domino supports native ES2015, or when jsdom is used.
const div1 = divs[0];
const div2 = divs[1];

expect(div1.className).toBe('');
expect(div2.className).toBe('');
Expand Down

0 comments on commit b734ceb

Please sign in to comment.