test(ivy): add ability to find already passing tests (#27449)
PR Close #27449
This commit is contained in:
parent
4b9948c1be
commit
f52600e261
|
@ -74,9 +74,9 @@ describe('@angular/core ng_package', () => {
|
|||
|
||||
describe('typescript support', () => {
|
||||
|
||||
fixmeIvy('FW-738: ngtsc doesn\'t generate flat index files') &&
|
||||
it('should have an index d.ts file',
|
||||
() => { expect(shx.cat('core.d.ts')).toContain(`export *`); });
|
||||
fixmeIvy('FW-738: ngtsc doesn\'t generate flat index files')
|
||||
.it('should have an index d.ts file',
|
||||
() => { expect(shx.cat('core.d.ts')).toContain(`export *`); });
|
||||
|
||||
it('should not have amd module names',
|
||||
() => { expect(shx.cat('public_api.d.ts')).not.toContain('<amd-module name'); });
|
||||
|
@ -90,8 +90,8 @@ describe('@angular/core ng_package', () => {
|
|||
});
|
||||
|
||||
|
||||
obsoleteInIvy('metadata files are no longer needed or produced in Ivy') &&
|
||||
describe('angular metadata', () => {
|
||||
obsoleteInIvy('metadata files are no longer needed or produced in Ivy')
|
||||
.describe('angular metadata', () => {
|
||||
|
||||
it('should have metadata.json files',
|
||||
() => { expect(shx.cat('core.metadata.json')).toContain(`"__symbolic":"module"`); });
|
||||
|
@ -112,8 +112,8 @@ describe('@angular/core ng_package', () => {
|
|||
.toMatch(/@license Angular v\d+\.\d+\.\d+(?!-PLACEHOLDER)/);
|
||||
});
|
||||
|
||||
obsoleteInIvy('we no longer need to export private symbols') &&
|
||||
it('should have been built from the generated bundle index', () => {
|
||||
obsoleteInIvy('we no longer need to export private symbols')
|
||||
.it('should have been built from the generated bundle index', () => {
|
||||
expect(shx.cat('fesm2015/core.js')).toMatch('export {.*makeParamDecorator');
|
||||
});
|
||||
});
|
||||
|
@ -147,9 +147,9 @@ describe('@angular/core ng_package', () => {
|
|||
expect(shx.cat('fesm5/core.js')).toMatch('import {.*__extends');
|
||||
});
|
||||
|
||||
obsoleteInIvy('we no longer need to export private symbols') &&
|
||||
it('should have been built from the generated bundle index',
|
||||
() => { expect(shx.cat('fesm5/core.js')).toMatch('export {.*makeParamDecorator'); });
|
||||
obsoleteInIvy('we no longer need to export private symbols')
|
||||
.it('should have been built from the generated bundle index',
|
||||
() => { expect(shx.cat('fesm5/core.js')).toMatch('export {.*makeParamDecorator'); });
|
||||
});
|
||||
|
||||
|
||||
|
@ -228,13 +228,13 @@ describe('@angular/core ng_package', () => {
|
|||
() => { expect(shx.cat(typingsFile)).toContain('export * from \'./public_api\';'); });
|
||||
|
||||
obsoleteInIvy(
|
||||
'now that we don\'t need metadata files, we don\'t need these redirects to help resolve paths to them') &&
|
||||
it('should have an \'redirect\' d.ts file in the parent dir',
|
||||
() => { expect(shx.cat('testing.d.ts')).toContain(`export *`); });
|
||||
'now that we don\'t need metadata files, we don\'t need these redirects to help resolve paths to them')
|
||||
.it('should have an \'redirect\' d.ts file in the parent dir',
|
||||
() => { expect(shx.cat('testing.d.ts')).toContain(`export *`); });
|
||||
});
|
||||
|
||||
obsoleteInIvy('metadata files are no longer needed or produced in Ivy') &&
|
||||
describe('angular metadata file', () => {
|
||||
obsoleteInIvy('metadata files are no longer needed or produced in Ivy')
|
||||
.describe('angular metadata file', () => {
|
||||
it('should have a \'redirect\' metadata.json file next to the d.ts file', () => {
|
||||
expect(shx.cat('testing.metadata.json'))
|
||||
.toContain(
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('ngc_wrapped', () => {
|
|||
// fixmeIvy placeholder to prevent jasmine from erroring out because there are no specs
|
||||
it('should be removed once the fixmeIvy below is resolved', () => {});
|
||||
|
||||
fixmeIvy('FW-741: ngtsc breaks tsc module resolution') && it('should work', () => {
|
||||
fixmeIvy('FW-741: ngtsc breaks tsc module resolution').it('should work', () => {
|
||||
const {read, write, runOneBuild, writeConfig, shouldExist, basePath} = setup();
|
||||
|
||||
write('some_project/index.ts', `
|
||||
|
|
|
@ -157,22 +157,22 @@ describe('insert/remove', () => {
|
|||
expect(fixture.nativeElement).toHaveText('baz');
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-739: destroy on NgModuleRef is not being called') &&
|
||||
it('should clean up moduleRef, if supplied', async(() => {
|
||||
let destroyed = false;
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
fixmeIvy('FW-739: destroy on NgModuleRef is not being called')
|
||||
.it('should clean up moduleRef, if supplied', async(() => {
|
||||
let destroyed = false;
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.componentInstance.module = compiler.compileModuleSync(TestModule2);
|
||||
fixture.componentInstance.currentComponent = Module2InjectedComponent;
|
||||
fixture.detectChanges();
|
||||
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'] !;
|
||||
spyOn(moduleRef, 'destroy').and.callThrough();
|
||||
const moduleRef = fixture.componentInstance.ngComponentOutlet['_moduleRef'] !;
|
||||
spyOn(moduleRef, 'destroy').and.callThrough();
|
||||
|
||||
expect(moduleRef.destroy).not.toHaveBeenCalled();
|
||||
fixture.destroy();
|
||||
expect(moduleRef.destroy).toHaveBeenCalled();
|
||||
}));
|
||||
expect(moduleRef.destroy).not.toHaveBeenCalled();
|
||||
fixture.destroy();
|
||||
expect(moduleRef.destroy).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
|
||||
const compiler = TestBed.get(Compiler) as Compiler;
|
||||
|
|
|
@ -168,8 +168,8 @@ describe('ngInjectableDef Bazel Integration', () => {
|
|||
expect(TestBed.get(INJECTOR).get('foo')).toEqual('bar');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-646: Directive providers don\'t support primitive types') &&
|
||||
it('Component injector understands requests for INJECTABLE', () => {
|
||||
fixmeIvy('FW-646: Directive providers don\'t support primitive types')
|
||||
.it('Component injector understands requests for INJECTABLE', () => {
|
||||
@Component({
|
||||
selector: 'test-cmp',
|
||||
template: 'test',
|
||||
|
|
|
@ -41,8 +41,8 @@ describe('Ivy NgModule', () => {
|
|||
|
||||
it('works', () => { createInjector(JitAppModule); });
|
||||
|
||||
fixmeIvy('FW-645: jit doesn\'t support forwardRefs') &&
|
||||
it('throws an error on circular module dependencies', () => {
|
||||
fixmeIvy('FW-645: jit doesn\'t support forwardRefs')
|
||||
.it('throws an error on circular module dependencies', () => {
|
||||
@NgModule({
|
||||
imports: [forwardRef(() => BModule)],
|
||||
})
|
||||
|
|
|
@ -60,7 +60,7 @@ describe('ngtools_api (deprecated)', () => {
|
|||
});
|
||||
}
|
||||
|
||||
fixmeIvy('FW-629: ngtsc lists lazy routes') && it('should list lazy routes recursively', () => {
|
||||
fixmeIvy('FW-629: ngtsc lists lazy routes').it('should list lazy routes recursively', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, host, options} = createProgram(['src/main.ts']);
|
||||
const routes = NgTools_InternalApi_NG_2.listLazyRoutes({
|
||||
|
|
|
@ -35,26 +35,26 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => {
|
|||
class MyAppModule {
|
||||
}
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
|
||||
const MyAppModuleFactory = new NgModuleFactory(MyAppModule);
|
||||
const moduleRef = await getTestBed().platform.bootstrapModuleFactory(
|
||||
MyAppModuleFactory, {ngZone: 'noop'});
|
||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
|
||||
expect(document.body.innerHTML)
|
||||
.toEqual('<hello-world><div>Hello World</div></hello-world>');
|
||||
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
|
||||
fixmeIvy('unknown').it(
|
||||
'should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
|
||||
const MyAppModuleFactory = new NgModuleFactory(MyAppModule);
|
||||
const moduleRef = await getTestBed().platform.bootstrapModuleFactory(
|
||||
MyAppModuleFactory, {ngZone: 'noop'});
|
||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
|
||||
expect(document.body.innerHTML)
|
||||
.toEqual('<hello-world><div>Hello World</div></hello-world>');
|
||||
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
|
||||
|
||||
helloWorldComponent.name = 'Mundo';
|
||||
appRef.tick();
|
||||
expect(document.body.innerHTML)
|
||||
.toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
|
||||
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
|
||||
helloWorldComponent.name = 'Mundo';
|
||||
appRef.tick();
|
||||
expect(document.body.innerHTML)
|
||||
.toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
|
||||
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
|
||||
|
||||
// Cleanup TestabilityRegistry
|
||||
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
|
||||
registry.unregisterAllApplications();
|
||||
}));
|
||||
// Cleanup TestabilityRegistry
|
||||
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
|
||||
registry.unregisterAllApplications();
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
|
@ -74,63 +74,63 @@ class SomeComponent {
|
|||
return MyModule;
|
||||
}
|
||||
|
||||
fixmeIvy('FW-776: Cannot bootstrap as there are still asynchronous initializers running') &&
|
||||
it('should bootstrap a component from a child module',
|
||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
})
|
||||
class SomeComponent {
|
||||
}
|
||||
fixmeIvy('FW-776: Cannot bootstrap as there are still asynchronous initializers running')
|
||||
.it('should bootstrap a component from a child module',
|
||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: 'hello', useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent],
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({
|
||||
providers: [{provide: 'hello', useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent],
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
createRootEl();
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed);
|
||||
const cmpFactory =
|
||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||
const component = app.bootstrap(cmpFactory);
|
||||
createRootEl();
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed);
|
||||
const cmpFactory =
|
||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||
const component = app.bootstrap(cmpFactory);
|
||||
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get('hello')).toEqual('component');
|
||||
})));
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get('hello')).toEqual('component');
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-776: Cannot bootstrap as there are still asynchronous initializers running') &&
|
||||
it('should bootstrap a component with a custom selector',
|
||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
})
|
||||
class SomeComponent {
|
||||
}
|
||||
fixmeIvy('FW-776: Cannot bootstrap as there are still asynchronous initializers running')
|
||||
.it('should bootstrap a component with a custom selector',
|
||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||
@Component({
|
||||
selector: 'bootstrap-app',
|
||||
template: '',
|
||||
})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: 'hello', useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent],
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({
|
||||
providers: [{provide: 'hello', useValue: 'component'}],
|
||||
declarations: [SomeComponent],
|
||||
entryComponents: [SomeComponent],
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
createRootEl('custom-selector');
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed);
|
||||
const cmpFactory =
|
||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||
const component = app.bootstrap(cmpFactory, 'custom-selector');
|
||||
createRootEl('custom-selector');
|
||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||
const module = modFactory.create(TestBed);
|
||||
const cmpFactory =
|
||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||
const component = app.bootstrap(cmpFactory, 'custom-selector');
|
||||
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get('hello')).toEqual('component');
|
||||
})));
|
||||
// The component should see the child module providers
|
||||
expect(component.injector.get('hello')).toEqual('component');
|
||||
})));
|
||||
|
||||
describe('ApplicationRef', () => {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
||||
|
|
|
@ -33,10 +33,10 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec'
|
|||
})('foo', 'bar');
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') && it('should work with inject()',
|
||||
fakeAsync(inject([Parser], (parser: any /** TODO #9100 */) => {
|
||||
expect(parser).toBeAnInstanceOf(Parser);
|
||||
})));
|
||||
fixmeIvy('unknown').it(
|
||||
'should work with inject()', fakeAsync(inject([Parser], (parser: any /** TODO #9100 */) => {
|
||||
expect(parser).toBeAnInstanceOf(Parser);
|
||||
})));
|
||||
|
||||
it('should throw on nested calls', () => {
|
||||
expect(() => {
|
||||
|
|
|
@ -15,8 +15,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
describe('forwardRef integration', function() {
|
||||
beforeEach(() => { TestBed.configureTestingModule({imports: [Module], declarations: [App]}); });
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should instantiate components which are declared using forwardRef', () => {
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should instantiate components which are declared using forwardRef', () => {
|
||||
const a =
|
||||
TestBed.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]}).createComponent(App);
|
||||
a.detectChanges();
|
||||
|
|
|
@ -69,21 +69,20 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(childComp.cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should not be able to get components from a parent component (content hierarchy)',
|
||||
() => {
|
||||
TestBed.overrideComponent(
|
||||
MainComp, {set: {template: '<child><nested></nested></child>'}});
|
||||
TestBed.overrideComponent(ChildComp, {set: {template: '<ng-content></ng-content>'}});
|
||||
fixmeIvy('unknown').it(
|
||||
'should not be able to get components from a parent component (content hierarchy)', () => {
|
||||
TestBed.overrideComponent(
|
||||
MainComp, {set: {template: '<child><nested></nested></child>'}});
|
||||
TestBed.overrideComponent(ChildComp, {set: {template: '<ng-content></ng-content>'}});
|
||||
|
||||
const compFixture = TestBed.createComponent(MainComp);
|
||||
const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
|
||||
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
|
||||
.toBe(ChildComp);
|
||||
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
|
||||
.toThrow(noComponentFactoryError(NestedChildComp));
|
||||
});
|
||||
const compFixture = TestBed.createComponent(MainComp);
|
||||
const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
|
||||
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
|
||||
.toBe(ChildComp);
|
||||
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
|
||||
.toThrow(noComponentFactoryError(NestedChildComp));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -209,8 +209,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toEqual('Some other <div>HTML</div>');
|
||||
});
|
||||
|
||||
modifiedInIvy('Binding to the class property directly works differently') &&
|
||||
it('should consume binding to className using class alias', () => {
|
||||
modifiedInIvy('Binding to the class property directly works differently')
|
||||
.it('should consume binding to className using class alias', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<div class="initial" [class]="ctxProp"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -238,8 +238,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().getProperty(nativeEl, 'htmlFor')).toBe('foo');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') &&
|
||||
it('should consume directive watch expression change.', () => {
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work')
|
||||
.it('should consume directive watch expression change.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
|
||||
const template = '<span>' +
|
||||
'<div my-dir [elprop]="ctxProp"></div>' +
|
||||
|
@ -263,8 +263,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('pipes', () => {
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') &&
|
||||
it('should support pipes in bindings', () => {
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work')
|
||||
.it('should support pipes in bindings', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
|
||||
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -290,8 +290,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
// GH issue 328 - https://github.com/angular/angular/issues/328
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work') &&
|
||||
it('should support different directive types on a single node', () => {
|
||||
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work')
|
||||
.it('should support different directive types on a single node', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildComp, MyDir]});
|
||||
const template = '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -350,8 +350,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(tc.injector.get(EventDir)).not.toBeNull();
|
||||
});
|
||||
|
||||
fixmeIvy('FW-680: Throw meaningful error for uninitialized @Output') &&
|
||||
it('should display correct error message for uninitialized @Output', () => {
|
||||
fixmeIvy('FW-680: Throw meaningful error for uninitialized @Output')
|
||||
.it('should display correct error message for uninitialized @Output', () => {
|
||||
@Component({selector: 'my-uninitialized-output', template: '<p>It works!</p>'})
|
||||
class UninitializedOutputComp {
|
||||
@Output() customEvent !: EventEmitter<any>;
|
||||
|
@ -374,8 +374,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
const fixture = TestBed.createComponent(MyComp);
|
||||
});
|
||||
|
||||
modifiedInIvy('Comment node order changed') &&
|
||||
it('should support template directives via `<ng-template>` elements.', () => {
|
||||
modifiedInIvy('Comment node order changed')
|
||||
.it('should support template directives via `<ng-template>` elements.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeViewport]});
|
||||
const template =
|
||||
'<ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template>';
|
||||
|
@ -403,8 +403,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-665: Discovery util fails with "Unable to find the given context data for the given target"') &&
|
||||
it('should not detach views in ViewContainers when the parent view is destroyed.', () => {
|
||||
'FW-665: Discovery util fails with "Unable to find the given context data for the given target"')
|
||||
.it('should not detach views in ViewContainers when the parent view is destroyed.', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeViewport]});
|
||||
const template =
|
||||
'<div *ngIf="ctxBoolProp"><ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template></div>';
|
||||
|
@ -478,8 +478,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toBeAnInstanceOf(ExportDir);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-708: Directives with multiple exports are not supported') &&
|
||||
it('should assign a directive to a ref when it has multiple exportAs names', () => {
|
||||
fixmeIvy('FW-708: Directives with multiple exports are not supported')
|
||||
.it('should assign a directive to a ref when it has multiple exportAs names', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveWithMultipleExportAsNames]});
|
||||
|
||||
|
@ -543,8 +543,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(value.tagName.toLowerCase()).toEqual('div');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-709: Context discovery does not support templates (comment nodes)') &&
|
||||
it('should assign the TemplateRef to a user-defined variable', () => {
|
||||
fixmeIvy('FW-709: Context discovery does not support templates (comment nodes)')
|
||||
.it('should assign the TemplateRef to a user-defined variable', () => {
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
.overrideComponent(
|
||||
|
@ -567,8 +567,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('variables', () => {
|
||||
modifiedInIvy('Comment node order changed') &&
|
||||
it('should allow to use variables in a for loop', () => {
|
||||
modifiedInIvy('Comment node order changed')
|
||||
.it('should allow to use variables in a for loop', () => {
|
||||
const template =
|
||||
'<ng-template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</ng-template>';
|
||||
|
||||
|
@ -586,8 +586,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
describe('OnPush components', () => {
|
||||
|
||||
fixmeIvy(
|
||||
'FW-764: fixture.detectChanges() is not respecting OnPush flag on components in the root template') &&
|
||||
it('should use ChangeDetectorRef to manually request a check', () => {
|
||||
'FW-764: fixture.detectChanges() is not respecting OnPush flag on components in the root template')
|
||||
.it('should use ChangeDetectorRef to manually request a check', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithRef]]]});
|
||||
const template = '<push-cmp-with-ref #cmp></push-cmp-with-ref>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -608,8 +608,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-764: fixture.detectChanges() is not respecting OnPush flag on components in the root template') &&
|
||||
it('should be checked when its bindings got updated', () => {
|
||||
'FW-764: fixture.detectChanges() is not respecting OnPush flag on components in the root template')
|
||||
.it('should be checked when its bindings got updated', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, PushCmp, EventCmp], imports: [CommonModule]});
|
||||
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>';
|
||||
|
@ -628,28 +628,27 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
fixmeIvy('unknown') &&
|
||||
it('should allow to destroy a component from within a host event handler',
|
||||
fakeAsync(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, [[PushCmpWithHostEvent]]]});
|
||||
const template = '<push-cmp-with-host-event></push-cmp-with-host-event>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixmeIvy('unknown').it(
|
||||
'should allow to destroy a component from within a host event handler',
|
||||
fakeAsync(() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithHostEvent]]]});
|
||||
const template = '<push-cmp-with-host-event></push-cmp-with-host-event>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const cmpEl = fixture.debugElement.children[0];
|
||||
const cmp: PushCmpWithHostEvent = cmpEl.injector.get(PushCmpWithHostEvent);
|
||||
cmp.ctxCallback = (_: any) => fixture.destroy();
|
||||
const cmpEl = fixture.debugElement.children[0];
|
||||
const cmp: PushCmpWithHostEvent = cmpEl.injector.get(PushCmpWithHostEvent);
|
||||
cmp.ctxCallback = (_: any) => fixture.destroy();
|
||||
|
||||
expect(() => cmpEl.triggerEventHandler('click', <Event>{})).not.toThrow();
|
||||
}));
|
||||
expect(() => cmpEl.triggerEventHandler('click', <Event>{})).not.toThrow();
|
||||
}));
|
||||
}
|
||||
|
||||
fixmeIvy('FW-758: OnPush events not marking view dirty when using renderer2') &&
|
||||
it('should be checked when an event is fired', () => {
|
||||
fixmeIvy('FW-758: OnPush events not marking view dirty when using renderer2')
|
||||
.it('should be checked when an event is fired', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, PushCmp, EventCmp], imports: [CommonModule]});
|
||||
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>';
|
||||
|
@ -774,71 +773,71 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-763: LView tree not properly constructed / destroyed for dynamically inserted components') &&
|
||||
it('should support events via EventEmitter on regular elements', async(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]});
|
||||
const template = '<div emitter listener></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
'FW-763: LView tree not properly constructed / destroyed for dynamically inserted components')
|
||||
.it('should support events via EventEmitter on regular elements', async(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]});
|
||||
const template = '<div emitter listener></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const tc = fixture.debugElement.children[0];
|
||||
const emitter = tc.injector.get(DirectiveEmittingEvent);
|
||||
const listener = tc.injector.get(DirectiveListeningEvent);
|
||||
const tc = fixture.debugElement.children[0];
|
||||
const emitter = tc.injector.get(DirectiveEmittingEvent);
|
||||
const listener = tc.injector.get(DirectiveListeningEvent);
|
||||
|
||||
expect(listener.msg).toEqual('');
|
||||
let eventCount = 0;
|
||||
expect(listener.msg).toEqual('');
|
||||
let eventCount = 0;
|
||||
|
||||
emitter.event.subscribe({
|
||||
next: () => {
|
||||
eventCount++;
|
||||
if (eventCount === 1) {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
fixture.destroy();
|
||||
emitter.fireEvent('fired again !');
|
||||
} else {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
}
|
||||
}
|
||||
});
|
||||
emitter.event.subscribe({
|
||||
next: () => {
|
||||
eventCount++;
|
||||
if (eventCount === 1) {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
fixture.destroy();
|
||||
emitter.fireEvent('fired again !');
|
||||
} else {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
emitter.fireEvent('fired !');
|
||||
}));
|
||||
emitter.fireEvent('fired !');
|
||||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-665: Discovery util fails with Unable to find the given context data for the given target') &&
|
||||
it('should support events via EventEmitter on template elements', async(() => {
|
||||
const fixture =
|
||||
TestBed
|
||||
.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]
|
||||
})
|
||||
.overrideComponent(MyComp, {
|
||||
set: {
|
||||
template:
|
||||
'<ng-template emitter listener (event)="ctxProp=$event"></ng-template>'
|
||||
}
|
||||
})
|
||||
.createComponent(MyComp);
|
||||
'FW-665: Discovery util fails with Unable to find the given context data for the given target')
|
||||
.it('should support events via EventEmitter on template elements', async(() => {
|
||||
const fixture =
|
||||
TestBed
|
||||
.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent]
|
||||
})
|
||||
.overrideComponent(MyComp, {
|
||||
set: {
|
||||
template:
|
||||
'<ng-template emitter listener (event)="ctxProp=$event"></ng-template>'
|
||||
}
|
||||
})
|
||||
.createComponent(MyComp);
|
||||
|
||||
const tc = fixture.debugElement.childNodes[0];
|
||||
const tc = fixture.debugElement.childNodes[0];
|
||||
|
||||
const emitter = tc.injector.get(DirectiveEmittingEvent);
|
||||
const myComp = fixture.debugElement.injector.get(MyComp);
|
||||
const listener = tc.injector.get(DirectiveListeningEvent);
|
||||
const emitter = tc.injector.get(DirectiveEmittingEvent);
|
||||
const myComp = fixture.debugElement.injector.get(MyComp);
|
||||
const listener = tc.injector.get(DirectiveListeningEvent);
|
||||
|
||||
myComp.ctxProp = '';
|
||||
expect(listener.msg).toEqual('');
|
||||
myComp.ctxProp = '';
|
||||
expect(listener.msg).toEqual('');
|
||||
|
||||
emitter.event.subscribe({
|
||||
next: () => {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
expect(myComp.ctxProp).toEqual('fired !');
|
||||
}
|
||||
});
|
||||
emitter.event.subscribe({
|
||||
next: () => {
|
||||
expect(listener.msg).toEqual('fired !');
|
||||
expect(myComp.ctxProp).toEqual('fired !');
|
||||
}
|
||||
});
|
||||
|
||||
emitter.fireEvent('fired !');
|
||||
}));
|
||||
emitter.fireEvent('fired !');
|
||||
}));
|
||||
|
||||
it('should support [()] syntax', async(() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithTwoWayBinding]});
|
||||
|
@ -860,8 +859,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported') &&
|
||||
it('should support render events', () => {
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported')
|
||||
.it('should support render events', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveListeningDomEvent]});
|
||||
const template = '<div listener></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -883,8 +882,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported') &&
|
||||
it('should support render global events', () => {
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported')
|
||||
.it('should support render global events', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveListeningDomEvent]});
|
||||
const template = '<div listener></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -947,8 +946,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().getProperty(tc.nativeElement, 'id')).toEqual('newId');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') &&
|
||||
it('should not use template variables for expressions in hostProperties', () => {
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView')
|
||||
.it('should not use template variables for expressions in hostProperties', () => {
|
||||
@Directive(
|
||||
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
|
||||
class DirectiveWithHostProps {
|
||||
|
@ -968,8 +967,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(tc.properties['title']).toBe(undefined);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-725: Pipes in host bindings fail with a cryptic error') &&
|
||||
it('should not allow pipes in hostProperties', () => {
|
||||
fixmeIvy('FW-725: Pipes in host bindings fail with a cryptic error')
|
||||
.it('should not allow pipes in hostProperties', () => {
|
||||
@Directive({selector: '[host-properties]', host: {'[id]': 'id | uppercase'}})
|
||||
class DirectiveWithHostProps {
|
||||
}
|
||||
|
@ -1004,8 +1003,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(dir.receivedArgs).toEqual(['one', undefined]);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-742: Pipes in host listeners should throw a descriptive error') &&
|
||||
it('should not allow pipes in hostListeners', () => {
|
||||
fixmeIvy('FW-742: Pipes in host listeners should throw a descriptive error')
|
||||
.it('should not allow pipes in hostListeners', () => {
|
||||
@Directive({selector: '[host-listener]', host: {'(click)': 'doIt() | somePipe'}})
|
||||
class DirectiveWithHostListener {
|
||||
}
|
||||
|
@ -1042,8 +1041,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
}
|
||||
|
||||
fixmeIvy(
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported') &&
|
||||
it('should support render global events from multiple directives', () => {
|
||||
'FW-743: Registering events on global objects (document, window, body) is not supported')
|
||||
.it('should support render global events from multiple directives', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveListeningDomEvent, DirectiveListeningDomEventOther]
|
||||
});
|
||||
|
@ -1123,8 +1122,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toHaveText('dynamic greet');
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation') &&
|
||||
it('should create a component that has been freshly compiled', () => {
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation')
|
||||
.it('should create a component that has been freshly compiled', () => {
|
||||
@Component({template: ''})
|
||||
class RootComp {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
|
@ -1163,8 +1162,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(compRef.instance.someToken).toBe('someRootValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation') &&
|
||||
it('should create a component with the passed NgModuleRef', () => {
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation')
|
||||
.it('should create a component with the passed NgModuleRef', () => {
|
||||
@Component({template: ''})
|
||||
class RootComp {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
|
@ -1204,48 +1203,48 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(compRef.instance.someToken).toBe('someValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation') &&
|
||||
it('should create a component with the NgModuleRef of the ComponentFactoryResolver',
|
||||
() => {
|
||||
@Component({template: ''})
|
||||
class RootComp {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
}
|
||||
fixmeIvy('FW-707: TestBed: No LView in getParentInjectorLocation')
|
||||
.it('should create a component with the NgModuleRef of the ComponentFactoryResolver',
|
||||
() => {
|
||||
@Component({template: ''})
|
||||
class RootComp {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [RootComp],
|
||||
providers: [{provide: 'someToken', useValue: 'someRootValue'}],
|
||||
})
|
||||
class RootModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [RootComp],
|
||||
providers: [{provide: 'someToken', useValue: 'someRootValue'}],
|
||||
})
|
||||
class RootModule {
|
||||
}
|
||||
|
||||
@Component({template: ''})
|
||||
class MyComp {
|
||||
constructor(@Inject('someToken') public someToken: string) {}
|
||||
}
|
||||
@Component({template: ''})
|
||||
class MyComp {
|
||||
constructor(@Inject('someToken') public someToken: string) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
providers: [{provide: 'someToken', useValue: 'someValue'}],
|
||||
})
|
||||
class MyModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [MyComp],
|
||||
entryComponents: [MyComp],
|
||||
providers: [{provide: 'someToken', useValue: 'someValue'}],
|
||||
})
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
const compFixture = TestBed.configureTestingModule({imports: [RootModule]})
|
||||
.createComponent(RootComp);
|
||||
const compiler = <Compiler>TestBed.get(Compiler);
|
||||
const myModule =
|
||||
compiler.compileModuleSync(MyModule).create(TestBed.get(NgModuleRef));
|
||||
const myCompFactory =
|
||||
myModule.componentFactoryResolver.resolveComponentFactory(MyComp);
|
||||
const compFixture = TestBed.configureTestingModule({imports: [RootModule]})
|
||||
.createComponent(RootComp);
|
||||
const compiler = <Compiler>TestBed.get(Compiler);
|
||||
const myModule =
|
||||
compiler.compileModuleSync(MyModule).create(TestBed.get(NgModuleRef));
|
||||
const myCompFactory =
|
||||
myModule.componentFactoryResolver.resolveComponentFactory(MyComp);
|
||||
|
||||
// Note: MyComp was declared as entryComponent in MyModule,
|
||||
// and we don't pass an explicit ModuleRef to the createComponent call.
|
||||
// -> expect the providers of MyModule!
|
||||
const compRef = compFixture.componentInstance.vc.createComponent(myCompFactory);
|
||||
expect(compRef.instance.someToken).toBe('someValue');
|
||||
});
|
||||
// Note: MyComp was declared as entryComponent in MyModule,
|
||||
// and we don't pass an explicit ModuleRef to the createComponent call.
|
||||
// -> expect the providers of MyModule!
|
||||
const compRef = compFixture.componentInstance.vc.createComponent(myCompFactory);
|
||||
expect(compRef.instance.someToken).toBe('someValue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.insert', () => {
|
||||
|
@ -1336,7 +1335,7 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') && it('should support viewProviders', () => {
|
||||
fixmeIvy('unknown').it('should support viewProviders', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveProvidingInjectableInView, DirectiveConsumingInjectable],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
|
@ -1448,8 +1447,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().querySelectorAll(fixture.nativeElement, 'script').length).toEqual(0);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-662: Components without selector are not supported') &&
|
||||
it('should throw when using directives without selector', () => {
|
||||
fixmeIvy('FW-662: Components without selector are not supported')
|
||||
.it('should throw when using directives without selector', () => {
|
||||
@Directive({})
|
||||
class SomeDirective {
|
||||
}
|
||||
|
@ -1496,8 +1495,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
fixmeIvy('FW-682: TestBed: tests assert that compilation produces specific error') &&
|
||||
it('should report a meaningful error when a directive is missing annotation', () => {
|
||||
fixmeIvy('FW-682: TestBed: tests assert that compilation produces specific error')
|
||||
.it('should report a meaningful error when a directive is missing annotation', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, SomeDirectiveMissingAnnotation]});
|
||||
|
||||
|
@ -1506,20 +1505,21 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
`Unexpected value '${stringify(SomeDirectiveMissingAnnotation)}' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation.`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: TestBed: tests assert that compilation produces specific error') &&
|
||||
it('should report a meaningful error when a component is missing view annotation', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ComponentWithoutView]});
|
||||
try {
|
||||
TestBed.createComponent(ComponentWithoutView);
|
||||
expect(true).toBe(false);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
`No template specified for component ${stringify(ComponentWithoutView)}`);
|
||||
}
|
||||
});
|
||||
fixmeIvy('FW-682: TestBed: tests assert that compilation produces specific error')
|
||||
.it('should report a meaningful error when a component is missing view annotation',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ComponentWithoutView]});
|
||||
try {
|
||||
TestBed.createComponent(ComponentWithoutView);
|
||||
expect(true).toBe(false);
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
`No template specified for component ${stringify(ComponentWithoutView)}`);
|
||||
}
|
||||
});
|
||||
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented') &&
|
||||
it('should provide an error context when an error happens in DI', () => {
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented')
|
||||
.it('should provide an error context when an error happens in DI', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveThrowingAnError],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
|
@ -1537,8 +1537,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
}
|
||||
});
|
||||
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented') &&
|
||||
it('should provide an error context when an error happens in change detection', () => {
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented')
|
||||
.it('should provide an error context when an error happens in change detection', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, DirectiveThrowingAnError]});
|
||||
const template = `<input [value]="one.two.three" #local>`;
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -1556,50 +1556,50 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
}
|
||||
});
|
||||
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented') &&
|
||||
it('should provide an error context when an error happens in change detection (text node)',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = `<div>{{one.two.three}}</div>`;
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
try {
|
||||
fixture.detectChanges();
|
||||
throw 'Should throw';
|
||||
} catch (e) {
|
||||
const c = getDebugContext(e);
|
||||
expect(c.renderNode).toBeTruthy();
|
||||
}
|
||||
});
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented')
|
||||
.it('should provide an error context when an error happens in change detection (text node)',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = `<div>{{one.two.three}}</div>`;
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
try {
|
||||
fixture.detectChanges();
|
||||
throw 'Should throw';
|
||||
} catch (e) {
|
||||
const c = getDebugContext(e);
|
||||
expect(c.renderNode).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) { // this is required to use fakeAsync
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented') &&
|
||||
it('should provide an error context when an error happens in an event handler',
|
||||
fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
});
|
||||
const template = `<span emitter listener (event)="throwError()" #local></span>`;
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
tick();
|
||||
fixmeIvy('FW-722: getDebugContext needs to be replaced / re-implemented')
|
||||
.it('should provide an error context when an error happens in an event handler',
|
||||
fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [MyComp, DirectiveEmittingEvent, DirectiveListeningEvent],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
});
|
||||
const template = `<span emitter listener (event)="throwError()" #local></span>`;
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
tick();
|
||||
|
||||
const tc = fixture.debugElement.children[0];
|
||||
const tc = fixture.debugElement.children[0];
|
||||
|
||||
const errorHandler = tc.injector.get(ErrorHandler);
|
||||
let err: any;
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => err = e);
|
||||
tc.injector.get(DirectiveEmittingEvent).fireEvent('boom');
|
||||
const errorHandler = tc.injector.get(ErrorHandler);
|
||||
let err: any;
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => err = e);
|
||||
tc.injector.get(DirectiveEmittingEvent).fireEvent('boom');
|
||||
|
||||
expect(err).toBeTruthy();
|
||||
const c = getDebugContext(err);
|
||||
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN');
|
||||
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
|
||||
expect((<Injector>c.injector).get).toBeTruthy();
|
||||
expect(c.context).toBe(fixture.componentInstance);
|
||||
expect(c.references['local']).toBeDefined();
|
||||
}));
|
||||
expect(err).toBeTruthy();
|
||||
const c = getDebugContext(err);
|
||||
expect(getDOM().nodeName(c.renderNode).toUpperCase()).toEqual('SPAN');
|
||||
expect(getDOM().nodeName(c.componentRenderElement).toUpperCase()).toEqual('DIV');
|
||||
expect((<Injector>c.injector).get).toBeTruthy();
|
||||
expect(c.context).toBe(fixture.componentInstance);
|
||||
expect(c.references['local']).toBeDefined();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1636,8 +1636,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('Property bindings', () => {
|
||||
fixmeIvy('FW-721: Bindings to unknown properties are not reported as errors') &&
|
||||
it('should throw on bindings to unknown properties', () => {
|
||||
fixmeIvy('FW-721: Bindings to unknown properties are not reported as errors')
|
||||
.it('should throw on bindings to unknown properties', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp]});
|
||||
const template = '<div unknown="{{ctxProp}}"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -1671,8 +1671,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(el.title).toBeFalsy();
|
||||
});
|
||||
|
||||
fixmeIvy('FW-711: elementProperty instruction should not be used in host bindings') &&
|
||||
it('should work when a directive uses hostProperty to update the DOM element', () => {
|
||||
fixmeIvy('FW-711: elementProperty instruction should not be used in host bindings')
|
||||
.it('should work when a directive uses hostProperty to update the DOM element', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveWithTitleAndHostProperty]});
|
||||
const template = '<span [title]="ctxProp"></span>';
|
||||
|
@ -1688,8 +1688,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('logging property updates', () => {
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported') &&
|
||||
it('should reflect property values as attributes', () => {
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported')
|
||||
.it('should reflect property values as attributes', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
|
||||
const template = '<div>' +
|
||||
'<div my-dir [elprop]="ctxProp"></div>' +
|
||||
|
@ -1712,8 +1712,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test_="hello"');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported') &&
|
||||
it('should reflect property values on template comments', () => {
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported')
|
||||
.it('should reflect property values on template comments', () => {
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
.overrideComponent(
|
||||
|
@ -1729,8 +1729,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
// also affected by FW-587: Inputs with aliases in component decorators don't work
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported') &&
|
||||
it('should indicate when toString() throws', () => {
|
||||
fixmeIvy('FW-664: ng-reflect-* is not supported')
|
||||
.it('should indicate when toString() throws', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
|
||||
const template = '<div my-dir [elprop]="toStringThrow"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
|
|
@ -138,8 +138,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
|
||||
afterEach(() => { resetTestEnvironmentWithSummaries(); });
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should use directive metadata from summaries', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should use directive metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
|
@ -153,8 +153,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomeDirective);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should use pipe metadata from summaries', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should use pipe metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
@Component({template: '{{1 | somePipe}}'})
|
||||
|
@ -166,8 +166,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomePipe);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should use Service metadata from summaries', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should use Service metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
|
@ -177,8 +177,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomeService);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should use NgModule metadata from summaries', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should use NgModule metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed
|
||||
|
@ -192,8 +192,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomeService);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should allow to create private components from imported NgModule summaries', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should allow to create private components from imported NgModule summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||
|
@ -201,8 +201,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomePrivateComponent);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should throw when trying to mock a type with a summary', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should throw when trying to mock a type with a summary', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.resetTestingModule();
|
||||
|
@ -221,35 +221,35 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
.toThrowError('SomeModule was AOT compiled, so its metadata cannot be changed.');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should return stack trace and component data on resetTestingModule when error is thrown',
|
||||
() => {
|
||||
resetTestEnvironmentWithSummaries();
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should return stack trace and component data on resetTestingModule when error is thrown',
|
||||
() => {
|
||||
resetTestEnvironmentWithSummaries();
|
||||
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [TestCompErrorOnDestroy]})
|
||||
.createComponent<TestCompErrorOnDestroy>(TestCompErrorOnDestroy);
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [TestCompErrorOnDestroy]})
|
||||
.createComponent<TestCompErrorOnDestroy>(TestCompErrorOnDestroy);
|
||||
|
||||
const expectedError = 'Error from ngOnDestroy';
|
||||
const expectedError = 'Error from ngOnDestroy';
|
||||
|
||||
const component: TestCompErrorOnDestroy = fixture.componentInstance;
|
||||
const component: TestCompErrorOnDestroy = fixture.componentInstance;
|
||||
|
||||
spyOn(console, 'error');
|
||||
spyOn(component, 'ngOnDestroy').and.throwError(expectedError);
|
||||
spyOn(console, 'error');
|
||||
spyOn(component, 'ngOnDestroy').and.throwError(expectedError);
|
||||
|
||||
const expectedObject = {
|
||||
stacktrace: new Error(expectedError),
|
||||
component,
|
||||
};
|
||||
const expectedObject = {
|
||||
stacktrace: new Error(expectedError),
|
||||
component,
|
||||
};
|
||||
|
||||
TestBed.resetTestingModule();
|
||||
TestBed.resetTestingModule();
|
||||
|
||||
expect(console.error)
|
||||
.toHaveBeenCalledWith('Error during cleanup of component', expectedObject);
|
||||
});
|
||||
expect(console.error)
|
||||
.toHaveBeenCalledWith('Error during cleanup of component', expectedObject);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should allow to add summaries via configureTestingModule', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should allow to add summaries via configureTestingModule', () => {
|
||||
resetTestEnvironmentWithSummaries();
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
|
@ -266,8 +266,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expectInstanceCreated(SomeDirective);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should allow to override a provider', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should allow to override a provider', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
const overwrittenValue = {};
|
||||
|
@ -280,8 +280,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(fixture.componentInstance.dep).toBe(overwrittenValue);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc') &&
|
||||
it('should allow to override a template', () => {
|
||||
fixmeIvy('FW-514: ngSummary shims not generated by ngtsc')
|
||||
.it('should allow to override a template', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.overrideTemplateUsingTestingModule(SomePublicComponent, 'overwritten');
|
||||
|
|
|
@ -52,8 +52,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(el).toHaveText('foo');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>') &&
|
||||
it('should be rendered as comment with children as siblings', () => {
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>')
|
||||
.it('should be rendered as comment with children as siblings', () => {
|
||||
const template = '<ng-container><p></p></ng-container>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
@ -67,8 +67,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().tagName(children[1]).toUpperCase()).toEqual('P');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>') &&
|
||||
it('should support nesting', () => {
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>')
|
||||
.it('should support nesting', () => {
|
||||
const template =
|
||||
'<ng-container>1</ng-container><ng-container><ng-container>2</ng-container></ng-container>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
@ -86,8 +86,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(children[4]).toHaveText('2');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>') &&
|
||||
it('should group inner nodes', () => {
|
||||
fixmeIvy('FW-678: ivy generates different DOM structure for <ng-container>')
|
||||
.it('should group inner nodes', () => {
|
||||
const template = '<ng-container *ngIf="ctxBoolProp"><p></p><b></b></ng-container>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
@ -136,8 +136,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(dir.text).toEqual('container');
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should contain all direct child directives in a <ng-container> (content dom)', () => {
|
||||
fixmeIvy('unknown').it(
|
||||
'should contain all direct child directives in a <ng-container> (content dom)', () => {
|
||||
const template =
|
||||
'<needs-content-children #q><ng-container><div text="foo"></div></ng-container></needs-content-children>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
|
|
@ -143,8 +143,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
}
|
||||
|
||||
describe('errors', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error when exporting a directive that was neither declared nor imported', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error when exporting a directive that was neither declared nor imported', () => {
|
||||
@NgModule({exports: [SomeDirective]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
@ -154,8 +154,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
`Can't export directive ${stringify(SomeDirective)} from ${stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error when exporting a pipe that was neither declared nor imported', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error when exporting a pipe that was neither declared nor imported', () => {
|
||||
@NgModule({exports: [SomePipe]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
@ -165,8 +165,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
`Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error if a directive is declared in more than 1 module', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error if a directive is declared in more than 1 module', () => {
|
||||
@NgModule({declarations: [SomeDirective]})
|
||||
class Module1 {
|
||||
}
|
||||
|
@ -184,26 +184,26 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
`You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error if a directive is declared in more than 1 module also if the module declaring it is imported',
|
||||
() => {
|
||||
@NgModule({declarations: [SomeDirective], exports: [SomeDirective]})
|
||||
class Module1 {
|
||||
}
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error if a directive is declared in more than 1 module also if the module declaring it is imported',
|
||||
() => {
|
||||
@NgModule({declarations: [SomeDirective], exports: [SomeDirective]})
|
||||
class Module1 {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [SomeDirective], imports: [Module1]})
|
||||
class Module2 {
|
||||
}
|
||||
@NgModule({declarations: [SomeDirective], imports: [Module1]})
|
||||
class Module2 {
|
||||
}
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error if a pipe is declared in more than 1 module', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error if a pipe is declared in more than 1 module', () => {
|
||||
@NgModule({declarations: [SomePipe]})
|
||||
class Module1 {
|
||||
}
|
||||
|
@ -221,29 +221,29 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
`You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported',
|
||||
() => {
|
||||
@NgModule({declarations: [SomePipe], exports: [SomePipe]})
|
||||
class Module1 {
|
||||
}
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported',
|
||||
() => {
|
||||
@NgModule({declarations: [SomePipe], exports: [SomePipe]})
|
||||
class Module1 {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [SomePipe], imports: [Module1]})
|
||||
class Module2 {
|
||||
}
|
||||
@NgModule({declarations: [SomePipe], imports: [Module1]})
|
||||
class Module2 {
|
||||
}
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('schemas', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should error on unknown bound properties on custom elements by default', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should error on unknown bound properties on custom elements by default', () => {
|
||||
@Component({template: '<some-element [someUnknownProp]="true"></some-element>'})
|
||||
class ComponentUsingInvalidProperty {
|
||||
}
|
||||
|
@ -281,16 +281,16 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
|
||||
afterEach(() => clearModulesForTest());
|
||||
|
||||
fixmeIvy('FW-740: missing global registry of NgModules by id') &&
|
||||
it('should register loaded modules', () => {
|
||||
fixmeIvy('FW-740: missing global registry of NgModules by id')
|
||||
.it('should register loaded modules', () => {
|
||||
createModule(SomeModule);
|
||||
const factory = getModuleFactory(token);
|
||||
expect(factory).toBeTruthy();
|
||||
expect(factory.moduleType).toBe(SomeModule);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when registering a duplicate module', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when registering a duplicate module', () => {
|
||||
createModule(SomeModule);
|
||||
expect(() => createModule(SomeOtherModule)).toThrowError(/Duplicate module registered/);
|
||||
});
|
||||
|
@ -311,37 +311,37 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw if we cannot find a module associated with a module-level entryComponent',
|
||||
() => {
|
||||
@Component({template: ''})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw if we cannot find a module associated with a module-level entryComponent',
|
||||
() => {
|
||||
@Component({template: ''})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [], entryComponents: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({declarations: [], entryComponents: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
'Component SomeCompWithEntryComponents is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
'Component SomeCompWithEntryComponents is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw if we cannot find a module associated with a component-level entryComponent',
|
||||
() => {
|
||||
@Component({template: '', entryComponents: [SomeComp]})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw if we cannot find a module associated with a component-level entryComponent',
|
||||
() => {
|
||||
@Component({template: '', entryComponents: [SomeComp]})
|
||||
class SomeCompWithEntryComponents {
|
||||
}
|
||||
|
||||
@NgModule({declarations: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({declarations: [SomeCompWithEntryComponents]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
'Component SomeComp is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
'Component SomeComp is not part of any NgModule or the module has not been imported into your module.');
|
||||
});
|
||||
|
||||
it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => {
|
||||
@NgModule({
|
||||
|
@ -426,8 +426,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('directives and pipes', () => {
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') &&
|
||||
describe('declarations', () => {
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView')
|
||||
.describe('declarations', () => {
|
||||
it('should be supported in root modules', () => {
|
||||
@NgModule({
|
||||
declarations: [CompUsingModuleDirectiveAndPipe, SomeDirective, SomePipe],
|
||||
|
@ -489,8 +489,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
|
||||
describe('import/export', () => {
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should support exported directives and pipes', () => {
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should support exported directives and pipes', () => {
|
||||
@NgModule(
|
||||
{declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeImportedModule {
|
||||
|
@ -511,31 +511,31 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should support exported directives and pipes if the module is wrapped into an `ModuleWithProviders`',
|
||||
() => {
|
||||
@NgModule(
|
||||
{declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeImportedModule {
|
||||
}
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should support exported directives and pipes if the module is wrapped into an `ModuleWithProviders`',
|
||||
() => {
|
||||
@NgModule(
|
||||
{declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeImportedModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [CompUsingModuleDirectiveAndPipe],
|
||||
imports: [{ngModule: SomeImportedModule}],
|
||||
entryComponents: [CompUsingModuleDirectiveAndPipe]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [CompUsingModuleDirectiveAndPipe],
|
||||
imports: [{ngModule: SomeImportedModule}],
|
||||
entryComponents: [CompUsingModuleDirectiveAndPipe]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
|
||||
const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
const compFixture = createComp(CompUsingModuleDirectiveAndPipe, SomeModule);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title'])
|
||||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should support reexported modules', () => {
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should support reexported modules', () => {
|
||||
@NgModule(
|
||||
{declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeReexportedModule {
|
||||
|
@ -559,8 +559,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should support exporting individual directives of an imported module', () => {
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should support exporting individual directives of an imported module', () => {
|
||||
@NgModule(
|
||||
{declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeReexportedModule {
|
||||
|
@ -584,8 +584,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toBe('transformed someValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should not use non exported pipes of an imported module', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should not use non exported pipes of an imported module', () => {
|
||||
@NgModule({
|
||||
declarations: [SomePipe],
|
||||
})
|
||||
|
@ -604,8 +604,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toThrowError(/The pipe 'somePipe' could not be found/);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should not use non exported directives of an imported module', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should not use non exported directives of an imported module', () => {
|
||||
@NgModule({
|
||||
declarations: [SomeDirective],
|
||||
})
|
||||
|
@ -667,14 +667,14 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(car.engine).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when no type and not @Inject (class case)', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when no type and not @Inject (class case)', () => {
|
||||
expect(() => createInjector([NoAnnotations]))
|
||||
.toThrowError('Can\'t resolve all parameters for NoAnnotations: (?).');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when no type and not @Inject (factory case)', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when no type and not @Inject (factory case)', () => {
|
||||
expect(() => createInjector([{provide: 'someToken', useFactory: factoryFn}]))
|
||||
.toThrowError('Can\'t resolve all parameters for factoryFn: (?).');
|
||||
});
|
||||
|
@ -745,8 +745,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(cars[0]).toBe(injector.get(SportsCar));
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when the aliased provider does not exist', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when the aliased provider does not exist', () => {
|
||||
const injector = createInjector([{provide: 'car', useExisting: SportsCar}]);
|
||||
const e = `NullInjectorError: No provider for ${stringify(SportsCar)}!`;
|
||||
expect(() => injector.get('car')).toThrowError(e);
|
||||
|
@ -796,14 +796,13 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(injector.get('token')).toEqual('value');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when given invalid providers', () => {
|
||||
expect(() => createInjector(<any>['blah']))
|
||||
.toThrowError(
|
||||
`Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?blah?]`);
|
||||
});
|
||||
fixmeIvy('FW-682: Compiler error handling').it('should throw when given invalid providers', () => {
|
||||
expect(() => createInjector(<any>['blah']))
|
||||
.toThrowError(
|
||||
`Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?blah?]`);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') && it('should throw when given blank providers', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling').it('should throw when given blank providers', () => {
|
||||
expect(() => createInjector(<any>[null, {provide: 'token', useValue: 'value'}]))
|
||||
.toThrowError(
|
||||
`Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
||||
|
@ -949,8 +948,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
.toThrowError('NullInjectorError: No provider for NonExisting!');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when trying to instantiate a cyclic dependency', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when trying to instantiate a cyclic dependency', () => {
|
||||
expect(() => createInjector([Car, {provide: Engine, useClass: CyclicEngine}]))
|
||||
.toThrowError(/Cannot instantiate cyclic dependency! Car/g);
|
||||
});
|
||||
|
@ -1053,8 +1052,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(created).toBe(false);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called') &&
|
||||
it('should support ngOnDestroy on any provider', () => {
|
||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
|
||||
.it('should support ngOnDestroy on any provider', () => {
|
||||
let destroyed = false;
|
||||
|
||||
class SomeInjectable {
|
||||
|
@ -1073,8 +1072,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(destroyed).toBe(true);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called') &&
|
||||
it('should support ngOnDestroy for lazy providers', () => {
|
||||
fixmeIvy('FW-739: TestBed: destroy on NgModuleRef is not being called')
|
||||
.it('should support ngOnDestroy for lazy providers', () => {
|
||||
let created = false;
|
||||
let destroyed = false;
|
||||
|
||||
|
@ -1318,8 +1317,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(injector.get('token1')).toBe('imported2');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682: Compiler error handling') &&
|
||||
it('should throw when given invalid providers in an imported ModuleWithProviders', () => {
|
||||
fixmeIvy('FW-682: Compiler error handling')
|
||||
.it('should throw when given invalid providers in an imported ModuleWithProviders', () => {
|
||||
@NgModule()
|
||||
class ImportedModule1 {
|
||||
}
|
||||
|
@ -1335,8 +1334,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
describe('tree shakable providers', () => {
|
||||
fixmeIvy('FW-794: NgModuleDefinition not exposed on NgModuleData') &&
|
||||
it('definition should not persist across NgModuleRef instances', () => {
|
||||
fixmeIvy('FW-794: NgModuleDefinition not exposed on NgModuleData')
|
||||
.it('definition should not persist across NgModuleRef instances', () => {
|
||||
@NgModule()
|
||||
class SomeModule {
|
||||
}
|
||||
|
|
|
@ -82,8 +82,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(main.nativeElement).toHaveText('');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-789: select attribute on <ng-content> should not be case-sensitive') &&
|
||||
it('should support multiple content tags', () => {
|
||||
fixmeIvy('FW-789: select attribute on <ng-content> should not be case-sensitive')
|
||||
.it('should support multiple content tags', () => {
|
||||
TestBed.configureTestingModule({declarations: [MultipleContentTagsComponent]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
|
@ -114,8 +114,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(main.nativeElement).toHaveText('(, BAC)');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target') &&
|
||||
it('should redistribute direct child viewcontainers when the light dom changes', () => {
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target')
|
||||
.it('should redistribute direct child viewcontainers when the light dom changes', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MultipleContentTagsComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
|
@ -158,8 +158,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(main.nativeElement).toHaveText('OUTER(SIMPLE(AB))');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target') &&
|
||||
it('should support nesting with content being direct child of a nested component', () => {
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target')
|
||||
.it('should support nesting with content being direct child of a nested component', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations:
|
||||
[InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective]
|
||||
|
@ -186,8 +186,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-745: Compiler isn\'t generating projectionDefs for <ng-content> tags inside <ng-templates>') &&
|
||||
it('should redistribute when the shadow dom changes', () => {
|
||||
'FW-745: Compiler isn\'t generating projectionDefs for <ng-content> tags inside <ng-templates>')
|
||||
.it('should redistribute when the shadow dom changes', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
|
@ -295,8 +295,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(main.nativeElement).toHaveText('SIMPLE()START(A)END');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target') &&
|
||||
it('should support moving ng-content around', () => {
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target')
|
||||
.it('should support moving ng-content around', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations:
|
||||
[ConditionalContentComponent, ProjectDirective, ManualViewportDirective]
|
||||
|
@ -435,8 +435,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
});
|
||||
}
|
||||
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target') &&
|
||||
it('should support nested conditionals that contain ng-contents', () => {
|
||||
fixmeIvy('FW-665: Unable to find the given context data for the given target')
|
||||
.it('should support nested conditionals that contain ng-contents', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalTextComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(
|
||||
|
@ -482,8 +482,8 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-745: Compiler isn\'t generating projectionDefs for <ng-content> tags inside <ng-templates>') &&
|
||||
it('should project filled view containers into a view container', () => {
|
||||
'FW-745: Compiler isn\'t generating projectionDefs for <ng-content> tags inside <ng-templates>')
|
||||
.it('should project filled view containers into a view container', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [ConditionalContentComponent, ManualViewportDirective]});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
|
|
|
@ -54,8 +54,8 @@ describe('Query API', () => {
|
|||
|
||||
describe('querying by directive type', () => {
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should contain all direct child directives in the light dom (constructor)', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should contain all direct child directives in the light dom (constructor)', () => {
|
||||
const template = `
|
||||
<div text="1"></div>
|
||||
<needs-query text="2">
|
||||
|
@ -97,23 +97,23 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should contain the first content child when target is on <ng-template> with embedded view (issue #16568)',
|
||||
() => {
|
||||
const template =
|
||||
'<div directive-needs-content-child><ng-template text="foo" [ngIf]="true"><div text="bar"></div></ng-template></div>' +
|
||||
'<needs-content-child #q><ng-template text="foo" [ngIf]="true"><div text="bar"></div></ng-template></needs-content-child>';
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
view.detectChanges();
|
||||
const q: NeedsContentChild = view.debugElement.children[1].references !['q'];
|
||||
expect(q.child.text).toEqual('foo');
|
||||
const directive: DirectiveNeedsContentChild =
|
||||
view.debugElement.children[0].injector.get(DirectiveNeedsContentChild);
|
||||
expect(directive.child.text).toEqual('foo');
|
||||
});
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should contain the first content child when target is on <ng-template> with embedded view (issue #16568)',
|
||||
() => {
|
||||
const template =
|
||||
'<div directive-needs-content-child><ng-template text="foo" [ngIf]="true"><div text="bar"></div></ng-template></div>' +
|
||||
'<needs-content-child #q><ng-template text="foo" [ngIf]="true"><div text="bar"></div></ng-template></needs-content-child>';
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
view.detectChanges();
|
||||
const q: NeedsContentChild = view.debugElement.children[1].references !['q'];
|
||||
expect(q.child.text).toEqual('foo');
|
||||
const directive: DirectiveNeedsContentChild =
|
||||
view.debugElement.children[0].injector.get(DirectiveNeedsContentChild);
|
||||
expect(directive.child.text).toEqual('foo');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases') &&
|
||||
it('should contain the first view child', () => {
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases')
|
||||
.it('should contain the first view child', () => {
|
||||
const template = '<needs-view-child #q></needs-view-child>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
|
@ -128,8 +128,8 @@ describe('Query API', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases') &&
|
||||
it('should set static view and content children already after the constructor call', () => {
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases')
|
||||
.it('should set static view and content children already after the constructor call', () => {
|
||||
const template =
|
||||
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
|
@ -142,8 +142,8 @@ describe('Query API', () => {
|
|||
expect(q.viewChild.text).toEqual('viewFoo');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases') &&
|
||||
it('should contain the first view child across embedded views', () => {
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases')
|
||||
.it('should contain the first view child across embedded views', () => {
|
||||
TestBed.overrideComponent(
|
||||
MyComp0, {set: {template: '<needs-view-child #q></needs-view-child>'}});
|
||||
TestBed.overrideComponent(NeedsViewChild, {
|
||||
|
@ -172,8 +172,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should contain all directives in the light dom when descendants flag is used', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should contain all directives in the light dom when descendants flag is used', () => {
|
||||
const template = '<div text="1"></div>' +
|
||||
'<needs-query-desc text="2"><div text="3">' +
|
||||
'<div text="4"></div>' +
|
||||
|
@ -185,8 +185,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should contain all directives in the light dom', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should contain all directives in the light dom', () => {
|
||||
const template = '<div text="1"></div>' +
|
||||
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
@ -196,8 +196,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should reflect dynamically inserted directives', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should reflect dynamically inserted directives', () => {
|
||||
const template = '<div text="1"></div>' +
|
||||
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
@ -221,8 +221,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should reflect moved directives', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should reflect moved directives', () => {
|
||||
const template = '<div text="1"></div>' +
|
||||
'<needs-query text="2"><div *ngFor="let i of list" [text]="i"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
@ -234,8 +234,8 @@ describe('Query API', () => {
|
|||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3d|2d|');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-682 - TestBed: tests assert that compilation produces specific error') &&
|
||||
it('should throw with descriptive error when query selectors are not present', () => {
|
||||
fixmeIvy('FW-682 - TestBed: tests assert that compilation produces specific error')
|
||||
.it('should throw with descriptive error when query selectors are not present', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyCompBroken0, HasNullQueryCondition]});
|
||||
const template = '<has-null-query-condition></has-null-query-condition>';
|
||||
TestBed.overrideComponent(MyCompBroken0, {set: {template}});
|
||||
|
@ -268,8 +268,8 @@ describe('Query API', () => {
|
|||
|
||||
describe('read a different token', () => {
|
||||
modifiedInIvy(
|
||||
'Breaking change in Ivy: no longer allow multiple local refs with the same name, all local refs are now unique') &&
|
||||
it('should contain all content children', () => {
|
||||
'Breaking change in Ivy: no longer allow multiple local refs with the same name, all local refs are now unique')
|
||||
.it('should contain all content children', () => {
|
||||
const template =
|
||||
'<needs-content-children-read #q text="ca"><div #q text="cb"></div></needs-content-children-read>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
@ -477,17 +477,19 @@ describe('Query API', () => {
|
|||
|
||||
describe('querying in the view', () => {
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should contain all the elements in the view with that have the given directive', () => {
|
||||
const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
});
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should contain all the elements in the view with that have the given directive',
|
||||
() => {
|
||||
const template =
|
||||
'<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy') &&
|
||||
it('should not include directive present on the host element', () => {
|
||||
'FW-781 - Directives invocation sequence on root and nested elements is different in Ivy')
|
||||
.it('should not include directive present on the host element', () => {
|
||||
const template = '<needs-view-query #q text="self"></needs-view-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
|
||||
|
@ -588,8 +590,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
// Note: this test is just document our current behavior, which we do for performance reasons.
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases') &&
|
||||
it('should not affected queries for projected templates if views are detached or moved', () => {
|
||||
fixmeIvy('FW-782 - View queries are executed twice in some cases')
|
||||
.it('should not affected queries for projected templates if views are detached or moved', () => {
|
||||
const template =
|
||||
'<manual-projecting #q><ng-template let-x="x"><div [text]="x"></div></ng-template></manual-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
@ -615,8 +617,8 @@ describe('Query API', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-763 - LView tree not properly constructed / destroyed for dynamically inserted components') &&
|
||||
it('should remove manually projected templates if their parent view is destroyed', () => {
|
||||
'FW-763 - LView tree not properly constructed / destroyed for dynamically inserted components')
|
||||
.it('should remove manually projected templates if their parent view is destroyed', () => {
|
||||
const template = `
|
||||
<manual-projecting #q><ng-template #tpl><div text="1"></div></ng-template></manual-projecting>
|
||||
<div *ngIf="shouldShow">
|
||||
|
@ -635,34 +637,34 @@ describe('Query API', () => {
|
|||
expect(q.query.length).toBe(0);
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should not throw if a content template is queried and created in the view during change detection',
|
||||
() => {
|
||||
@Component(
|
||||
{selector: 'auto-projecting', template: '<div *ngIf="true; then: content"></div>'})
|
||||
class AutoProjecting {
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChild(TemplateRef)
|
||||
content !: TemplateRef<any>;
|
||||
fixmeIvy('unknown').it(
|
||||
'should not throw if a content template is queried and created in the view during change detection',
|
||||
() => {
|
||||
@Component(
|
||||
{selector: 'auto-projecting', template: '<div *ngIf="true; then: content"></div>'})
|
||||
class AutoProjecting {
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChild(TemplateRef)
|
||||
content !: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChildren(TextDirective)
|
||||
query !: QueryList<TextDirective>;
|
||||
}
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChildren(TextDirective)
|
||||
query !: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AutoProjecting]});
|
||||
const template =
|
||||
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
TestBed.configureTestingModule({declarations: [AutoProjecting]});
|
||||
const template =
|
||||
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
// This should be 1, but due to
|
||||
// https://github.com/angular/angular/issues/15117
|
||||
// this is 0.
|
||||
expect(q.query.length).toBe(0);
|
||||
});
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
// This should be 1, but due to
|
||||
// https://github.com/angular/angular/issues/15117
|
||||
// this is 0.
|
||||
expect(q.query.length).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
describe('platform pipes', () => {
|
||||
beforeEach(() => { TestBed.configureCompiler({...config}); });
|
||||
|
||||
fixmeIvy('unknown') && it('should overwrite them by custom pipes', () => {
|
||||
fixmeIvy('unknown').it('should overwrite them by custom pipes', () => {
|
||||
TestBed.configureTestingModule({declarations: [CustomPipe]});
|
||||
const template = '{{true | somePipe}}';
|
||||
TestBed.overrideComponent(MyComp1, {set: {template}});
|
||||
|
@ -74,46 +74,46 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(CountingPipe.calls).toBe(1);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account') &&
|
||||
it('should only update the bound property when using asyncPipe - #15205',
|
||||
fakeAsync(() => {
|
||||
@Component({template: '<div myDir [a]="p | async" [b]="2"></div>'})
|
||||
class MyComp {
|
||||
p = Promise.resolve(1);
|
||||
}
|
||||
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
|
||||
.it('should only update the bound property when using asyncPipe - #15205',
|
||||
fakeAsync(() => {
|
||||
@Component({template: '<div myDir [a]="p | async" [b]="2"></div>'})
|
||||
class MyComp {
|
||||
p = Promise.resolve(1);
|
||||
}
|
||||
|
||||
@Directive({selector: '[myDir]'})
|
||||
class MyDir {
|
||||
setterCalls: {[key: string]: any} = {};
|
||||
// TODO(issue/24571): remove '!'.
|
||||
changes !: SimpleChanges;
|
||||
@Directive({selector: '[myDir]'})
|
||||
class MyDir {
|
||||
setterCalls: {[key: string]: any} = {};
|
||||
// TODO(issue/24571): remove '!'.
|
||||
changes !: SimpleChanges;
|
||||
|
||||
@Input()
|
||||
set a(v: number) { this.setterCalls['a'] = v; }
|
||||
@Input()
|
||||
set b(v: number) { this.setterCalls['b'] = v; }
|
||||
@Input()
|
||||
set a(v: number) { this.setterCalls['a'] = v; }
|
||||
@Input()
|
||||
set b(v: number) { this.setterCalls['b'] = v; }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) { this.changes = changes; }
|
||||
}
|
||||
ngOnChanges(changes: SimpleChanges) { this.changes = changes; }
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const dir =
|
||||
fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir) as MyDir;
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
const dir =
|
||||
fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir) as MyDir;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(dir.setterCalls).toEqual({'a': null, 'b': 2});
|
||||
expect(Object.keys(dir.changes)).toEqual(['a', 'b']);
|
||||
fixture.detectChanges();
|
||||
expect(dir.setterCalls).toEqual({'a': null, 'b': 2});
|
||||
expect(Object.keys(dir.changes)).toEqual(['a', 'b']);
|
||||
|
||||
dir.setterCalls = {};
|
||||
dir.changes = {};
|
||||
dir.setterCalls = {};
|
||||
dir.changes = {};
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(dir.setterCalls).toEqual({'a': 1});
|
||||
expect(Object.keys(dir.changes)).toEqual(['a']);
|
||||
}));
|
||||
expect(dir.setterCalls).toEqual({'a': 1});
|
||||
expect(Object.keys(dir.changes)).toEqual(['a']);
|
||||
}));
|
||||
|
||||
it('should only evaluate methods once - #10639', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyCountingComp]});
|
||||
|
@ -333,8 +333,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(fixture.debugElement.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
modifiedInIvy('Comment node order changed') &&
|
||||
it('should allow empty embedded templates', () => {
|
||||
modifiedInIvy('Comment node order changed')
|
||||
.it('should allow empty embedded templates', () => {
|
||||
@Component({template: '<ng-template [ngIf]="true"></ng-template>'})
|
||||
class MyComp {
|
||||
}
|
||||
|
@ -351,37 +351,36 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should support @ContentChild and @Input on the same property for static queries',
|
||||
() => {
|
||||
@Directive({selector: 'test'})
|
||||
class Test {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() @ContentChild(TemplateRef) tpl !: TemplateRef<any>;
|
||||
}
|
||||
fixmeIvy('unknown').it(
|
||||
'should support @ContentChild and @Input on the same property for static queries', () => {
|
||||
@Directive({selector: 'test'})
|
||||
class Test {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() @ContentChild(TemplateRef) tpl !: TemplateRef<any>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<test></test><br>
|
||||
<test><ng-template>Custom as a child</ng-template></test><br>
|
||||
<ng-template #custom>Custom as a binding</ng-template>
|
||||
<test [tpl]="custom"></test><br>
|
||||
`
|
||||
})
|
||||
class App {
|
||||
}
|
||||
})
|
||||
class App {
|
||||
}
|
||||
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [App, Test]}).createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const fixture =
|
||||
TestBed.configureTestingModule({declarations: [App, Test]}).createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
const testDirs =
|
||||
fixture.debugElement.queryAll(By.directive(Test)).map(el => el.injector.get(Test));
|
||||
expect(testDirs[0].tpl).toBeUndefined();
|
||||
expect(testDirs[1].tpl).toBeDefined();
|
||||
expect(testDirs[2].tpl).toBeDefined();
|
||||
});
|
||||
const testDirs =
|
||||
fixture.debugElement.queryAll(By.directive(Test)).map(el => el.injector.get(Test));
|
||||
expect(testDirs[0].tpl).toBeUndefined();
|
||||
expect(testDirs[1].tpl).toBeDefined();
|
||||
expect(testDirs[2].tpl).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not add ng-version for dynamically created components', () => {
|
||||
@Component({template: ''})
|
||||
|
|
|
@ -52,8 +52,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
afterEach(() => { getDOM().log = originalLog; });
|
||||
|
||||
describe('events', () => {
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state') &&
|
||||
it('should disallow binding to attr.on*', () => {
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state')
|
||||
.it('should disallow binding to attr.on*', () => {
|
||||
const template = `<div [attr.onclick]="ctxProp"></div>`;
|
||||
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
||||
|
||||
|
@ -62,8 +62,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state') &&
|
||||
it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => {
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state')
|
||||
.it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => {
|
||||
const template = `<div [onclick]="ctxProp"></div>`;
|
||||
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@ -75,8 +75,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-786: Element properties and directive inputs are not distinguished for sanitisation purposes') &&
|
||||
it('should disallow binding to on* unless it is consumed by a directive', () => {
|
||||
'FW-786: Element properties and directive inputs are not distinguished for sanitisation purposes')
|
||||
.it('should disallow binding to on* unless it is consumed by a directive', () => {
|
||||
const template = `<div [onPrefixedProp]="ctxProp" [onclick]="ctxProp"></div>`;
|
||||
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
|
@ -173,8 +173,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
checkEscapeOfHrefProperty(fixture, true);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-785: Host bindings are not sanitised') &&
|
||||
it('should escape unsafe properties if they are used in host bindings', () => {
|
||||
fixmeIvy('FW-785: Host bindings are not sanitised')
|
||||
.it('should escape unsafe properties if they are used in host bindings', () => {
|
||||
@Directive({selector: '[dirHref]'})
|
||||
class HrefDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
|
@ -190,8 +190,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
checkEscapeOfHrefProperty(fixture, false);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-785: Host bindings are not sanitised') &&
|
||||
it('should escape unsafe attributes if they are used in host bindings', () => {
|
||||
fixmeIvy('FW-785: Host bindings are not sanitised')
|
||||
.it('should escape unsafe attributes if they are used in host bindings', () => {
|
||||
@Directive({selector: '[dirHref]'})
|
||||
class HrefDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
|
@ -227,8 +227,8 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
expect(getDOM().getStyle(e, 'background')).not.toContain('javascript');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state') &&
|
||||
it('should escape unsafe SVG attributes', () => {
|
||||
fixmeIvy('FW-787: Exception in template parsing leaves TestBed in corrupted state')
|
||||
.it('should escape unsafe SVG attributes', () => {
|
||||
const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
|
||||
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
||||
|
||||
|
|
|
@ -102,167 +102,167 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
function declareTests(
|
||||
{ngUrl, templateDecorator}:
|
||||
{ngUrl: string, templateDecorator: (template: string) => { [key: string]: any }}) {
|
||||
fixmeIvy('unknown') &&
|
||||
it('should use the right source url in html parse errors', fakeAsync(() => {
|
||||
@Component({...templateDecorator('<div>\n </error>')})
|
||||
class MyComp {
|
||||
}
|
||||
fixmeIvy('unknown').it(
|
||||
'should use the right source url in html parse errors', fakeAsync(() => {
|
||||
@Component({...templateDecorator('<div>\n </error>')})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => compileAndCreateComponent(MyComp))
|
||||
.toThrowError(new RegExp(
|
||||
`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`));
|
||||
}));
|
||||
expect(() => compileAndCreateComponent(MyComp))
|
||||
.toThrowError(
|
||||
new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:2`));
|
||||
}));
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should use the right source url in template parse errors', fakeAsync(() => {
|
||||
@Component({...templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>')})
|
||||
class MyComp {
|
||||
}
|
||||
fixmeIvy('unknown').it(
|
||||
'should use the right source url in template parse errors', fakeAsync(() => {
|
||||
@Component({...templateDecorator('<div>\n <div unknown="{{ctxProp}}"></div>')})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => compileAndCreateComponent(MyComp))
|
||||
.toThrowError(new RegExp(
|
||||
`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`));
|
||||
}));
|
||||
expect(() => compileAndCreateComponent(MyComp))
|
||||
.toThrowError(
|
||||
new RegExp(`Template parse errors[\\s\\S]*${ngUrl.replace('$', '\\$')}@1:7`));
|
||||
}));
|
||||
|
||||
fixmeIvy('unknown') && it('should create a sourceMap for templates', fakeAsync(() => {
|
||||
const template = `Hello World!`;
|
||||
fixmeIvy('unknown').it('should create a sourceMap for templates', fakeAsync(() => {
|
||||
const template = `Hello World!`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
compileAndCreateComponent(MyComp);
|
||||
compileAndCreateComponent(MyComp);
|
||||
|
||||
const sourceMap =
|
||||
getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js');
|
||||
expect(sourceMap.sources).toEqual([
|
||||
'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl
|
||||
]);
|
||||
expect(sourceMap.sourcesContent).toEqual([' ', template]);
|
||||
}));
|
||||
const sourceMap =
|
||||
getSourceMap('ng:///DynamicTestModule/MyComp.ngfactory.js');
|
||||
expect(sourceMap.sources).toEqual([
|
||||
'ng:///DynamicTestModule/MyComp.ngfactory.js', ngUrl
|
||||
]);
|
||||
expect(sourceMap.sourcesContent).toEqual([' ', template]);
|
||||
}));
|
||||
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should report source location for di errors', fakeAsync(() => {
|
||||
const template = `<div>\n <div someDir></div></div>`;
|
||||
fixmeIvy('unknown').it(
|
||||
'should report source location for di errors', fakeAsync(() => {
|
||||
const template = `<div>\n <div someDir></div></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() { throw new Error('Test'); }
|
||||
}
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() { throw new Error('Test'); }
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
compileAndCreateComponent(MyComp);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
compileAndCreateComponent(MyComp);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should report di errors with multiple elements and directives', fakeAsync(() => {
|
||||
const template = `<div someDir></div><div someDir="throw"></div>`;
|
||||
fixmeIvy('unknown').it(
|
||||
'should report di errors with multiple elements and directives', fakeAsync(() => {
|
||||
const template = `<div someDir></div><div someDir="throw"></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor(@Attribute('someDir') someDir: string) {
|
||||
if (someDir === 'throw') {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
}
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor(@Attribute('someDir') someDir: string) {
|
||||
if (someDir === 'throw') {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
compileAndCreateComponent(MyComp);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the 2nd-element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 1,
|
||||
column: 19,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
let error: any;
|
||||
try {
|
||||
compileAndCreateComponent(MyComp);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// The error should be logged from the 2nd-element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 1,
|
||||
column: 19,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should report source location for binding errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span [title]="createError()"></span></div>`;
|
||||
fixmeIvy('unknown').it(
|
||||
'should report source location for binding errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span [title]="createError()"></span></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
}
|
||||
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
|
||||
let error: any;
|
||||
try {
|
||||
comp.detectChanges();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// the stack should point to the binding
|
||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
let error: any;
|
||||
try {
|
||||
comp.detectChanges();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
// the stack should point to the binding
|
||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('unknown') &&
|
||||
it('should report source location for event errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span (click)="createError()"></span></div>`;
|
||||
fixmeIvy('unknown').it(
|
||||
'should report source location for event errors', fakeAsync(() => {
|
||||
const template = `<div>\n <span (click)="createError()"></span></div>`;
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
}
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
}
|
||||
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
|
||||
let error: any;
|
||||
const errorHandler = TestBed.get(ErrorHandler);
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e);
|
||||
comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT');
|
||||
expect(error).toBeTruthy();
|
||||
// the stack should point to the binding
|
||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
let error: any;
|
||||
const errorHandler = TestBed.get(ErrorHandler);
|
||||
spyOn(errorHandler, 'handleError').and.callFake((e: any) => error = e);
|
||||
comp.debugElement.children[0].children[0].triggerEventHandler('click', 'EVENT');
|
||||
expect(error).toBeTruthy();
|
||||
// the stack should point to the binding
|
||||
expect(getSourcePositionForStack(error.stack)).toEqual({
|
||||
line: 2,
|
||||
column: 12,
|
||||
source: ngUrl,
|
||||
});
|
||||
// The error should be logged from the element
|
||||
expect(getSourcePositionForStack(getErrorLoggerStack(error))).toEqual({
|
||||
line: 2,
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
|
||||
}));
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') && it('should not inject deps from sibling root elements', () => {
|
||||
fixmeIvy('unknown').it('should not inject deps from sibling root elements', () => {
|
||||
const rootElNodes = [
|
||||
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
|
||||
|
@ -181,7 +181,7 @@ import {fixmeIvy} from '@angular/private/testing';
|
|||
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||
});
|
||||
|
||||
fixmeIvy('unknown') && it('should throw for missing dependencies', () => {
|
||||
fixmeIvy('unknown').it('should throw for missing dependencies', () => {
|
||||
expect(() => createAndGetRootNodes(compViewDef([
|
||||
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(1, NodeFlags.None, null, 0, SomeService, ['nonExistingDep'])
|
||||
|
|
|
@ -160,7 +160,8 @@ function bootstrap(
|
|||
|
||||
afterEach(destroyPlatform);
|
||||
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation') &&
|
||||
// TODO(misko): can't use `fixmeIvy.it` because the `it` is somehow special here.
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation').isEnabled &&
|
||||
it('should throw if bootstrapped Directive is not a Component',
|
||||
inject([AsyncTestCompleter], (done: AsyncTestCompleter) => {
|
||||
const logger = new MockConsole();
|
||||
|
@ -188,7 +189,8 @@ function bootstrap(
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation') &&
|
||||
// TODO(misko): can't use `fixmeIvy.it` because the `it` is somehow special here.
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation').isEnabled &&
|
||||
it('should throw if no provider',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
const logger = new MockConsole();
|
||||
|
|
|
@ -251,8 +251,8 @@ class CompWithUrlTemplate {
|
|||
expect(compFixture.componentInstance).toBeAnInstanceOf(CompUsingModuleDirectiveAndPipe);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') &&
|
||||
it('should use set up directives and pipes', () => {
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView')
|
||||
.it('should use set up directives and pipes', () => {
|
||||
const compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe);
|
||||
const el = compFixture.debugElement;
|
||||
|
||||
|
@ -289,14 +289,14 @@ class CompWithUrlTemplate {
|
|||
expect(service.value).toEqual('real value');
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') &&
|
||||
it('should use set up directives and pipes', withModule(moduleConfig, () => {
|
||||
const compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe);
|
||||
const el = compFixture.debugElement;
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView')
|
||||
.it('should use set up directives and pipes', withModule(moduleConfig, () => {
|
||||
const compFixture = TestBed.createComponent(CompUsingModuleDirectiveAndPipe);
|
||||
const el = compFixture.debugElement;
|
||||
|
||||
compFixture.detectChanges();
|
||||
expect(el.children[0].properties['title']).toBe('transformed someValue');
|
||||
}));
|
||||
compFixture.detectChanges();
|
||||
expect(el.children[0].properties['title']).toBe('transformed someValue');
|
||||
}));
|
||||
|
||||
it('should use set up library modules',
|
||||
withModule(moduleConfig).inject([SomeLibModule], (libModule: SomeLibModule) => {
|
||||
|
@ -310,12 +310,13 @@ class CompWithUrlTemplate {
|
|||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation') && isBrowser &&
|
||||
it('should allow to createSync components with templateUrl after explicit async compilation',
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(CompWithUrlTemplate);
|
||||
expect(fixture.nativeElement).toHaveText('from external template');
|
||||
});
|
||||
isBrowser &&
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation')
|
||||
.it('should allow to createSync components with templateUrl after explicit async compilation',
|
||||
() => {
|
||||
const fixture = TestBed.createComponent(CompWithUrlTemplate);
|
||||
expect(fixture.nativeElement).toHaveText('from external template');
|
||||
});
|
||||
});
|
||||
|
||||
describe('overwriting metadata', () => {
|
||||
|
@ -371,8 +372,8 @@ class CompWithUrlTemplate {
|
|||
.overrideDirective(
|
||||
SomeDirective, {set: {selector: '[someDir]', host: {'[title]': 'someProp'}}});
|
||||
});
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView') &&
|
||||
it('should work', () => {
|
||||
fixmeIvy('FW-681: not possible to retrieve host property bindings from TView')
|
||||
.it('should work', () => {
|
||||
const compFixture = TestBed.createComponent(SomeComponent);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.debugElement.children[0].properties['title']).toEqual('hello');
|
||||
|
@ -387,8 +388,8 @@ class CompWithUrlTemplate {
|
|||
.overridePipe(SomePipe, {set: {name: 'somePipe'}})
|
||||
.overridePipe(SomePipe, {add: {pure: false}});
|
||||
});
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should work', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should work', () => {
|
||||
const compFixture = TestBed.createComponent(SomeComponent);
|
||||
compFixture.detectChanges();
|
||||
expect(compFixture.nativeElement).toHaveText('transformed hello');
|
||||
|
@ -458,8 +459,8 @@ class CompWithUrlTemplate {
|
|||
expect(TestBed.get('a')).toBe('mockA: depValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support SkipSelf', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support SkipSelf', () => {
|
||||
@NgModule({
|
||||
providers: [
|
||||
{provide: 'a', useValue: 'aValue'},
|
||||
|
@ -501,8 +502,8 @@ class CompWithUrlTemplate {
|
|||
expect(someModule).toBeAnInstanceOf(SomeModule);
|
||||
});
|
||||
|
||||
obsoleteInIvy(`deprecated method, won't be reimplemented for Render3`) &&
|
||||
it('should keep imported NgModules lazy with deprecatedOverrideProvider', () => {
|
||||
obsoleteInIvy(`deprecated method, won't be reimplemented for Render3`)
|
||||
.it('should keep imported NgModules lazy with deprecatedOverrideProvider', () => {
|
||||
let someModule: SomeModule|undefined;
|
||||
|
||||
@NgModule()
|
||||
|
@ -553,8 +554,8 @@ class CompWithUrlTemplate {
|
|||
});
|
||||
|
||||
describe('in Components', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support useValue', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support useValue', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
|
@ -571,8 +572,8 @@ class CompWithUrlTemplate {
|
|||
expect(ctx.debugElement.injector.get('a')).toBe('mockValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support useFactory', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support useFactory', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
|
@ -591,8 +592,8 @@ class CompWithUrlTemplate {
|
|||
expect(ctx.debugElement.injector.get('a')).toBe('mockA: depValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support @Optional without matches', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support @Optional without matches', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
|
@ -611,8 +612,8 @@ class CompWithUrlTemplate {
|
|||
expect(ctx.debugElement.injector.get('a')).toBe('mockA: null');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support Optional with matches', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support Optional with matches', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [
|
||||
|
@ -632,8 +633,8 @@ class CompWithUrlTemplate {
|
|||
expect(ctx.debugElement.injector.get('a')).toBe('mockA: depValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support SkipSelf', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support SkipSelf', () => {
|
||||
@Directive({
|
||||
selector: '[myDir]',
|
||||
providers: [
|
||||
|
@ -662,8 +663,8 @@ class CompWithUrlTemplate {
|
|||
.toBe('mockA: parentDepValue');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should support multiple providers in a template', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should support multiple providers in a template', () => {
|
||||
@Directive({
|
||||
selector: '[myDir1]',
|
||||
providers: [
|
||||
|
@ -708,9 +709,8 @@ class CompWithUrlTemplate {
|
|||
constructor(@Inject('a') a: any, @Inject('b') b: any) {}
|
||||
}
|
||||
|
||||
fixmeIvy(
|
||||
'FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should inject providers that were declared before it', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should inject providers that were declared before it', () => {
|
||||
TestBed.overrideProvider(
|
||||
'b', {useFactory: (a: string) => `mockB: ${a}`, deps: ['a']});
|
||||
const ctx = TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
|
@ -719,9 +719,8 @@ class CompWithUrlTemplate {
|
|||
expect(ctx.debugElement.injector.get('b')).toBe('mockB: aValue');
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should inject providers that were declared after it', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should inject providers that were declared after it', () => {
|
||||
TestBed.overrideProvider(
|
||||
'a', {useFactory: (b: string) => `mockA: ${b}`, deps: ['b']});
|
||||
const ctx = TestBed.configureTestingModule({declarations: [MyComp]})
|
||||
|
@ -741,8 +740,8 @@ class CompWithUrlTemplate {
|
|||
});
|
||||
|
||||
describe('overrideTemplateUsingTestingModule', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should compile the template in the context of the testing module', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should compile the template in the context of the testing module', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
prop = 'some prop';
|
||||
|
@ -770,8 +769,8 @@ class CompWithUrlTemplate {
|
|||
expect(testDir !.test).toBe('some prop');
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should throw if the TestBed is already created', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should throw if the TestBed is already created', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
}
|
||||
|
@ -783,8 +782,8 @@ class CompWithUrlTemplate {
|
|||
/Cannot override template when the test module has already been instantiated/);
|
||||
});
|
||||
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)') &&
|
||||
it('should reset overrides when the testing module is resetted', () => {
|
||||
fixmeIvy('FW-788: Support metadata override in TestBed (for AOT-compiled components)')
|
||||
.it('should reset overrides when the testing module is resetted', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
}
|
||||
|
@ -809,30 +808,30 @@ class CompWithUrlTemplate {
|
|||
{providers: [{provide: ResourceLoader, useValue: {get: resourceLoaderGet}}]});
|
||||
});
|
||||
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation') &&
|
||||
it('should use set up providers', fakeAsync(() => {
|
||||
TestBed.compileComponents();
|
||||
tick();
|
||||
const compFixture = TestBed.createComponent(CompWithUrlTemplate);
|
||||
expect(compFixture.nativeElement).toHaveText('Hello world!');
|
||||
}));
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation')
|
||||
.it('should use set up providers', fakeAsync(() => {
|
||||
TestBed.compileComponents();
|
||||
tick();
|
||||
const compFixture = TestBed.createComponent(CompWithUrlTemplate);
|
||||
expect(compFixture.nativeElement).toHaveText('Hello world!');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('useJit true', () => {
|
||||
beforeEach(() => TestBed.configureCompiler({useJit: true}));
|
||||
obsoleteInIvy('the Render3 compiler JiT mode is not configurable') &&
|
||||
it('should set the value into CompilerConfig',
|
||||
inject([CompilerConfig], (config: CompilerConfig) => {
|
||||
expect(config.useJit).toBe(true);
|
||||
}));
|
||||
obsoleteInIvy('the Render3 compiler JiT mode is not configurable')
|
||||
.it('should set the value into CompilerConfig',
|
||||
inject([CompilerConfig], (config: CompilerConfig) => {
|
||||
expect(config.useJit).toBe(true);
|
||||
}));
|
||||
});
|
||||
describe('useJit false', () => {
|
||||
beforeEach(() => TestBed.configureCompiler({useJit: false}));
|
||||
obsoleteInIvy('the Render3 compiler JiT mode is not configurable') &&
|
||||
it('should set the value into CompilerConfig',
|
||||
inject([CompilerConfig], (config: CompilerConfig) => {
|
||||
expect(config.useJit).toBe(false);
|
||||
}));
|
||||
obsoleteInIvy('the Render3 compiler JiT mode is not configurable')
|
||||
.it('should set the value into CompilerConfig',
|
||||
inject([CompilerConfig], (config: CompilerConfig) => {
|
||||
expect(config.useJit).toBe(false);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -921,28 +920,28 @@ class CompWithUrlTemplate {
|
|||
{providers: [{provide: ResourceLoader, useValue: {get: resourceLoaderGet}}]});
|
||||
});
|
||||
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation') &&
|
||||
it('should report an error for declared components with templateUrl which never call TestBed.compileComponents',
|
||||
() => {
|
||||
const itPromise = patchJasmineIt();
|
||||
fixmeIvy('FW-553: TestBed is unaware of async compilation')
|
||||
.it('should report an error for declared components with templateUrl which never call TestBed.compileComponents',
|
||||
() => {
|
||||
const itPromise = patchJasmineIt();
|
||||
|
||||
expect(
|
||||
() =>
|
||||
it('should fail', withModule(
|
||||
{declarations: [CompWithUrlTemplate]},
|
||||
() => TestBed.createComponent(CompWithUrlTemplate))))
|
||||
.toThrowError(
|
||||
`This test module uses the component ${stringify(CompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
|
||||
`Please call "TestBed.compileComponents" before your test.`);
|
||||
expect(
|
||||
() => it(
|
||||
'should fail', withModule(
|
||||
{declarations: [CompWithUrlTemplate]},
|
||||
() => TestBed.createComponent(CompWithUrlTemplate))))
|
||||
.toThrowError(
|
||||
`This test module uses the component ${stringify(CompWithUrlTemplate)} which is using a "templateUrl" or "styleUrls", but they were never compiled. ` +
|
||||
`Please call "TestBed.compileComponents" before your test.`);
|
||||
|
||||
restoreJasmineIt();
|
||||
});
|
||||
restoreJasmineIt();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
fixmeIvy(`FW-721: Bindings to unknown properties are not reported as errors`) &&
|
||||
it('should error on unknown bound properties on custom elements by default', () => {
|
||||
fixmeIvy(`FW-721: Bindings to unknown properties are not reported as errors`)
|
||||
.it('should error on unknown bound properties on custom elements by default', () => {
|
||||
@Component({template: '<some-element [someUnknownProp]="true"></some-element>'})
|
||||
class ComponentUsingInvalidProperty {
|
||||
}
|
||||
|
|
|
@ -579,28 +579,28 @@ class HiddenModule {
|
|||
});
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-672: SVG xlink:href is sanitized to :xlink:href (extra ":")') &&
|
||||
it('works with SVG elements', async(() => {
|
||||
renderModule(SVGServerModule, {document: doc}).then(output => {
|
||||
expect(output).toBe(
|
||||
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' +
|
||||
'<svg><use xlink:href="#clear"></use></svg></app></body></html>');
|
||||
called = true;
|
||||
});
|
||||
}));
|
||||
fixmeIvy('FW-672: SVG xlink:href is sanitized to :xlink:href (extra ":")')
|
||||
.it('works with SVG elements', async(() => {
|
||||
renderModule(SVGServerModule, {document: doc}).then(output => {
|
||||
expect(output).toBe(
|
||||
'<html><head></head><body><app ng-version="0.0.0-PLACEHOLDER">' +
|
||||
'<svg><use xlink:href="#clear"></use></svg></app></body></html>');
|
||||
called = true;
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy(
|
||||
`FW-643: Components with animations throw with "Failed to execute 'setAttribute' on 'Element'`) &&
|
||||
it('works with animation', async(() => {
|
||||
renderModule(AnimationServerModule, {document: doc}).then(output => {
|
||||
expect(output).toContain('Works!');
|
||||
expect(output).toContain('ng-trigger-myAnimation');
|
||||
expect(output).toContain('opacity:1;');
|
||||
expect(output).toContain('transform:translate3d(0 , 0 , 0);');
|
||||
expect(output).toContain('font-weight:bold;');
|
||||
called = true;
|
||||
});
|
||||
}));
|
||||
`FW-643: Components with animations throw with "Failed to execute 'setAttribute' on 'Element'`)
|
||||
.it('works with animation', async(() => {
|
||||
renderModule(AnimationServerModule, {document: doc}).then(output => {
|
||||
expect(output).toContain('Works!');
|
||||
expect(output).toContain('ng-trigger-myAnimation');
|
||||
expect(output).toContain('opacity:1;');
|
||||
expect(output).toContain('transform:translate3d(0 , 0 , 0);');
|
||||
expect(output).toContain('font-weight:bold;');
|
||||
called = true;
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle ViewEncapsulation.Native', async(() => {
|
||||
renderModule(NativeExampleModule, {document: doc}).then(output => {
|
||||
|
|
|
@ -95,44 +95,44 @@ let lastCreatedRenderer: Renderer2;
|
|||
expect(renderEl).toHaveText('Hello World!');
|
||||
});
|
||||
|
||||
fixmeIvy('#FW-750 - fixture.debugElement is null') &&
|
||||
it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements',
|
||||
() => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<input [title]="y" style="position:absolute">')
|
||||
.createComponent(MyComp2);
|
||||
fixmeIvy('#FW-750 - fixture.debugElement is null')
|
||||
.it('should update any element property/attributes/class/style(s) independent of the compilation on the root element and other elements',
|
||||
() => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<input [title]="y" style="position:absolute">')
|
||||
.createComponent(MyComp2);
|
||||
|
||||
const checkSetters = (componentRef: ComponentRef<any>, workerEl: any) => {
|
||||
expect(lastCreatedRenderer).not.toBeNull();
|
||||
const checkSetters = (componentRef: ComponentRef<any>, workerEl: any) => {
|
||||
expect(lastCreatedRenderer).not.toBeNull();
|
||||
|
||||
const el = getRenderElement(workerEl);
|
||||
lastCreatedRenderer.setProperty(workerEl, 'tabIndex', 1);
|
||||
expect(el.tabIndex).toEqual(1);
|
||||
const el = getRenderElement(workerEl);
|
||||
lastCreatedRenderer.setProperty(workerEl, 'tabIndex', 1);
|
||||
expect(el.tabIndex).toEqual(1);
|
||||
|
||||
lastCreatedRenderer.addClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(true);
|
||||
lastCreatedRenderer.addClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(true);
|
||||
|
||||
lastCreatedRenderer.removeClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(false);
|
||||
lastCreatedRenderer.removeClass(workerEl, 'a');
|
||||
expect(getDOM().hasClass(el, 'a')).toBe(false);
|
||||
|
||||
lastCreatedRenderer.setStyle(workerEl, 'width', '10px');
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('10px');
|
||||
lastCreatedRenderer.setStyle(workerEl, 'width', '10px');
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('10px');
|
||||
|
||||
lastCreatedRenderer.removeStyle(workerEl, 'width');
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('');
|
||||
lastCreatedRenderer.removeStyle(workerEl, 'width');
|
||||
expect(getDOM().getStyle(el, 'width')).toEqual('');
|
||||
|
||||
lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue');
|
||||
expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue');
|
||||
};
|
||||
lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue');
|
||||
expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue');
|
||||
};
|
||||
|
||||
// root element
|
||||
checkSetters(fixture.componentRef, fixture.nativeElement);
|
||||
// nested elements
|
||||
checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement);
|
||||
});
|
||||
// root element
|
||||
checkSetters(fixture.componentRef, fixture.nativeElement);
|
||||
// nested elements
|
||||
checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement);
|
||||
});
|
||||
|
||||
fixmeIvy('#FW-664 ng-reflect-* is not supported') &&
|
||||
it('should update any template comment property/attributes', () => {
|
||||
fixmeIvy('#FW-664 ng-reflect-* is not supported')
|
||||
.it('should update any template comment property/attributes', () => {
|
||||
const fixture =
|
||||
TestBed.overrideTemplate(MyComp2, '<ng-container *ngIf="ctxBoolProp"></ng-container>')
|
||||
.createComponent(MyComp2);
|
||||
|
@ -161,7 +161,7 @@ let lastCreatedRenderer: Renderer2;
|
|||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
fixmeIvy('#FW-750 - fixture.debugElement is null') && it('should listen to events', () => {
|
||||
fixmeIvy('#FW-750 - fixture.debugElement is null').it('should listen to events', () => {
|
||||
const fixture = TestBed.overrideTemplate(MyComp2, '<input (change)="ctxNumProp = 1">')
|
||||
.createComponent(MyComp2);
|
||||
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
|
||||
import {bazelDefineCompileValue} from './bazel_define_compile_value';
|
||||
|
||||
/**
|
||||
* Set this constant to `true` to run all tests and report which of the tests marked with `fixmeIvy`
|
||||
* are actually already passing.
|
||||
*
|
||||
* This is useful for locating already passing tests. The already passing tests should have their
|
||||
* `fixmeIvy` removed.
|
||||
*/
|
||||
const FIND_PASSING_TESTS = false;
|
||||
|
||||
/**
|
||||
* A function to conditionally include a test or a block of tests only when tests run against Ivy.
|
||||
|
@ -33,17 +41,21 @@ export const ivyEnabled = 'aot' === (bazelDefineCompileValue as string);
|
|||
* when running against Ivy.
|
||||
*
|
||||
* ```
|
||||
* fixmeIvy('some reason') && describe(...);
|
||||
* fixmeIvy('some reason').describe(...);
|
||||
* ```
|
||||
*
|
||||
* or
|
||||
*
|
||||
* ```
|
||||
* fixmeIvy('some reason') && it(...);
|
||||
* fixmeIvy('some reason').it(...);
|
||||
* ```
|
||||
*/
|
||||
export function fixmeIvy(reason: string): boolean {
|
||||
return !ivyEnabled;
|
||||
export function fixmeIvy(reason: string): JasmineMethods {
|
||||
if (FIND_PASSING_TESTS) {
|
||||
return ivyEnabled ? PASSTHROUGH : IGNORE;
|
||||
} else {
|
||||
return ivyEnabled ? IGNORE : PASSTHROUGH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,17 +66,17 @@ export function fixmeIvy(reason: string): boolean {
|
|||
* Any tests disabled using this switch should not be user-facing breaking changes.
|
||||
*
|
||||
* ```
|
||||
* obsoleteInIvy('some reason') && describe(...);
|
||||
* obsoleteInIvy('some reason').describe(...);
|
||||
* ```
|
||||
*
|
||||
* or
|
||||
*
|
||||
* ```
|
||||
* obsoleteInIvy('some reason') && it(...);
|
||||
* obsoleteInIvy('some reason').it(...);
|
||||
* ```
|
||||
*/
|
||||
export function obsoleteInIvy(reason: string): boolean {
|
||||
return !ivyEnabled;
|
||||
export function obsoleteInIvy(reason: string): JasmineMethods {
|
||||
return ivyEnabled ? IGNORE : PASSTHROUGH;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,15 +87,87 @@ export function obsoleteInIvy(reason: string): boolean {
|
|||
* documented as a breaking change.
|
||||
*
|
||||
* ```
|
||||
* modifiedInIvy('some reason') && describe(...);
|
||||
* modifiedInIvy('some reason').describe(...);
|
||||
* ```
|
||||
*
|
||||
* or
|
||||
*
|
||||
* ```
|
||||
* modifiedInIvy('some reason') && it(...);
|
||||
* modifiedInIvy('some reason').it(...);
|
||||
* ```
|
||||
*/
|
||||
export function modifiedInIvy(reason: string): boolean {
|
||||
return !ivyEnabled;
|
||||
export function modifiedInIvy(reason: string): JasmineMethods {
|
||||
return ivyEnabled ? IGNORE : PASSTHROUGH;
|
||||
}
|
||||
|
||||
export interface JasmineMethods {
|
||||
it: typeof it;
|
||||
fit: typeof fit;
|
||||
describe: typeof describe;
|
||||
fdescribe: typeof fdescribe;
|
||||
fixmeIvy: typeof fixmeIvy;
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
const PASSTHROUGH: JasmineMethods = {
|
||||
it: maybeAppendFindPassingTestsMarker(it),
|
||||
fit: maybeAppendFindPassingTestsMarker(fit),
|
||||
describe: maybeAppendFindPassingTestsMarker(describe),
|
||||
fdescribe: maybeAppendFindPassingTestsMarker(fdescribe),
|
||||
fixmeIvy: maybeAppendFindPassingTestsMarker(fixmeIvy),
|
||||
isEnabled: true,
|
||||
};
|
||||
|
||||
const FIND_PASSING_TESTS_MARKER = '__FIND_PASSING_TESTS_MARKER__';
|
||||
function maybeAppendFindPassingTestsMarker<T extends Function>(fn: T): T {
|
||||
return FIND_PASSING_TESTS ? function(...args: any[]) {
|
||||
if (typeof args[0] == 'string') {
|
||||
args[0] += FIND_PASSING_TESTS_MARKER;
|
||||
}
|
||||
return fn.apply(this, args);
|
||||
} : fn as any;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
const IGNORE: JasmineMethods = {
|
||||
it: noop,
|
||||
fit: noop,
|
||||
describe: noop,
|
||||
fdescribe: noop,
|
||||
fixmeIvy: (reason) => IGNORE,
|
||||
isEnabled: false,
|
||||
};
|
||||
|
||||
if (FIND_PASSING_TESTS) {
|
||||
const env = jasmine.getEnv();
|
||||
const passingTests: jasmine.CustomReporterResult[] = [];
|
||||
const stillFailing: jasmine.CustomReporterResult[] = [];
|
||||
let specCount = 0;
|
||||
env.clearReporters();
|
||||
env.addReporter({
|
||||
specDone: function(result: jasmine.CustomReporterResult) {
|
||||
specCount++;
|
||||
if (result.fullName.indexOf(FIND_PASSING_TESTS_MARKER) != -1) {
|
||||
(result.status == 'passed' ? passingTests : stillFailing).push(result);
|
||||
}
|
||||
},
|
||||
jasmineDone: function(details: jasmine.RunDetails) {
|
||||
if (passingTests.length) {
|
||||
passingTests.forEach((result) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('ALREADY PASSING', result.fullName.replace(FIND_PASSING_TESTS_MARKER, ''));
|
||||
});
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(
|
||||
`${specCount} specs,`, //
|
||||
`${passingTests.length} passing specs,`, //
|
||||
`${stillFailing.length} still failing specs`);
|
||||
|
||||
} else {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log('NO PASSING TESTS FOUND.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -467,38 +467,38 @@ describe('Integration', () => {
|
|||
expect(location.path()).toEqual('/child/simple');
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-768: markViewDirty instruction is scheduling a tick') &&
|
||||
it('should work when an outlet is added/removed', fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'someRoot',
|
||||
template: `[<div *ngIf="cond"><router-outlet></router-outlet></div>]`
|
||||
})
|
||||
class RootCmpWithLink {
|
||||
cond: boolean = true;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [RootCmpWithLink]});
|
||||
fixmeIvy('FW-768: markViewDirty instruction is scheduling a tick')
|
||||
.it('should work when an outlet is added/removed', fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'someRoot',
|
||||
template: `[<div *ngIf="cond"><router-outlet></router-outlet></div>]`
|
||||
})
|
||||
class RootCmpWithLink {
|
||||
cond: boolean = true;
|
||||
}
|
||||
TestBed.configureTestingModule({declarations: [RootCmpWithLink]});
|
||||
|
||||
const router: Router = TestBed.get(Router);
|
||||
const router: Router = TestBed.get(Router);
|
||||
|
||||
const fixture = createRoot(router, RootCmpWithLink);
|
||||
const fixture = createRoot(router, RootCmpWithLink);
|
||||
|
||||
router.resetConfig([
|
||||
{path: 'simple', component: SimpleCmp},
|
||||
{path: 'blank', component: BlankCmp},
|
||||
]);
|
||||
router.resetConfig([
|
||||
{path: 'simple', component: SimpleCmp},
|
||||
{path: 'blank', component: BlankCmp},
|
||||
]);
|
||||
|
||||
router.navigateByUrl('/simple');
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[simple]');
|
||||
router.navigateByUrl('/simple');
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[simple]');
|
||||
|
||||
fixture.componentInstance.cond = false;
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[]');
|
||||
fixture.componentInstance.cond = false;
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[]');
|
||||
|
||||
fixture.componentInstance.cond = true;
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[simple]');
|
||||
}));
|
||||
fixture.componentInstance.cond = true;
|
||||
advance(fixture);
|
||||
expect(fixture.nativeElement).toHaveText('[simple]');
|
||||
}));
|
||||
|
||||
it('should update location when navigating', fakeAsync(() => {
|
||||
@Component({template: `record`})
|
||||
|
@ -2595,46 +2595,46 @@ describe('Integration', () => {
|
|||
expect(canceledStatus).toEqual(false);
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-766: One router test is wrong') &&
|
||||
it('works with componentless routes',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
fixmeIvy('FW-766: One router test is wrong')
|
||||
.it('works with componentless routes',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.resetConfig([
|
||||
{
|
||||
path: 'grandparent',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'parent',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'child',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'simple',
|
||||
component: SimpleCmp,
|
||||
canDeactivate: ['RecordingDeactivate']
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{path: 'simple', component: SimpleCmp}
|
||||
]);
|
||||
router.resetConfig([
|
||||
{
|
||||
path: 'grandparent',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'parent',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'child',
|
||||
canDeactivate: ['RecordingDeactivate'],
|
||||
children: [{
|
||||
path: 'simple',
|
||||
component: SimpleCmp,
|
||||
canDeactivate: ['RecordingDeactivate']
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
},
|
||||
{path: 'simple', component: SimpleCmp}
|
||||
]);
|
||||
|
||||
router.navigateByUrl('/grandparent/parent/child/simple');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/grandparent/parent/child/simple');
|
||||
router.navigateByUrl('/grandparent/parent/child/simple');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/grandparent/parent/child/simple');
|
||||
|
||||
router.navigateByUrl('/simple');
|
||||
advance(fixture);
|
||||
router.navigateByUrl('/simple');
|
||||
advance(fixture);
|
||||
|
||||
const child = fixture.debugElement.children[1].componentInstance;
|
||||
const child = fixture.debugElement.children[1].componentInstance;
|
||||
|
||||
expect(log.map((a: any) => a.path)).toEqual([
|
||||
'simple', 'child', 'parent', 'grandparent'
|
||||
]);
|
||||
expect(log.map((a: any) => a.component)).toEqual([child, null, null, null]);
|
||||
})));
|
||||
expect(log.map((a: any) => a.path)).toEqual([
|
||||
'simple', 'child', 'parent', 'grandparent'
|
||||
]);
|
||||
expect(log.map((a: any) => a.component)).toEqual([child, null, null, null]);
|
||||
})));
|
||||
|
||||
it('works with aux routes',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
|
@ -3524,94 +3524,94 @@ describe('Integration', () => {
|
|||
expect(fixture.nativeElement).toHaveText('lazy-loaded-parent [lazy-loaded-child]');
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-646: Directive providers don\'t support primitive types as DI tokens') &&
|
||||
it('should have 2 injector trees: module and element',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
@Component({
|
||||
selector: 'lazy',
|
||||
template: 'parent[<router-outlet></router-outlet>]',
|
||||
viewProviders: [
|
||||
{provide: 'shadow', useValue: 'from parent component'},
|
||||
],
|
||||
})
|
||||
class Parent {
|
||||
}
|
||||
fixmeIvy('FW-646: Directive providers don\'t support primitive types as DI tokens')
|
||||
.it('should have 2 injector trees: module and element',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
@Component({
|
||||
selector: 'lazy',
|
||||
template: 'parent[<router-outlet></router-outlet>]',
|
||||
viewProviders: [
|
||||
{provide: 'shadow', useValue: 'from parent component'},
|
||||
],
|
||||
})
|
||||
class Parent {
|
||||
}
|
||||
|
||||
@Component({selector: 'lazy', template: 'child'})
|
||||
class Child {
|
||||
}
|
||||
@Component({selector: 'lazy', template: 'child'})
|
||||
class Child {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Parent],
|
||||
imports: [RouterModule.forChild([{
|
||||
path: 'parent',
|
||||
component: Parent,
|
||||
children: [
|
||||
{path: 'child', loadChildren: 'child'},
|
||||
]
|
||||
}])],
|
||||
providers: [
|
||||
{provide: 'moduleName', useValue: 'parent'},
|
||||
{provide: 'fromParent', useValue: 'from parent'},
|
||||
],
|
||||
})
|
||||
class ParentModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Parent],
|
||||
imports: [RouterModule.forChild([{
|
||||
path: 'parent',
|
||||
component: Parent,
|
||||
children: [
|
||||
{path: 'child', loadChildren: 'child'},
|
||||
]
|
||||
}])],
|
||||
providers: [
|
||||
{provide: 'moduleName', useValue: 'parent'},
|
||||
{provide: 'fromParent', useValue: 'from parent'},
|
||||
],
|
||||
})
|
||||
class ParentModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Child],
|
||||
imports: [RouterModule.forChild([{path: '', component: Child}])],
|
||||
providers: [
|
||||
{provide: 'moduleName', useValue: 'child'},
|
||||
{provide: 'fromChild', useValue: 'from child'},
|
||||
{provide: 'shadow', useValue: 'from child module'},
|
||||
],
|
||||
})
|
||||
class ChildModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Child],
|
||||
imports: [RouterModule.forChild([{path: '', component: Child}])],
|
||||
providers: [
|
||||
{provide: 'moduleName', useValue: 'child'},
|
||||
{provide: 'fromChild', useValue: 'from child'},
|
||||
{provide: 'shadow', useValue: 'from child module'},
|
||||
],
|
||||
})
|
||||
class ChildModule {
|
||||
}
|
||||
|
||||
loader.stubbedModules = {
|
||||
parent: ParentModule,
|
||||
child: ChildModule,
|
||||
};
|
||||
loader.stubbedModules = {
|
||||
parent: ParentModule,
|
||||
child: ChildModule,
|
||||
};
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'parent'}]);
|
||||
router.navigateByUrl('/lazy/parent/child');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/lazy/parent/child');
|
||||
expect(fixture.nativeElement).toHaveText('parent[child]');
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'parent'}]);
|
||||
router.navigateByUrl('/lazy/parent/child');
|
||||
advance(fixture);
|
||||
expect(location.path()).toEqual('/lazy/parent/child');
|
||||
expect(fixture.nativeElement).toHaveText('parent[child]');
|
||||
|
||||
const pInj = fixture.debugElement.query(By.directive(Parent)).injector !;
|
||||
const cInj = fixture.debugElement.query(By.directive(Child)).injector !;
|
||||
const pInj = fixture.debugElement.query(By.directive(Parent)).injector !;
|
||||
const cInj = fixture.debugElement.query(By.directive(Child)).injector !;
|
||||
|
||||
expect(pInj.get('moduleName')).toEqual('parent');
|
||||
expect(pInj.get('fromParent')).toEqual('from parent');
|
||||
expect(pInj.get(Parent)).toBeAnInstanceOf(Parent);
|
||||
expect(pInj.get('fromChild', null)).toEqual(null);
|
||||
expect(pInj.get(Child, null)).toEqual(null);
|
||||
expect(pInj.get('moduleName')).toEqual('parent');
|
||||
expect(pInj.get('fromParent')).toEqual('from parent');
|
||||
expect(pInj.get(Parent)).toBeAnInstanceOf(Parent);
|
||||
expect(pInj.get('fromChild', null)).toEqual(null);
|
||||
expect(pInj.get(Child, null)).toEqual(null);
|
||||
|
||||
expect(cInj.get('moduleName')).toEqual('child');
|
||||
expect(cInj.get('fromParent')).toEqual('from parent');
|
||||
expect(cInj.get('fromChild')).toEqual('from child');
|
||||
expect(cInj.get(Parent)).toBeAnInstanceOf(Parent);
|
||||
expect(cInj.get(Child)).toBeAnInstanceOf(Child);
|
||||
// The child module can not shadow the parent component
|
||||
expect(cInj.get('shadow')).toEqual('from parent component');
|
||||
expect(cInj.get('moduleName')).toEqual('child');
|
||||
expect(cInj.get('fromParent')).toEqual('from parent');
|
||||
expect(cInj.get('fromChild')).toEqual('from child');
|
||||
expect(cInj.get(Parent)).toBeAnInstanceOf(Parent);
|
||||
expect(cInj.get(Child)).toBeAnInstanceOf(Child);
|
||||
// The child module can not shadow the parent component
|
||||
expect(cInj.get('shadow')).toEqual('from parent component');
|
||||
|
||||
const pmInj = pInj.get(NgModuleRef).injector;
|
||||
const cmInj = cInj.get(NgModuleRef).injector;
|
||||
const pmInj = pInj.get(NgModuleRef).injector;
|
||||
const cmInj = cInj.get(NgModuleRef).injector;
|
||||
|
||||
expect(pmInj.get('moduleName')).toEqual('parent');
|
||||
expect(cmInj.get('moduleName')).toEqual('child');
|
||||
expect(pmInj.get('moduleName')).toEqual('parent');
|
||||
expect(cmInj.get('moduleName')).toEqual('child');
|
||||
|
||||
expect(pmInj.get(Parent, '-')).toEqual('-');
|
||||
expect(cmInj.get(Parent, '-')).toEqual('-');
|
||||
expect(pmInj.get(Child, '-')).toEqual('-');
|
||||
expect(cmInj.get(Child, '-')).toEqual('-');
|
||||
})));
|
||||
expect(pmInj.get(Parent, '-')).toEqual('-');
|
||||
expect(cmInj.get(Parent, '-')).toEqual('-');
|
||||
expect(pmInj.get(Child, '-')).toEqual('-');
|
||||
expect(cmInj.get(Child, '-')).toEqual('-');
|
||||
})));
|
||||
|
||||
// https://github.com/angular/angular/issues/12889
|
||||
it('should create a single instance of lazy-loaded modules',
|
||||
|
@ -3653,56 +3653,56 @@ describe('Integration', () => {
|
|||
|
||||
// https://github.com/angular/angular/issues/13870
|
||||
fixmeIvy(
|
||||
'FW-767: Lazy loaded modules are not used when resolving dependencies in one of their components') &&
|
||||
it('should create a single instance of guards for lazy-loaded modules',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
@Injectable()
|
||||
class Service {
|
||||
}
|
||||
'FW-767: Lazy loaded modules are not used when resolving dependencies in one of their components')
|
||||
.it('should create a single instance of guards for lazy-loaded modules',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
@Injectable()
|
||||
class Service {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class Resolver implements Resolve<Service> {
|
||||
constructor(public service: Service) {}
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
return this.service;
|
||||
}
|
||||
}
|
||||
@Injectable()
|
||||
class Resolver implements Resolve<Service> {
|
||||
constructor(public service: Service) {}
|
||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
return this.service;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'lazy', template: 'lazy'})
|
||||
class LazyLoadedComponent {
|
||||
resolvedService: Service;
|
||||
constructor(public injectedService: Service, route: ActivatedRoute) {
|
||||
this.resolvedService = route.snapshot.data['service'];
|
||||
}
|
||||
}
|
||||
@Component({selector: 'lazy', template: 'lazy'})
|
||||
class LazyLoadedComponent {
|
||||
resolvedService: Service;
|
||||
constructor(public injectedService: Service, route: ActivatedRoute) {
|
||||
this.resolvedService = route.snapshot.data['service'];
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
providers: [Service, Resolver],
|
||||
imports: [
|
||||
RouterModule.forChild([{
|
||||
path: 'loaded',
|
||||
component: LazyLoadedComponent,
|
||||
resolve: {'service': Resolver},
|
||||
}]),
|
||||
]
|
||||
})
|
||||
class LoadedModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
providers: [Service, Resolver],
|
||||
imports: [
|
||||
RouterModule.forChild([{
|
||||
path: 'loaded',
|
||||
component: LazyLoadedComponent,
|
||||
resolve: {'service': Resolver},
|
||||
}]),
|
||||
]
|
||||
})
|
||||
class LoadedModule {
|
||||
}
|
||||
|
||||
loader.stubbedModules = {expected: LoadedModule};
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);
|
||||
router.navigateByUrl('/lazy/loaded');
|
||||
advance(fixture);
|
||||
loader.stubbedModules = {expected: LoadedModule};
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
router.resetConfig([{path: 'lazy', loadChildren: 'expected'}]);
|
||||
router.navigateByUrl('/lazy/loaded');
|
||||
advance(fixture);
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('lazy');
|
||||
const lzc = fixture.debugElement.query(By.directive(LazyLoadedComponent))
|
||||
.componentInstance;
|
||||
expect(lzc.injectedService).toBe(lzc.resolvedService);
|
||||
})));
|
||||
expect(fixture.nativeElement).toHaveText('lazy');
|
||||
const lzc = fixture.debugElement.query(By.directive(LazyLoadedComponent))
|
||||
.componentInstance;
|
||||
expect(lzc.injectedService).toBe(lzc.resolvedService);
|
||||
})));
|
||||
|
||||
|
||||
it('should emit RouteConfigLoadStart and RouteConfigLoadEnd event when route is lazy loaded',
|
||||
|
@ -3954,27 +3954,27 @@ describe('Integration', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-767: Lazy loaded modules are not used when resolving dependencies in one of their components') &&
|
||||
it('should use the injector of the lazily-loaded configuration',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
loader.stubbedModules = {expected: LoadedModule};
|
||||
'FW-767: Lazy loaded modules are not used when resolving dependencies in one of their components')
|
||||
.it('should use the injector of the lazily-loaded configuration',
|
||||
fakeAsync(inject(
|
||||
[Router, Location, NgModuleFactoryLoader],
|
||||
(router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => {
|
||||
loader.stubbedModules = {expected: LoadedModule};
|
||||
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
||||
router.resetConfig([{
|
||||
path: 'eager-parent',
|
||||
component: EagerParentComponent,
|
||||
children: [{path: 'lazy', loadChildren: 'expected'}]
|
||||
}]);
|
||||
router.resetConfig([{
|
||||
path: 'eager-parent',
|
||||
component: EagerParentComponent,
|
||||
children: [{path: 'lazy', loadChildren: 'expected'}]
|
||||
}]);
|
||||
|
||||
router.navigateByUrl('/eager-parent/lazy/lazy-parent/lazy-child');
|
||||
advance(fixture);
|
||||
router.navigateByUrl('/eager-parent/lazy/lazy-parent/lazy-child');
|
||||
advance(fixture);
|
||||
|
||||
expect(location.path()).toEqual('/eager-parent/lazy/lazy-parent/lazy-child');
|
||||
expect(fixture.nativeElement).toHaveText('eager-parent lazy-parent lazy-child');
|
||||
})));
|
||||
expect(location.path()).toEqual('/eager-parent/lazy/lazy-parent/lazy-child');
|
||||
expect(fixture.nativeElement).toHaveText('eager-parent lazy-parent lazy-child');
|
||||
})));
|
||||
});
|
||||
|
||||
it('works when given a callback',
|
||||
|
@ -4439,82 +4439,82 @@ describe('Integration', () => {
|
|||
expect(simpleCmp1).not.toBe(simpleCmp2);
|
||||
})));
|
||||
|
||||
fixmeIvy('FW-768: markViewDirty instruction is scheduling a tick') &&
|
||||
it('should not mount the component of the previously reused route when the outlet was not instantiated at the time of route activation',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'root-cmp',
|
||||
template:
|
||||
'<div *ngIf="isToolpanelShowing"><router-outlet name="toolpanel"></router-outlet></div>'
|
||||
})
|
||||
class RootCmpWithCondOutlet implements OnDestroy {
|
||||
private subscription: Subscription;
|
||||
public isToolpanelShowing: boolean = false;
|
||||
fixmeIvy('FW-768: markViewDirty instruction is scheduling a tick')
|
||||
.it('should not mount the component of the previously reused route when the outlet was not instantiated at the time of route activation',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'root-cmp',
|
||||
template:
|
||||
'<div *ngIf="isToolpanelShowing"><router-outlet name="toolpanel"></router-outlet></div>'
|
||||
})
|
||||
class RootCmpWithCondOutlet implements OnDestroy {
|
||||
private subscription: Subscription;
|
||||
public isToolpanelShowing: boolean = false;
|
||||
|
||||
constructor(router: Router) {
|
||||
this.subscription =
|
||||
router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(
|
||||
() => this.isToolpanelShowing =
|
||||
!!router.parseUrl(router.url).root.children['toolpanel']);
|
||||
}
|
||||
constructor(router: Router) {
|
||||
this.subscription =
|
||||
router.events.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe(
|
||||
() => this.isToolpanelShowing =
|
||||
!!router.parseUrl(router.url).root.children['toolpanel']);
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||
}
|
||||
public ngOnDestroy(): void { this.subscription.unsubscribe(); }
|
||||
}
|
||||
|
||||
@Component({selector: 'tool-1-cmp', template: 'Tool 1 showing'})
|
||||
class Tool1Component {
|
||||
}
|
||||
@Component({selector: 'tool-1-cmp', template: 'Tool 1 showing'})
|
||||
class Tool1Component {
|
||||
}
|
||||
|
||||
@Component({selector: 'tool-2-cmp', template: 'Tool 2 showing'})
|
||||
class Tool2Component {
|
||||
}
|
||||
@Component({selector: 'tool-2-cmp', template: 'Tool 2 showing'})
|
||||
class Tool2Component {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [RootCmpWithCondOutlet, Tool1Component, Tool2Component],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterTestingModule.withRoutes([
|
||||
{path: 'a', outlet: 'toolpanel', component: Tool1Component},
|
||||
{path: 'b', outlet: 'toolpanel', component: Tool2Component},
|
||||
]),
|
||||
],
|
||||
})
|
||||
class TestModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [RootCmpWithCondOutlet, Tool1Component, Tool2Component],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterTestingModule.withRoutes([
|
||||
{path: 'a', outlet: 'toolpanel', component: Tool1Component},
|
||||
{path: 'b', outlet: 'toolpanel', component: Tool2Component},
|
||||
]),
|
||||
],
|
||||
})
|
||||
class TestModule {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({imports: [TestModule]});
|
||||
TestBed.configureTestingModule({imports: [TestModule]});
|
||||
|
||||
const router: Router = TestBed.get(Router);
|
||||
router.routeReuseStrategy = new AttachDetachReuseStrategy();
|
||||
const router: Router = TestBed.get(Router);
|
||||
router.routeReuseStrategy = new AttachDetachReuseStrategy();
|
||||
|
||||
const fixture = createRoot(router, RootCmpWithCondOutlet);
|
||||
const fixture = createRoot(router, RootCmpWithCondOutlet);
|
||||
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(a)');
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(a)');
|
||||
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(b)');
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(b)');
|
||||
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(c)');
|
||||
// Activate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: 'a'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool1Component, '(c)');
|
||||
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(d)');
|
||||
// Deactivate 'tool-1'
|
||||
router.navigate([{outlets: {toolpanel: null}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).not.toContainComponent(Tool1Component, '(d)');
|
||||
|
||||
// Activate 'tool-2'
|
||||
router.navigate([{outlets: {toolpanel: 'b'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool2Component, '(e)');
|
||||
}));
|
||||
// Activate 'tool-2'
|
||||
router.navigate([{outlets: {toolpanel: 'b'}}]);
|
||||
advance(fixture);
|
||||
expect(fixture).toContainComponent(Tool2Component, '(e)');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -62,64 +62,64 @@ describe('RouterPreloader', () => {
|
|||
|
||||
|
||||
fixmeIvy(
|
||||
'FW-765: NgModuleRef hierarchy is differently constructed when the router preloads modules') &&
|
||||
it('should work',
|
||||
fakeAsync(inject(
|
||||
[NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef],
|
||||
(loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router,
|
||||
testModule: NgModuleRef<any>) => {
|
||||
const events: Array<RouteConfigLoadStart|RouteConfigLoadEnd> = [];
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedCmp],
|
||||
imports: [RouterModule.forChild(
|
||||
[{path: 'LoadedModule2', component: LazyLoadedCmp}])]
|
||||
})
|
||||
class LoadedModule2 {
|
||||
}
|
||||
'FW-765: NgModuleRef hierarchy is differently constructed when the router preloads modules')
|
||||
.it('should work',
|
||||
fakeAsync(inject(
|
||||
[NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef],
|
||||
(loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router,
|
||||
testModule: NgModuleRef<any>) => {
|
||||
const events: Array<RouteConfigLoadStart|RouteConfigLoadEnd> = [];
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedCmp],
|
||||
imports: [RouterModule.forChild(
|
||||
[{path: 'LoadedModule2', component: LazyLoadedCmp}])]
|
||||
})
|
||||
class LoadedModule2 {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(
|
||||
[{path: 'LoadedModule1', loadChildren: 'expected2'}])]
|
||||
})
|
||||
class LoadedModule1 {
|
||||
}
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(
|
||||
[{path: 'LoadedModule1', loadChildren: 'expected2'}])]
|
||||
})
|
||||
class LoadedModule1 {
|
||||
}
|
||||
|
||||
router.events.subscribe(e => {
|
||||
if (e instanceof RouteConfigLoadEnd || e instanceof RouteConfigLoadStart) {
|
||||
events.push(e);
|
||||
}
|
||||
});
|
||||
router.events.subscribe(e => {
|
||||
if (e instanceof RouteConfigLoadEnd || e instanceof RouteConfigLoadStart) {
|
||||
events.push(e);
|
||||
}
|
||||
});
|
||||
|
||||
loader.stubbedModules = {
|
||||
expected: LoadedModule1,
|
||||
expected2: LoadedModule2,
|
||||
};
|
||||
loader.stubbedModules = {
|
||||
expected: LoadedModule1,
|
||||
expected2: LoadedModule2,
|
||||
};
|
||||
|
||||
preloader.preload().subscribe(() => {});
|
||||
preloader.preload().subscribe(() => {});
|
||||
|
||||
tick();
|
||||
tick();
|
||||
|
||||
const c = router.config;
|
||||
expect(c[0].loadChildren).toEqual('expected');
|
||||
const c = router.config;
|
||||
expect(c[0].loadChildren).toEqual('expected');
|
||||
|
||||
const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig !;
|
||||
const module: any = loadedConfig.module;
|
||||
expect(loadedConfig.routes[0].path).toEqual('LoadedModule1');
|
||||
expect(module._parent).toBe(testModule);
|
||||
const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig !;
|
||||
const module: any = loadedConfig.module;
|
||||
expect(loadedConfig.routes[0].path).toEqual('LoadedModule1');
|
||||
expect(module._parent).toBe(testModule);
|
||||
|
||||
const loadedConfig2: LoadedRouterConfig =
|
||||
(loadedConfig.routes[0] as any)._loadedConfig !;
|
||||
const module2: any = loadedConfig2.module;
|
||||
expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2');
|
||||
expect(module2._parent).toBe(module);
|
||||
const loadedConfig2: LoadedRouterConfig =
|
||||
(loadedConfig.routes[0] as any)._loadedConfig !;
|
||||
const module2: any = loadedConfig2.module;
|
||||
expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2');
|
||||
expect(module2._parent).toBe(module);
|
||||
|
||||
expect(events.map(e => e.toString())).toEqual([
|
||||
'RouteConfigLoadStart(path: lazy)',
|
||||
'RouteConfigLoadEnd(path: lazy)',
|
||||
'RouteConfigLoadStart(path: LoadedModule1)',
|
||||
'RouteConfigLoadEnd(path: LoadedModule1)',
|
||||
]);
|
||||
})));
|
||||
expect(events.map(e => e.toString())).toEqual([
|
||||
'RouteConfigLoadStart(path: lazy)',
|
||||
'RouteConfigLoadEnd(path: lazy)',
|
||||
'RouteConfigLoadStart(path: LoadedModule1)',
|
||||
'RouteConfigLoadEnd(path: LoadedModule1)',
|
||||
]);
|
||||
})));
|
||||
});
|
||||
|
||||
describe('should support modules that have already been loaded', () => {
|
||||
|
@ -131,59 +131,59 @@ describe('RouterPreloader', () => {
|
|||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-765: NgModuleRef hierarchy is differently constructed when the router preloads modules') &&
|
||||
it('should work',
|
||||
fakeAsync(inject(
|
||||
[NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef, Compiler],
|
||||
(loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router,
|
||||
testModule: NgModuleRef<any>, compiler: Compiler) => {
|
||||
@NgModule()
|
||||
class LoadedModule2 {
|
||||
}
|
||||
'FW-765: NgModuleRef hierarchy is differently constructed when the router preloads modules')
|
||||
.it('should work',
|
||||
fakeAsync(inject(
|
||||
[NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef, Compiler],
|
||||
(loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router,
|
||||
testModule: NgModuleRef<any>, compiler: Compiler) => {
|
||||
@NgModule()
|
||||
class LoadedModule2 {
|
||||
}
|
||||
|
||||
const module2 = compiler.compileModuleSync(LoadedModule2).create(null);
|
||||
const module2 = compiler.compileModuleSync(LoadedModule2).create(null);
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([
|
||||
<Route>{
|
||||
path: 'LoadedModule2',
|
||||
loadChildren: 'no',
|
||||
_loadedConfig: {
|
||||
routes: [{path: 'LoadedModule3', loadChildren: 'expected3'}],
|
||||
module: module2,
|
||||
}
|
||||
},
|
||||
])]
|
||||
})
|
||||
class LoadedModule1 {
|
||||
}
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([
|
||||
<Route>{
|
||||
path: 'LoadedModule2',
|
||||
loadChildren: 'no',
|
||||
_loadedConfig: {
|
||||
routes: [{path: 'LoadedModule3', loadChildren: 'expected3'}],
|
||||
module: module2,
|
||||
}
|
||||
},
|
||||
])]
|
||||
})
|
||||
class LoadedModule1 {
|
||||
}
|
||||
|
||||
@NgModule({imports: [RouterModule.forChild([])]})
|
||||
class LoadedModule3 {
|
||||
}
|
||||
@NgModule({imports: [RouterModule.forChild([])]})
|
||||
class LoadedModule3 {
|
||||
}
|
||||
|
||||
loader.stubbedModules = {
|
||||
expected: LoadedModule1,
|
||||
expected3: LoadedModule3,
|
||||
};
|
||||
loader.stubbedModules = {
|
||||
expected: LoadedModule1,
|
||||
expected3: LoadedModule3,
|
||||
};
|
||||
|
||||
preloader.preload().subscribe(() => {});
|
||||
preloader.preload().subscribe(() => {});
|
||||
|
||||
tick();
|
||||
tick();
|
||||
|
||||
const c = router.config;
|
||||
const c = router.config;
|
||||
|
||||
const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig !;
|
||||
const module: any = loadedConfig.module;
|
||||
expect(module._parent).toBe(testModule);
|
||||
const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig !;
|
||||
const module: any = loadedConfig.module;
|
||||
expect(module._parent).toBe(testModule);
|
||||
|
||||
const loadedConfig2: LoadedRouterConfig =
|
||||
(loadedConfig.routes[0] as any)._loadedConfig !;
|
||||
const loadedConfig3: LoadedRouterConfig =
|
||||
(loadedConfig2.routes[0] as any)._loadedConfig !;
|
||||
const module3: any = loadedConfig3.module;
|
||||
expect(module3._parent).toBe(module2);
|
||||
})));
|
||||
const loadedConfig2: LoadedRouterConfig =
|
||||
(loadedConfig.routes[0] as any)._loadedConfig !;
|
||||
const loadedConfig3: LoadedRouterConfig =
|
||||
(loadedConfig2.routes[0] as any)._loadedConfig !;
|
||||
const module3: any = loadedConfig3.module;
|
||||
expect(module3._parent).toBe(module2);
|
||||
})));
|
||||
});
|
||||
|
||||
describe('should ignore errors', () => {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -77,59 +77,59 @@ withEachNg1Version(() => {
|
|||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-712: Rendering is being run on next "animation frame" rather than "Zone.microTaskEmpty" trigger') &&
|
||||
it('should propagate changes to a downgraded component inside the ngZone', async(() => {
|
||||
const element = html('<my-app></my-app>');
|
||||
let appComponent: AppComponent;
|
||||
'FW-712: Rendering is being run on next "animation frame" rather than "Zone.microTaskEmpty" trigger')
|
||||
.it('should propagate changes to a downgraded component inside the ngZone', async(() => {
|
||||
const element = html('<my-app></my-app>');
|
||||
let appComponent: AppComponent;
|
||||
|
||||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
value?: number;
|
||||
constructor() { appComponent = this; }
|
||||
}
|
||||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
value?: number;
|
||||
constructor() { appComponent = this; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: '<div>{{ valueFromPromise }}</div>',
|
||||
})
|
||||
class ChildComponent {
|
||||
valueFromPromise?: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: '<div>{{ valueFromPromise }}</div>',
|
||||
})
|
||||
class ChildComponent {
|
||||
valueFromPromise?: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
|
||||
constructor(private zone: NgZone) {}
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['value'].isFirstChange()) return;
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['value'].isFirstChange()) return;
|
||||
|
||||
// HACK(ivy): Using setTimeout allows this test to pass but hides the ivy renderer
|
||||
// timing BC.
|
||||
// setTimeout(() => expect(element.textContent).toEqual('5'), 0);
|
||||
this.zone.onMicrotaskEmpty.subscribe(
|
||||
() => { expect(element.textContent).toEqual('5'); });
|
||||
// HACK(ivy): Using setTimeout allows this test to pass but hides the ivy renderer
|
||||
// timing BC.
|
||||
// setTimeout(() => expect(element.textContent).toEqual('5'), 0);
|
||||
this.zone.onMicrotaskEmpty.subscribe(
|
||||
() => { expect(element.textContent).toEqual('5'); });
|
||||
|
||||
// Create a micro-task to update the value to be rendered asynchronously.
|
||||
Promise.resolve().then(
|
||||
() => this.valueFromPromise = changes['value'].currentValue);
|
||||
}
|
||||
}
|
||||
// Create a micro-task to update the value to be rendered asynchronously.
|
||||
Promise.resolve().then(
|
||||
() => this.valueFromPromise = changes['value'].currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, ChildComponent],
|
||||
entryComponents: [AppComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [AppComponent, ChildComponent],
|
||||
entryComponents: [AppComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'myApp', downgradeComponent({component: AppComponent}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'myApp', downgradeComponent({component: AppComponent}));
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
appComponent.value = 5;
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
appComponent.value = 5;
|
||||
});
|
||||
}));
|
||||
|
||||
// This test demonstrates https://github.com/angular/angular/issues/6385
|
||||
// which was invalidly fixed by https://github.com/angular/angular/pull/6386
|
||||
|
|
|
@ -22,82 +22,82 @@ withEachNg1Version(() => {
|
|||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should instantiate ng2 in ng1 template and project content', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should instantiate ng2 in ng1 template and project content', async(() => {
|
||||
|
||||
// the ng2 component that will be used in ng1 (downgraded)
|
||||
@Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`})
|
||||
class Ng2Component {
|
||||
prop = 'NG2';
|
||||
ngContent = 'ng2-content';
|
||||
}
|
||||
// the ng2 component that will be used in ng1 (downgraded)
|
||||
@Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`})
|
||||
class Ng2Component {
|
||||
prop = 'NG2';
|
||||
ngContent = 'ng2-content';
|
||||
}
|
||||
|
||||
// our upgrade module to host the component to downgrade
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// our upgrade module to host the component to downgrade
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// the ng1 app module that will consume the downgraded component
|
||||
const ng1Module = angular
|
||||
.module('ng1', [])
|
||||
// create an ng1 facade of the ng2 component
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['prop'] = 'NG1';
|
||||
$rootScope['ngContent'] = 'ng1-content';
|
||||
});
|
||||
// the ng1 app module that will consume the downgraded component
|
||||
const ng1Module = angular
|
||||
.module('ng1', [])
|
||||
// create an ng1 facade of the ng2 component
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['prop'] = 'NG1';
|
||||
$rootScope['ngContent'] = 'ng1-content';
|
||||
});
|
||||
|
||||
const element =
|
||||
html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>');
|
||||
const element =
|
||||
html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]');
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should correctly project structural directives', async(() => {
|
||||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
}
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should correctly project structural directives', async(() => {
|
||||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['items'] = [
|
||||
{id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]},
|
||||
{id: 'c', subitems: [7, 8, 9]}
|
||||
];
|
||||
});
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['items'] = [
|
||||
{id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]},
|
||||
{id: 'c', subitems: [7, 8, 9]}
|
||||
];
|
||||
});
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<ng2 ng-repeat="item in items" [item-id]="item.id">
|
||||
<div ng-repeat="subitem in item.subitems">{{ subitem }}</div>
|
||||
</ng2>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should instantiate ng1 in ng2 template and project content', async(() => {
|
||||
|
||||
|
@ -145,39 +145,39 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should support multi-slot projection', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should support multi-slot projection', async(() => {
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
||||
'2b(<ng-content select=".ng1b"></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() {}
|
||||
}
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
||||
'2b(<ng-content select=".ng1b"></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// The ng-if on one of the projected children is here to make sure
|
||||
// the correct slot is targeted even with structural directives in play.
|
||||
const element = html(
|
||||
'<ng2><div ng-if="true" class="ng1a">1a</div><div' +
|
||||
' class="ng1b">1b</div></ng2>');
|
||||
// The ng-if on one of the projected children is here to make sure
|
||||
// the correct slot is targeted even with structural directives in play.
|
||||
const element = html(
|
||||
'<ng2><div ng-if="true" class="ng1a">1a</div><div' +
|
||||
' class="ng1b">1b</div></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,106 +22,106 @@ withEachNg1Version(() => {
|
|||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
fixmeIvy('FW-716: Error: [$rootScope:inprog] $digest already in progress') &&
|
||||
it('should bind properties, events', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['name'] = 'world';
|
||||
$rootScope['dataA'] = 'A';
|
||||
$rootScope['dataB'] = 'B';
|
||||
$rootScope['modelA'] = 'initModelA';
|
||||
$rootScope['modelB'] = 'initModelB';
|
||||
$rootScope['eventA'] = '?';
|
||||
$rootScope['eventB'] = '?';
|
||||
});
|
||||
fixmeIvy('FW-716: Error: [$rootScope:inprog] $digest already in progress')
|
||||
.it('should bind properties, events', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['name'] = 'world';
|
||||
$rootScope['dataA'] = 'A';
|
||||
$rootScope['dataB'] = 'B';
|
||||
$rootScope['modelA'] = 'initModelA';
|
||||
$rootScope['modelB'] = 'initModelB';
|
||||
$rootScope['eventA'] = '?';
|
||||
$rootScope['eventB'] = '?';
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||
outputs: [
|
||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
|
||||
'twoWayBEmitter: twoWayBChange'
|
||||
],
|
||||
template: 'ignore: {{ignore}}; ' +
|
||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
|
||||
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
ignore = '-';
|
||||
literal = '?';
|
||||
interpolate = '?';
|
||||
oneWayA = '?';
|
||||
oneWayB = '?';
|
||||
twoWayA = '?';
|
||||
twoWayB = '?';
|
||||
eventA = new EventEmitter();
|
||||
eventB = new EventEmitter();
|
||||
twoWayAEmitter = new EventEmitter();
|
||||
twoWayBEmitter = new EventEmitter();
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||
outputs: [
|
||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
|
||||
'twoWayBEmitter: twoWayBChange'
|
||||
],
|
||||
template: 'ignore: {{ignore}}; ' +
|
||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
|
||||
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
ignore = '-';
|
||||
literal = '?';
|
||||
interpolate = '?';
|
||||
oneWayA = '?';
|
||||
oneWayB = '?';
|
||||
twoWayA = '?';
|
||||
twoWayB = '?';
|
||||
eventA = new EventEmitter();
|
||||
eventB = new EventEmitter();
|
||||
twoWayAEmitter = new EventEmitter();
|
||||
twoWayBEmitter = new EventEmitter();
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const assert = (prop: string, value: any) => {
|
||||
const propVal = (this as any)[prop];
|
||||
if (propVal != value) {
|
||||
throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`);
|
||||
}
|
||||
};
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const assert = (prop: string, value: any) => {
|
||||
const propVal = (this as any)[prop];
|
||||
if (propVal != value) {
|
||||
throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`);
|
||||
}
|
||||
};
|
||||
|
||||
const assertChange = (prop: string, value: any) => {
|
||||
assert(prop, value);
|
||||
if (!changes[prop]) {
|
||||
throw new Error(`Changes record for '${prop}' not found.`);
|
||||
}
|
||||
const actualValue = changes[prop].currentValue;
|
||||
if (actualValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`);
|
||||
}
|
||||
};
|
||||
const assertChange = (prop: string, value: any) => {
|
||||
assert(prop, value);
|
||||
if (!changes[prop]) {
|
||||
throw new Error(`Changes record for '${prop}' not found.`);
|
||||
}
|
||||
const actualValue = changes[prop].currentValue;
|
||||
if (actualValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`);
|
||||
}
|
||||
};
|
||||
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
assert('ignore', '-');
|
||||
assertChange('literal', 'Text');
|
||||
assertChange('interpolate', 'Hello world');
|
||||
assertChange('oneWayA', 'A');
|
||||
assertChange('oneWayB', 'B');
|
||||
assertChange('twoWayA', 'initModelA');
|
||||
assertChange('twoWayB', 'initModelB');
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
assert('ignore', '-');
|
||||
assertChange('literal', 'Text');
|
||||
assertChange('interpolate', 'Hello world');
|
||||
assertChange('oneWayA', 'A');
|
||||
assertChange('oneWayB', 'B');
|
||||
assertChange('twoWayA', 'initModelA');
|
||||
assertChange('twoWayB', 'initModelB');
|
||||
|
||||
this.twoWayAEmitter.emit('newA');
|
||||
this.twoWayBEmitter.emit('newB');
|
||||
this.eventA.emit('aFired');
|
||||
this.eventB.emit('bFired');
|
||||
break;
|
||||
case 1:
|
||||
assertChange('twoWayA', 'newA');
|
||||
assertChange('twoWayB', 'newB');
|
||||
break;
|
||||
case 2:
|
||||
assertChange('interpolate', 'Hello everyone');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.twoWayAEmitter.emit('newA');
|
||||
this.twoWayBEmitter.emit('newB');
|
||||
this.eventA.emit('aFired');
|
||||
this.eventB.emit('bFired');
|
||||
break;
|
||||
case 1:
|
||||
assertChange('twoWayA', 'newA');
|
||||
assertChange('twoWayB', 'newB');
|
||||
break;
|
||||
case 2:
|
||||
assertChange('interpolate', 'Hello everyone');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ng1Module.directive('ng2', downgradeComponent({
|
||||
component: Ng2Component,
|
||||
}));
|
||||
ng1Module.directive('ng2', downgradeComponent({
|
||||
component: Ng2Component,
|
||||
}));
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<div>
|
||||
<ng2 literal="Text" interpolate="Hello {{name}}"
|
||||
bind-one-way-a="dataA" [one-way-b]="dataB"
|
||||
|
@ -130,23 +130,23 @@ withEachNg1Version(() => {
|
|||
| modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}};
|
||||
</div>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
|
||||
$apply(upgrade, 'name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
});
|
||||
}));
|
||||
$apply(upgrade, 'name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind properties to onpush components', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(
|
||||
|
@ -189,58 +189,58 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should support two-way binding and event listener', async(() => {
|
||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['value'] = 'world';
|
||||
$rootScope['listener'] = listenerSpy;
|
||||
});
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
||||
.it('should support two-way binding and event listener', async(() => {
|
||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['value'] = 'world';
|
||||
$rootScope['listener'] = listenerSpy;
|
||||
});
|
||||
|
||||
@Component({selector: 'ng2', template: `model: {{model}};`})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
@Input() model = '?';
|
||||
@Output() modelChange = new EventEmitter();
|
||||
@Component({selector: 'ng2', template: `model: {{model}};`})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
@Input() model = '?';
|
||||
@Output() modelChange = new EventEmitter();
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
expect(changes.model.currentValue).toBe('world');
|
||||
this.modelChange.emit('newC');
|
||||
break;
|
||||
case 1:
|
||||
expect(changes.model.currentValue).toBe('newC');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
expect(changes.model.currentValue).toBe('world');
|
||||
this.modelChange.emit('newC');
|
||||
break;
|
||||
case 1:
|
||||
expect(changes.model.currentValue).toBe('newC');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ng1Module.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
ng1Module.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<div>
|
||||
<ng2 [(model)]="value" (model-change)="listener($event)"></ng2>
|
||||
| value: {{value}}
|
||||
</div>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC');
|
||||
expect(listenerSpy).toHaveBeenCalledWith('newC');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC');
|
||||
expect(listenerSpy).toHaveBeenCalledWith('newC');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should run change-detection on every digest (by default)', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
@ -404,66 +404,66 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
||||
.it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
ngOnChangesCount: {{ ngOnChangesCount }} |
|
||||
firstChangesCount: {{ firstChangesCount }} |
|
||||
initialValue: {{ initialValue }}`
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
|
||||
if (this.ngOnChangesCount === 1) {
|
||||
this.initialValue = this.foo;
|
||||
}
|
||||
if (this.ngOnChangesCount === 1) {
|
||||
this.initialValue = this.foo;
|
||||
}
|
||||
|
||||
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
||||
this.firstChangesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
||||
this.firstChangesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<ng2 [foo]="'foo'"></ng2>
|
||||
<ng2 foo="bar"></ng2>
|
||||
<ng2 [foo]="'baz'" ng-if="true"></ng2>
|
||||
<ng2 foo="qux" ng-if="true"></ng2>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const nodes = element.querySelectorAll('ng2');
|
||||
const expectedTextWith = (value: string) =>
|
||||
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const nodes = element.querySelectorAll('ng2');
|
||||
const expectedTextWith = (value: string) =>
|
||||
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
||||
|
||||
expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo'));
|
||||
expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar'));
|
||||
expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz'));
|
||||
expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux'));
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo'));
|
||||
expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar'));
|
||||
expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz'));
|
||||
expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux'));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind to ng-model', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(
|
||||
|
@ -709,88 +709,88 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should respect hierarchical dependency injection for ng2', async(() => {
|
||||
@Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'})
|
||||
class ParentComponent {
|
||||
}
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should respect hierarchical dependency injection for ng2', async(() => {
|
||||
@Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'})
|
||||
class ParentComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'child', template: 'child'})
|
||||
class ChildComponent {
|
||||
constructor(parent: ParentComponent) {}
|
||||
}
|
||||
@Component({selector: 'child', template: 'child'})
|
||||
class ChildComponent {
|
||||
constructor(parent: ParentComponent) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParentComponent, ChildComponent],
|
||||
entryComponents: [ParentComponent, ChildComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [ParentComponent, ChildComponent],
|
||||
entryComponents: [ParentComponent, ChildComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('parent', downgradeComponent({component: ParentComponent}))
|
||||
.directive('child', downgradeComponent({component: ChildComponent}));
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('parent', downgradeComponent({component: ParentComponent}))
|
||||
.directive('child', downgradeComponent({component: ChildComponent}));
|
||||
|
||||
const element = html('<parent><child></child></parent>');
|
||||
const element = html('<parent><child></child></parent>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent)).toBe('parent(child)');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent)).toBe('parent(child)');
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-717: Injector on lazy loaded components are not the same as their NgModule\'s injector') &&
|
||||
it('should work with ng2 lazy loaded components', async(() => {
|
||||
let componentInjector: Injector;
|
||||
'FW-717: Injector on lazy loaded components are not the same as their NgModule\'s injector')
|
||||
.it('should work with ng2 lazy loaded components', async(() => {
|
||||
let componentInjector: Injector;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(injector: Injector) { componentInjector = injector; }
|
||||
}
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(injector: Injector) { componentInjector = injector; }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
@Component({template: ''})
|
||||
class LazyLoadedComponent {
|
||||
constructor(public module: NgModuleRef<any>) {}
|
||||
}
|
||||
@Component({template: ''})
|
||||
class LazyLoadedComponent {
|
||||
constructor(public module: NgModuleRef<any>) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
entryComponents: [LazyLoadedComponent],
|
||||
})
|
||||
class LazyLoadedModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
entryComponents: [LazyLoadedComponent],
|
||||
})
|
||||
class LazyLoadedModule {
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
const element = html('<ng2></ng2>');
|
||||
const element = html('<ng2></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const modInjector = upgrade.injector;
|
||||
// Emulate the router lazy loading a module and creating a component
|
||||
const compiler = modInjector.get(Compiler);
|
||||
const modFactory = compiler.compileModuleSync(LazyLoadedModule);
|
||||
const childMod = modFactory.create(modInjector);
|
||||
const cmpFactory =
|
||||
childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent) !;
|
||||
const lazyCmp = cmpFactory.create(componentInjector);
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const modInjector = upgrade.injector;
|
||||
// Emulate the router lazy loading a module and creating a component
|
||||
const compiler = modInjector.get(Compiler);
|
||||
const modFactory = compiler.compileModuleSync(LazyLoadedModule);
|
||||
const childMod = modFactory.create(modInjector);
|
||||
const cmpFactory = childMod.componentFactoryResolver.resolveComponentFactory(
|
||||
LazyLoadedComponent) !;
|
||||
const lazyCmp = cmpFactory.create(componentInjector);
|
||||
|
||||
expect(lazyCmp.instance.module.injector === childMod.injector).toBe(true);
|
||||
});
|
||||
expect(lazyCmp.instance.module.injector === childMod.injector).toBe(true);
|
||||
});
|
||||
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should throw if `downgradedModule` is specified', async(() => {
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
|
|
|
@ -132,64 +132,64 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-718: upgraded service not being initialized correctly on the injector') &&
|
||||
it('should support using an upgraded service', async(() => {
|
||||
class Ng2Service {
|
||||
constructor(@Inject('ng1Value') private ng1Value: string) {}
|
||||
getValue = () => `${this.ng1Value}-bar`;
|
||||
}
|
||||
fixmeIvy('FW-718: upgraded service not being initialized correctly on the injector')
|
||||
.it('should support using an upgraded service', async(() => {
|
||||
class Ng2Service {
|
||||
constructor(@Inject('ng1Value') private ng1Value: string) {}
|
||||
getValue = () => `${this.ng1Value}-bar`;
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2', template: '{{ value }}'})
|
||||
class Ng2Component {
|
||||
value: string;
|
||||
constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); }
|
||||
}
|
||||
@Component({selector: 'ng2', template: '{{ value }}'})
|
||||
class Ng2Component {
|
||||
value: string;
|
||||
constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
providers: [
|
||||
Ng2Service,
|
||||
{
|
||||
provide: 'ng1Value',
|
||||
useFactory: (i: angular.IInjectorService) => i.get('ng1Value'),
|
||||
deps: ['$injector'],
|
||||
},
|
||||
],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
providers: [
|
||||
Ng2Service,
|
||||
{
|
||||
provide: 'ng1Value',
|
||||
useFactory: (i: angular.IInjectorService) => i.get('ng1Value'),
|
||||
deps: ['$injector'],
|
||||
},
|
||||
],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.value('ng1Value', 'foo');
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.value('ng1Value', 'foo');
|
||||
|
||||
const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
|
||||
$rootScope.$apply('loadNg2 = true');
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
$rootScope.$apply('loadNg2 = true');
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
expect(() => $injector.get(INJECTOR_KEY)).not.toThrow();
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
expect(() => $injector.get(INJECTOR_KEY)).not.toThrow();
|
||||
|
||||
// Wait for `$evalAsync()` to propagate inputs.
|
||||
setTimeout(() => expect(element.textContent).toBe('foo-bar'));
|
||||
});
|
||||
}));
|
||||
// Wait for `$evalAsync()` to propagate inputs.
|
||||
setTimeout(() => expect(element.textContent).toBe('foo-bar'));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create components inside the Angular zone', async(() => {
|
||||
@Component({selector: 'ng2', template: 'In the zone: {{ inTheZone }}'})
|
||||
|
@ -261,66 +261,66 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should propagate input changes inside the Angular zone', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
||||
.it('should propagate input changes inside the Angular zone', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnChanges {
|
||||
@Input() attrInput = 'foo';
|
||||
@Input() propInput = 'foo';
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnChanges {
|
||||
@Input() attrInput = 'foo';
|
||||
@Input() propInput = 'foo';
|
||||
|
||||
constructor() { ng2Component = this; }
|
||||
ngOnChanges() {}
|
||||
}
|
||||
constructor() { ng2Component = this; }
|
||||
ngOnChanges() {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope.attrVal = 'bar';
|
||||
$rootScope.propVal = 'bar';
|
||||
});
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope.attrVal = 'bar';
|
||||
$rootScope.propVal = 'bar';
|
||||
});
|
||||
|
||||
const element =
|
||||
html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element =
|
||||
html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true);
|
||||
const changesSpy =
|
||||
spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone);
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true);
|
||||
const changesSpy =
|
||||
spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone);
|
||||
|
||||
expect(ng2Component.attrInput).toBe('bar');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(ng2Component.attrInput).toBe('bar');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
|
||||
$rootScope.$apply('attrVal = "baz"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(1);
|
||||
$rootScope.$apply('attrVal = "baz"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
$rootScope.$apply('propVal = "qux"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('qux');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
}));
|
||||
$rootScope.$apply('propVal = "qux"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('qux');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should wire up the component for change detection', async(() => {
|
||||
@Component(
|
||||
|
@ -364,166 +364,168 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should run the lifecycle hooks in the correct order', async(() => {
|
||||
const logs: string[] = [];
|
||||
let rootScope: angular.IRootScopeService;
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly')
|
||||
.fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should run the lifecycle hooks in the correct order', async(() => {
|
||||
const logs: string[] = [];
|
||||
let rootScope: angular.IRootScopeService;
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
{{ value }}
|
||||
<button (click)="value = 'qux'"></button>
|
||||
<ng-content></ng-content>
|
||||
`
|
||||
})
|
||||
class Ng2Component implements AfterContentChecked,
|
||||
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy,
|
||||
OnInit {
|
||||
@Input() value = 'foo';
|
||||
})
|
||||
class Ng2Component implements AfterContentChecked,
|
||||
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges,
|
||||
OnDestroy, OnInit {
|
||||
@Input() value = 'foo';
|
||||
|
||||
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
||||
ngAfterContentInit() { this.log('AfterContentInit'); }
|
||||
ngAfterViewChecked() { this.log('AfterViewChecked'); }
|
||||
ngAfterViewInit() { this.log('AfterViewInit'); }
|
||||
ngDoCheck() { this.log('DoCheck'); }
|
||||
ngOnChanges() { this.log('OnChanges'); }
|
||||
ngOnDestroy() { this.log('OnDestroy'); }
|
||||
ngOnInit() { this.log('OnInit'); }
|
||||
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
||||
ngAfterContentInit() { this.log('AfterContentInit'); }
|
||||
ngAfterViewChecked() { this.log('AfterViewChecked'); }
|
||||
ngAfterViewInit() { this.log('AfterViewInit'); }
|
||||
ngDoCheck() { this.log('DoCheck'); }
|
||||
ngOnChanges() { this.log('OnChanges'); }
|
||||
ngOnDestroy() { this.log('OnDestroy'); }
|
||||
ngOnInit() { this.log('OnInit'); }
|
||||
|
||||
private log(hook: string) { logs.push(`${hook}(${this.value})`); }
|
||||
}
|
||||
private log(hook: string) { logs.push(`${hook}(${this.value})`); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
rootScope = $rootScope;
|
||||
rootScope.value = 'bar';
|
||||
});
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
rootScope = $rootScope;
|
||||
rootScope.value = 'bar';
|
||||
});
|
||||
|
||||
const element =
|
||||
html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>');
|
||||
angular.bootstrap(element, [ng1Module.name]);
|
||||
const element =
|
||||
html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>');
|
||||
angular.bootstrap(element, [ng1Module.name]);
|
||||
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const button = element.querySelector('button') !;
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const button = element.querySelector('button') !;
|
||||
|
||||
// Once initialized.
|
||||
expect(multiTrim(element.textContent)).toBe('bar Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(bar)',
|
||||
// Initial CD triggered directly through the `detectChanges()` or
|
||||
// `inputChanges`
|
||||
// $watcher (for `propagateDigest` true/false respectively).
|
||||
'OnInit(bar)',
|
||||
'DoCheck(bar)',
|
||||
'AfterContentInit(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewInit(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher (2nd
|
||||
// $digest).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
] :
|
||||
[]),
|
||||
// CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Once initialized.
|
||||
expect(multiTrim(element.textContent)).toBe('bar Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges`
|
||||
// $watcher.
|
||||
'OnChanges(bar)',
|
||||
// Initial CD triggered directly through the `detectChanges()` or
|
||||
// `inputChanges`
|
||||
// $watcher (for `propagateDigest` true/false respectively).
|
||||
'OnInit(bar)',
|
||||
'DoCheck(bar)',
|
||||
'AfterContentInit(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewInit(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher (2nd
|
||||
// $digest).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
] :
|
||||
[]),
|
||||
// CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Change inputs and run `$digest`.
|
||||
rootScope.$apply('value = "baz"');
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(baz)',
|
||||
// `propagateDigest: true` (3 CD runs):
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
// - CD triggered directly through the `detectChanges()` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `detectChanges`
|
||||
// $watcher).
|
||||
// `propagateDigest: false` (2 CD runs):
|
||||
// - CD triggered directly through the `inputChanges` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]),
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Change inputs and run `$digest`.
|
||||
rootScope.$apply('value = "baz"');
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges`
|
||||
// $watcher.
|
||||
'OnChanges(baz)',
|
||||
// `propagateDigest: true` (3 CD runs):
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
// - CD triggered directly through the `detectChanges()` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `detectChanges`
|
||||
// $watcher).
|
||||
// `propagateDigest: false` (2 CD runs):
|
||||
// - CD triggered directly through the `inputChanges` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]),
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Run `$digest` (without changing inputs).
|
||||
rootScope.$digest();
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual(
|
||||
propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher.
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
// CD triggered due to entering/leaving the NgZone (in the above
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]);
|
||||
logs.length = 0;
|
||||
// Run `$digest` (without changing inputs).
|
||||
rootScope.$digest();
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual(
|
||||
propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher.
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
// CD triggered due to entering/leaving the NgZone (in the above
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]);
|
||||
logs.length = 0;
|
||||
|
||||
// Trigger change detection (without changing inputs).
|
||||
button.click();
|
||||
expect(multiTrim(element.textContent)).toBe('qux Content');
|
||||
expect(logs).toEqual([
|
||||
'DoCheck(qux)',
|
||||
'AfterContentChecked(qux)',
|
||||
'AfterViewChecked(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Trigger change detection (without changing inputs).
|
||||
button.click();
|
||||
expect(multiTrim(element.textContent)).toBe('qux Content');
|
||||
expect(logs).toEqual([
|
||||
'DoCheck(qux)',
|
||||
'AfterContentChecked(qux)',
|
||||
'AfterViewChecked(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Destroy the component.
|
||||
rootScope.$apply('hideNg2 = true');
|
||||
expect(logs).toEqual([
|
||||
'OnDestroy(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
});
|
||||
});
|
||||
}));
|
||||
// Destroy the component.
|
||||
rootScope.$apply('hideNg2 = true');
|
||||
expect(logs).toEqual([
|
||||
'OnDestroy(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should detach hostViews from the ApplicationRef once destroyed', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
|
|
@ -24,70 +24,70 @@ withEachNg1Version(() => {
|
|||
|
||||
it('should have AngularJS loaded', () => expect(angular.version.major).toBe(1));
|
||||
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should verify UpgradeAdapter example', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered')
|
||||
.it('should verify UpgradeAdapter example', async(() => {
|
||||
|
||||
// This is wrapping (upgrading) an AngularJS component to be used in an Angular
|
||||
// component
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1Component extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() title !: string;
|
||||
// This is wrapping (upgrading) an AngularJS component to be used in an Angular
|
||||
// component
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1Component extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() title !: string;
|
||||
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// This is an Angular component that will be downgraded
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('name') nameProp !: string;
|
||||
}
|
||||
// This is an Angular component that will be downgraded
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('name') nameProp !: string;
|
||||
}
|
||||
|
||||
// This module represents the Angular pieces of the application
|
||||
@NgModule({
|
||||
declarations: [Ng1Component, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from
|
||||
complaining */
|
||||
}
|
||||
}
|
||||
// This module represents the Angular pieces of the application
|
||||
@NgModule({
|
||||
declarations: [Ng1Component, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from
|
||||
complaining */
|
||||
}
|
||||
}
|
||||
|
||||
// This module represents the AngularJS pieces of the application
|
||||
const ng1Module =
|
||||
angular
|
||||
.module('myExample', [])
|
||||
// This is an AngularJS component that will be upgraded
|
||||
.directive(
|
||||
'ng1',
|
||||
() => {
|
||||
return {
|
||||
scope: {title: '='},
|
||||
transclude: true,
|
||||
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
||||
};
|
||||
})
|
||||
// This is wrapping (downgrading) an Angular component to be used in
|
||||
// AngularJS
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// This module represents the AngularJS pieces of the application
|
||||
const ng1Module =
|
||||
angular
|
||||
.module('myExample', [])
|
||||
// This is an AngularJS component that will be upgraded
|
||||
.directive(
|
||||
'ng1',
|
||||
() => {
|
||||
return {
|
||||
scope: {title: '='},
|
||||
transclude: true,
|
||||
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
||||
};
|
||||
})
|
||||
// This is wrapping (downgrading) an Angular component to be used in
|
||||
// AngularJS
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// This is the (AngularJS) application bootstrap element
|
||||
// Notice that it is actually a downgraded Angular component
|
||||
const element = html('<ng2 name="World">project</ng2>');
|
||||
// This is the (AngularJS) application bootstrap element
|
||||
// Notice that it is actually a downgraded Angular component
|
||||
const element = html('<ng2 name="World">project</ng2>');
|
||||
|
||||
// Let's use a helper function to make this simpler
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(element.textContent))
|
||||
.toBe('ng2[ng1[Hello World!](transclude)](project)');
|
||||
});
|
||||
}));
|
||||
// Let's use a helper function to make this simpler
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(element.textContent))
|
||||
.toBe('ng2[ng1[Hello World!](transclude)](project)');
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2135,76 +2135,76 @@ withEachNg1Version(() => {
|
|||
|
||||
describe('transclusion', () => {
|
||||
fixmeIvy(
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) &&
|
||||
it('should support single-slot transclusion', async(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`)
|
||||
.it('should support single-slot transclusion', async(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: 'ng1(<div ng-transclude></div>)',
|
||||
transclude: true
|
||||
};
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: 'ng1(<div ng-transclude></div>)',
|
||||
transclude: true
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2A',
|
||||
template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)'
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2A',
|
||||
template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)'
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
|
||||
|
||||
ng2ComponentAInstance.value = 'baz';
|
||||
ng2ComponentAInstance.showB = true;
|
||||
$digest(adapter);
|
||||
ng2ComponentAInstance.value = 'baz';
|
||||
ng2ComponentAInstance.showB = true;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
|
||||
|
||||
ng2ComponentBInstance.value = 'qux';
|
||||
$digest(adapter);
|
||||
ng2ComponentBInstance.value = 'qux';
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support single-slot transclusion with fallback content', async(() => {
|
||||
let ng1ControllerInstances: any[] = [];
|
||||
|
@ -2532,29 +2532,29 @@ withEachNg1Version(() => {
|
|||
}));
|
||||
|
||||
fixmeIvy(
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) &&
|
||||
it('should support structural directives in transcluded content', async(() => {
|
||||
let ng2ComponentInstance: Ng2Component;
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`)
|
||||
.it('should support structural directives in transcluded content', async(() => {
|
||||
let ng2ComponentInstance: Ng2Component;
|
||||
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template:
|
||||
'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))',
|
||||
transclude: {slotX: 'contentX'}
|
||||
};
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template:
|
||||
'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))',
|
||||
transclude: {slotX: 'contentX'}
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
ng2(
|
||||
<ng1>
|
||||
<content-x><div *ngIf="show">{{ x }}1</div></content-x>
|
||||
|
@ -2563,53 +2563,53 @@ withEachNg1Version(() => {
|
|||
<div *ngIf="show">{{ y }}2</div>
|
||||
</ng1>
|
||||
)`
|
||||
})
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
}
|
||||
})
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html(`<ng2></ng2>`);
|
||||
// Bootstrap
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(foo1)|default(bar2)))');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(foo1)|default(bar2)))');
|
||||
|
||||
ng2ComponentInstance.x = 'baz';
|
||||
ng2ComponentInstance.y = 'qux';
|
||||
ng2ComponentInstance.show = false;
|
||||
$digest(adapter);
|
||||
ng2ComponentInstance.x = 'baz';
|
||||
ng2ComponentInstance.y = 'qux';
|
||||
ng2ComponentInstance.show = false;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz2)|default(qux1)))');
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz2)|default(qux1)))');
|
||||
|
||||
ng2ComponentInstance.show = true;
|
||||
$digest(adapter);
|
||||
ng2ComponentInstance.show = true;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz1)|default(qux2)))');
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz1)|default(qux2)))');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('lifecycle hooks', () => {
|
||||
|
@ -3336,103 +3336,105 @@ withEachNg1Version(() => {
|
|||
}));
|
||||
|
||||
|
||||
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
||||
it('should call `$onDestroy()` on controller', async(() => {
|
||||
const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA');
|
||||
const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB');
|
||||
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed')
|
||||
.it('should call `$onDestroy()` on controller', async(() => {
|
||||
const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA');
|
||||
const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB');
|
||||
|
||||
// Define `ng1Directive`
|
||||
const ng1DirectiveA: angular.IDirective = {
|
||||
template: 'ng1A',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { controllerOnDestroyA(); }}
|
||||
};
|
||||
// Define `ng1Directive`
|
||||
const ng1DirectiveA: angular.IDirective = {
|
||||
template: 'ng1A',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { controllerOnDestroyA(); }}
|
||||
};
|
||||
|
||||
const ng1DirectiveB: angular.IDirective = {
|
||||
template: 'ng1B',
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller:
|
||||
class {constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }}
|
||||
};
|
||||
const ng1DirectiveB: angular.IDirective = {
|
||||
template: 'ng1B',
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {
|
||||
constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }
|
||||
}
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1A'})
|
||||
class Ng1ComponentAFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1A', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1A'})
|
||||
class Ng1ComponentAFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1A', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'ng1B'})
|
||||
class Ng1ComponentBFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1B', elementRef, injector);
|
||||
}
|
||||
}
|
||||
@Directive({selector: 'ng1B'})
|
||||
class Ng1ComponentBFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1B', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() show !: boolean;
|
||||
}
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() show !: boolean;
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.directive('ng1A', () => ng1DirectiveA)
|
||||
.directive('ng1B', () => ng1DirectiveB)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.directive('ng1A', () => ng1DirectiveA)
|
||||
.directive('ng1B', () => ng1DirectiveB)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>');
|
||||
// Bootstrap
|
||||
const element =
|
||||
html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
const $rootScope =
|
||||
adapter.$injector.get('$rootScope') as angular.IRootScopeService;
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
const $rootScope =
|
||||
adapter.$injector.get('$rootScope') as angular.IRootScopeService;
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$apply('destroyFromNg1 = true');
|
||||
$rootScope.$apply('destroyFromNg1 = true');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
|
||||
controllerOnDestroyA.calls.reset();
|
||||
controllerOnDestroyB.calls.reset();
|
||||
$rootScope.$apply('destroyFromNg1 = false');
|
||||
controllerOnDestroyA.calls.reset();
|
||||
controllerOnDestroyB.calls.reset();
|
||||
$rootScope.$apply('destroyFromNg1 = false');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$apply('destroyFromNg2 = true');
|
||||
$rootScope.$apply('destroyFromNg2 = true');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not call `$onDestroy()` on scope', async(() => {
|
||||
const scopeOnDestroy = jasmine.createSpy('scopeOnDestroy');
|
||||
|
|
Loading…
Reference in New Issue