test: update acceptance core tests to work with es2015 (#44505)

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
This commit is contained in:
Paul Gschwendtner 2021-12-17 19:25:48 +01:00 committed by Andrew Scott
parent 92806ff042
commit 807c6a4e10
5 changed files with 135 additions and 64 deletions

View file

@ -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__"],
@ -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",
@ -50,5 +72,6 @@ karma_web_test_suite(
name = "acceptance_web",
deps = [
":acceptance_lib",
":forward_ref_test_lib",
],
)

View file

@ -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);
});
});
});
});
});

View file

@ -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 {
@ -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',
@ -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(

View file

@ -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');
@ -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');

View file

@ -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;');
});
@ -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');
@ -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');
@ -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;');
@ -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');
@ -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('');