2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2018-03-16 16:18:11 -04:00
|
|
|
import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory, NgZone, OnChanges, OnDestroy, Output, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core';
|
2016-11-22 12:21:03 -05:00
|
|
|
import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
2016-08-05 16:32:04 -04:00
|
|
|
import {BrowserModule} from '@angular/platform-browser';
|
2016-10-11 18:48:08 -04:00
|
|
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
2018-11-22 16:51:55 -05:00
|
|
|
import {fixmeIvy} from '@angular/private/testing';
|
feat(upgrade): return a function (instead of array) from `downgradeInjectable()` (#14037)
This makes it more consistent with the dynamic version of `upgrade` and makes it
possible to share code between the dynamic and static versions.
This commit also refactors the file layout, moving common and dynamic-specific
files to `common/` and `dynamic/` directories respectively and renaming `aot/`
to `static/`.
Some private keys, used as AngularJS DI tokens, have also been renamed, but this
should not affect apps, since these keys are undocumented and not supposed to
be used externally.
BREAKING CHANGE:
Previously, `upgrade/static/downgradeInjectable` returned an array of the form:
```js
['dep1', 'dep2', ..., function factory(dep1, dep2, ...) { ... }]
```
Now it returns a function with an `$inject` property:
```js
factory.$inject = ['dep1', 'dep2', ...];
function factory(dep1, dep2, ...) { ... }
```
It shouldn't affect the behavior of apps, since both forms are equally suitable
to be used for registering AngularJS injectable services, but it is possible
that type-checking might fail or that current code breaks if it relies on the
returned value being an array.
2017-01-13 09:20:28 -05:00
|
|
|
import * as angular from '@angular/upgrade/src/common/angular1';
|
2018-10-11 10:44:59 -04:00
|
|
|
import {$EXCEPTION_HANDLER} from '@angular/upgrade/src/common/constants';
|
2017-01-19 06:04:24 -05:00
|
|
|
import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter';
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-02-23 08:18:21 -05:00
|
|
|
import {$apply, $digest, html, multiTrim, withEachNg1Version} from './test_helpers';
|
2015-08-05 13:32:14 -04:00
|
|
|
|
2017-12-17 18:10:54 -05:00
|
|
|
declare global {
|
|
|
|
export var inject: Function;
|
|
|
|
}
|
|
|
|
|
2018-02-15 12:21:18 -05:00
|
|
|
withEachNg1Version(() => {
|
2015-10-13 00:32:41 -04:00
|
|
|
describe('adapter: ng1 to ng2', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
|
2016-08-12 17:15:37 -04:00
|
|
|
beforeEach(() => destroyPlatform());
|
|
|
|
afterEach(() => destroyPlatform());
|
feat(testing): add implicit test module
Every test now has an implicit module. It can be configured via `configureModule` (from @angular/core/testing)
to add providers, directives, pipes, ...
The compiler now has to be configured separately via `configureCompiler` (from @angular/core/testing)
to add providers or define whether to use jit.
BREAKING CHANGE:
- Application providers can no longer inject compiler internals (i.e. everything
from `@angular/compiler). Inject `Compiler` instead. This reflects the
changes to `bootstrap` for module support (3f55aa609f60f130f1d69188ed057214b1267cb3).
- Compiler providers can no longer be added via `addProviders` / `withProviders`.
Use the new method `configureCompiler` instead.
- Platform directives / pipes need to be provided via
`configureModule` and can no longer be provided via the
`PLATFORM_PIPES` / `PLATFORM_DIRECTIVES` tokens.
- `setBaseTestProviders()` was renamed into `initTestEnvironment` and
now takes a `PlatformRef` and a factory for a
`Compiler`.
- E.g. for the browser platform:
BEFORE:
```
import {setBaseTestProviders} from ‘@angular/core/testing’;
import {TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from ‘@angular/platform-browser-dynamic/testing’;
setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
```
AFTER:
```
import {setBaseTestProviders} from ‘@angular/core/testing’;
import {browserTestCompiler, browserDynamicTestPlatform,
BrowserDynamicTestModule} from ‘@angular/platform-browser-dynamic/testing’;
initTestEnvironment(
browserTestCompiler,
browserDynamicTestPlatform(),
BrowserDynamicTestModule);
```
- E.g. for the server platform:
BEFORE:
```
import {setBaseTestProviders} from ‘@angular/core/testing’;
import {TEST_SERVER_PLATFORM_PROVIDERS,
TEST_SERVER_APPLICATION_PROVIDERS} from ‘@angular/platform-server/testing/server’;
setBaseTestProviders(TEST_SERVER_PLATFORM_PROVIDERS,
TEST_SERVER_APPLICATION_PROVIDERS);
```
AFTER:
```
import {setBaseTestProviders} from ‘@angular/core/testing’;
import {serverTestCompiler, serverTestPlatform,
ServerTestModule} from ‘@angular/platform-browser-dynamic/testing’;
initTestEnvironment(
serverTestCompiler,
serverTestPlatform(),
ServerTestModule);
```
Related to #9726
Closes #9846
2016-07-04 12:37:30 -04:00
|
|
|
|
2017-01-13 15:14:58 -05:00
|
|
|
describe('(basic use)', () => {
|
2017-01-27 01:30:42 -05:00
|
|
|
it('should have AngularJS loaded', () => expect(angular.version.major).toBe(1));
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should instantiate ng2 in ng1 template and project content', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'NG2' }}(<ng-content></ng-content>)`,
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element =
|
|
|
|
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('ng1[NG2(~ng-content~)]');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should instantiate ng1 in ng2 template and project content', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng1', () => {
|
|
|
|
return {
|
|
|
|
transclude: true,
|
|
|
|
template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'
|
|
|
|
};
|
|
|
|
});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<div>{{\'ng1(\'}}<ng2></ng2>{{\')\'}}</div>');
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-01-13 15:14:58 -05:00
|
|
|
|
|
|
|
it('supports the compilerOptions argument', async(() => {
|
|
|
|
const platformRef = platformBrowserDynamic();
|
2017-06-01 17:45:49 -04:00
|
|
|
spyOn(platformRef, 'bootstrapModule').and.callThrough();
|
|
|
|
spyOn(platformRef, 'bootstrapModuleFactory').and.callThrough();
|
2017-01-13 15:14:58 -05:00
|
|
|
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({selector: 'ng2', template: `{{ 'NG2' }}(<ng-content></ng-content>)`})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
|
|
|
const element =
|
|
|
|
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
|
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2AppModule {
|
|
|
|
ngDoBootstrap() {}
|
2017-06-01 17:45:49 -04:00
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2AppModule, {providers: []});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
2017-06-01 17:45:49 -04:00
|
|
|
expect(platformRef.bootstrapModule).toHaveBeenCalledWith(jasmine.any(Function), [
|
|
|
|
{providers: []}, jasmine.any(Object)
|
|
|
|
]);
|
|
|
|
expect(platformRef.bootstrapModuleFactory)
|
|
|
|
.toHaveBeenCalledWith(
|
|
|
|
jasmine.any(NgModuleFactory), {providers: [], ngZone: jasmine.any(NgZone)});
|
2017-01-13 15:14:58 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
2015-08-06 16:19:29 -04:00
|
|
|
|
2016-10-26 22:24:47 -04:00
|
|
|
describe('bootstrap errors', () => {
|
|
|
|
let adapter: UpgradeAdapter;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2016-10-27 13:40:02 -04:00
|
|
|
angular.module('ng1', []);
|
2016-10-26 22:24:47 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `<BAD TEMPLATE div></div>`,
|
|
|
|
})
|
|
|
|
class ng2Component {
|
|
|
|
}
|
2016-10-26 22:24:47 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [ng2Component],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-10-26 22:24:47 -04:00
|
|
|
|
|
|
|
adapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
});
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy(
|
|
|
|
'FW-682: JIT compilation occurs at component definition time rather than bootstrap') &&
|
|
|
|
it('should throw an uncaught error', fakeAsync(() => {
|
|
|
|
const resolveSpy = jasmine.createSpy('resolveSpy');
|
|
|
|
spyOn(console, 'error');
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']).ready(resolveSpy);
|
|
|
|
flushMicrotasks();
|
|
|
|
}).toThrowError();
|
|
|
|
expect(resolveSpy).not.toHaveBeenCalled();
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy(
|
|
|
|
'FW-682: JIT compilation occurs at component definition time rather than bootstrap') &&
|
|
|
|
it('should output an error message to the console and re-throw', fakeAsync(() => {
|
|
|
|
const consoleErrorSpy: jasmine.Spy = spyOn(console, 'error');
|
|
|
|
expect(() => {
|
|
|
|
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']);
|
|
|
|
flushMicrotasks();
|
|
|
|
}).toThrowError();
|
|
|
|
const args: any[] = consoleErrorSpy.calls.mostRecent().args;
|
|
|
|
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
|
|
expect(args.length).toBeGreaterThan(0);
|
|
|
|
expect(args[0]).toEqual(jasmine.any(Error));
|
|
|
|
}));
|
2016-10-26 22:24:47 -04:00
|
|
|
});
|
|
|
|
|
2015-09-30 18:42:02 -04:00
|
|
|
describe('scope/component change-detection', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should interleave scope and component expressions', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
const log: string[] = [];
|
|
|
|
const l = (value: string) => {
|
|
|
|
log.push(value);
|
|
|
|
return value + ';';
|
|
|
|
};
|
2015-09-30 18:42:02 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng1a', () => ({template: '{{ l(\'ng1a\') }}'}));
|
|
|
|
ng1Module.directive('ng1b', () => ({template: '{{ l(\'ng1b\') }}'}));
|
|
|
|
ng1Module.run(($rootScope: any) => {
|
|
|
|
$rootScope.l = l;
|
|
|
|
$rootScope.reset = () => log.length = 0;
|
|
|
|
});
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
l: any;
|
|
|
|
constructor() { this.l = l; }
|
|
|
|
}
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element =
|
|
|
|
html('<div>{{reset(); l(\'1A\');}}<ng2>{{l(\'1B\')}}</ng2>{{l(\'1C\')}}</div>');
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('1A;2A;ng1a;2B;ng1b;2C;1C;');
|
|
|
|
// https://github.com/angular/angular.js/issues/12983
|
|
|
|
expect(log).toEqual(['1A', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']);
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-01-13 15:19:57 -05:00
|
|
|
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
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(() => {
|
|
|
|
let appComponent: AppComponent;
|
|
|
|
let upgradeRef: UpgradeAdapterRef;
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
|
|
|
class AppComponent {
|
|
|
|
value?: number;
|
|
|
|
constructor() { appComponent = this; }
|
|
|
|
}
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'my-child',
|
|
|
|
template: '<div>{{valueFromPromise}}',
|
|
|
|
})
|
|
|
|
class ChildComponent {
|
|
|
|
valueFromPromise?: number;
|
|
|
|
@Input()
|
|
|
|
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
|
|
|
|
|
|
|
constructor(private zone: NgZone) {}
|
|
|
|
|
|
|
|
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');
|
|
|
|
// upgradeRef.dispose();
|
|
|
|
// }, 0);
|
|
|
|
this.zone.onMicrotaskEmpty.subscribe(() => {
|
|
|
|
expect(element.textContent).toEqual('5');
|
|
|
|
upgradeRef.dispose();
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise.resolve().then(
|
|
|
|
() => this.valueFromPromise = changes['value'].currentValue);
|
|
|
|
}
|
|
|
|
}
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({declarations: [AppComponent, ChildComponent], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []).directive(
|
|
|
|
'myApp', adapter.downgradeNg2Component(AppComponent));
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<my-app></my-app>');
|
2017-01-13 15:19:57 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
upgradeRef = ref;
|
|
|
|
appComponent.value = 5;
|
|
|
|
});
|
|
|
|
}));
|
2017-01-13 15:19:57 -05:00
|
|
|
|
|
|
|
// This test demonstrates https://github.com/angular/angular/issues/6385
|
|
|
|
// which was invalidly fixed by https://github.com/angular/angular/pull/6386
|
|
|
|
// it('should not trigger $digest from an async operation in a watcher', async(() => {
|
|
|
|
// @Component({selector: 'my-app', template: ''})
|
|
|
|
// class AppComponent {
|
|
|
|
// }
|
|
|
|
|
|
|
|
// @NgModule({declarations: [AppComponent], imports: [BrowserModule]})
|
|
|
|
// class Ng2Module {
|
|
|
|
// }
|
|
|
|
|
|
|
|
// const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
// const ng1Module = angular.module('ng1', []).directive(
|
|
|
|
// 'myApp', adapter.downgradeNg2Component(AppComponent));
|
|
|
|
|
|
|
|
// const element = html('<my-app></my-app>');
|
|
|
|
|
|
|
|
// adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// let doTimeout = false;
|
|
|
|
// let timeoutId: number;
|
|
|
|
// ref.ng1RootScope.$watch(() => {
|
|
|
|
// if (doTimeout && !timeoutId) {
|
|
|
|
// timeoutId = window.setTimeout(function() {
|
|
|
|
// timeoutId = null;
|
|
|
|
// }, 10);
|
|
|
|
// }
|
|
|
|
// });
|
|
|
|
// doTimeout = true;
|
|
|
|
// });
|
|
|
|
// }));
|
2015-09-30 18:42:02 -04:00
|
|
|
});
|
2015-10-01 16:14:59 -04:00
|
|
|
|
2015-10-11 14:18:11 -04:00
|
|
|
describe('downgrade ng2 component', () => {
|
2017-01-13 17:36:16 -05:00
|
|
|
it('should allow non-element selectors for downgraded components', async(() => {
|
|
|
|
@Component({selector: '[itWorks]', template: 'It works'})
|
|
|
|
class WorksComponent {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [WorksComponent], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(WorksComponent));
|
|
|
|
|
|
|
|
const element = html('<ng2></ng2>');
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
2017-03-24 12:59:18 -04:00
|
|
|
expect(multiTrim(document.body.textContent !)).toBe('It works');
|
2017-01-13 17:36:16 -05:00
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
|
|
|
it('should bind properties, events', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []).value(
|
|
|
|
$EXCEPTION_HANDLER, (err: any) => { throw err; });
|
|
|
|
|
|
|
|
ng1Module.run(($rootScope: any) => {
|
|
|
|
$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 Ng2 {
|
|
|
|
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) => {
|
|
|
|
if ((this as any)[prop] != value) {
|
|
|
|
throw new Error(
|
|
|
|
`Expected: '${prop}' to be '${value}' but was '${(this as any)[prop]}'`);
|
|
|
|
}
|
|
|
|
};
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const assertChange = (prop: string, value: any) => {
|
|
|
|
assert(prop, value);
|
|
|
|
if (!changes[prop]) {
|
|
|
|
throw new Error(`Changes record for '${prop}' not found.`);
|
|
|
|
}
|
|
|
|
const actValue = changes[prop].currentValue;
|
|
|
|
if (actValue != value) {
|
|
|
|
throw new Error(
|
|
|
|
`Expected changes record for'${prop}' to be '${value}' but was '${actValue}'`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
2016-06-08 19:38:52 -04:00
|
|
|
}
|
|
|
|
}
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div>
|
2017-02-04 10:19:09 -05:00
|
|
|
<ng2 literal="Text" interpolate="Hello {{name}}"
|
2015-10-01 16:14:59 -04:00
|
|
|
bind-one-way-a="dataA" [one-way-b]="dataB"
|
|
|
|
bindon-two-way-a="modelA" [(two-way-b)]="modelB"
|
|
|
|
on-event-a='eventA=$event' (event-b)="eventB=$event"></ng2>
|
|
|
|
| modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}};
|
|
|
|
</div>`);
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
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;');
|
|
|
|
|
|
|
|
ref.ng1RootScope.$apply('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;');
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-01 16:14:59 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
}));
|
2016-01-13 09:35:48 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
|
|
|
it('should support two-way binding and event listener', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
|
|
|
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
|
|
|
$rootScope['value'] = 'world';
|
|
|
|
$rootScope['listener'] = listenerSpy;
|
|
|
|
});
|
2018-03-16 16:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@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));
|
|
|
|
}
|
|
|
|
}
|
2018-03-16 16:18:11 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2018-03-16 16:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({declarations: [Ng2Component], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
ngDoBootstrap() {}
|
|
|
|
}
|
2018-03-16 16:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`
|
2018-03-16 16:18:11 -04:00
|
|
|
<div>
|
|
|
|
<ng2 [(model)]="value" (model-change)="listener($event)"></ng2>
|
|
|
|
| value: {{value}}
|
|
|
|
</div>
|
|
|
|
`);
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC');
|
|
|
|
expect(listenerSpy).toHaveBeenCalledWith('newC');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2018-03-16 16:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
|
|
|
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-03 11:18:40 -04:00
|
|
|
ngOnChangesCount: {{ ngOnChangesCount }} |
|
|
|
|
firstChangesCount: {{ firstChangesCount }} |
|
|
|
|
initialValue: {{ initialValue }}`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
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++;
|
|
|
|
|
|
|
|
if (this.ngOnChangesCount === 1) {
|
|
|
|
this.initialValue = this.foo;
|
|
|
|
}
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
|
|
|
this.firstChangesCount++;
|
|
|
|
}
|
|
|
|
}
|
2017-05-03 11:18:40 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
|
|
|
class Ng2Module {
|
2017-05-03 11:18:40 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1Module = angular.module('ng1', []).directive(
|
|
|
|
'ng2', adapter.downgradeNg2Component(Ng2Component));
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`
|
2017-05-03 11:18:40 -04:00
|
|
|
<ng2 [foo]="'foo'"></ng2>
|
|
|
|
<ng2 foo="bar"></ng2>
|
|
|
|
<ng2 [foo]="'baz'" ng-if="true"></ng2>
|
|
|
|
<ng2 foo="qux" ng-if="true"></ng2>
|
|
|
|
`);
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(ref => {
|
|
|
|
const nodes = element.querySelectorAll('ng2');
|
|
|
|
const expectedTextWith = (value: string) =>
|
|
|
|
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
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'));
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-05-03 11:18:40 -04:00
|
|
|
|
2017-01-23 14:23:45 -05:00
|
|
|
it('should bind to ng-model', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
ng1Module.run(($rootScope: any /** TODO #9100 */) => { $rootScope.modelA = 'A'; });
|
|
|
|
|
|
|
|
let ng2Instance: Ng2;
|
|
|
|
@Component({selector: 'ng2', template: '{{_value}}'})
|
|
|
|
class Ng2 {
|
|
|
|
private _value: any = '';
|
|
|
|
private _onChangeCallback: (_: any) => void = () => {};
|
2017-07-06 13:46:20 -04:00
|
|
|
private _onTouchedCallback: () => void = () => {};
|
2017-01-23 14:23:45 -05:00
|
|
|
constructor() { ng2Instance = this; }
|
|
|
|
writeValue(value: any) { this._value = value; }
|
|
|
|
registerOnChange(fn: any) { this._onChangeCallback = fn; }
|
2017-07-06 13:46:20 -04:00
|
|
|
registerOnTouched(fn: any) { this._onTouchedCallback = fn; }
|
|
|
|
doTouch() { this._onTouchedCallback(); }
|
2017-01-23 14:23:45 -05:00
|
|
|
doChange(newValue: string) {
|
|
|
|
this._value = newValue;
|
|
|
|
this._onChangeCallback(newValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2 ng-model="modelA"></ng2> | {{modelA}}</div>`);
|
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-23 14:23:45 -05:00
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
let $rootScope: any = ref.ng1RootScope;
|
|
|
|
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('A | A');
|
|
|
|
|
|
|
|
$rootScope.modelA = 'B';
|
|
|
|
$rootScope.$apply();
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('B | B');
|
|
|
|
|
|
|
|
ng2Instance.doChange('C');
|
|
|
|
expect($rootScope.modelA).toBe('C');
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('C | C');
|
|
|
|
|
2017-07-06 13:46:20 -04:00
|
|
|
const downgradedElement = <Element>document.body.querySelector('ng2');
|
|
|
|
expect(downgradedElement.classList.contains('ng-touched')).toBe(false);
|
|
|
|
|
|
|
|
ng2Instance.doTouch();
|
|
|
|
$rootScope.$apply();
|
|
|
|
expect(downgradedElement.classList.contains('ng-touched')).toBe(true);
|
|
|
|
|
2017-01-23 14:23:45 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should properly run cleanup when ng1 directive is destroyed', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
const onDestroyed: EventEmitter<string> = new EventEmitter<string>();
|
2016-01-13 09:35:48 -05:00
|
|
|
|
|
|
|
ng1Module.directive('ng1', () => {
|
|
|
|
return {
|
|
|
|
template: '<div ng-if="!destroyIt"><ng2></ng2></div>',
|
2016-10-27 13:40:02 -04:00
|
|
|
controller: function($rootScope: any, $timeout: Function) {
|
2016-10-10 12:18:33 -04:00
|
|
|
$timeout(() => { $rootScope.destroyIt = true; });
|
2016-01-13 09:35:48 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({selector: 'ng2', template: 'test'})
|
|
|
|
class Ng2 {
|
|
|
|
ngOnDestroy() { onDestroyed.emit('destroyed'); }
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-01-13 09:35:48 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html('<ng1></ng1>');
|
2016-07-21 20:12:00 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
2016-08-19 16:51:45 -04:00
|
|
|
onDestroyed.subscribe(() => { ref.dispose(); });
|
2016-07-21 20:12:00 -04:00
|
|
|
});
|
2016-01-13 09:35:48 -05:00
|
|
|
}));
|
2016-05-17 18:55:53 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
|
|
|
it('should properly run cleanup with multiple levels of nesting', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let destroyed = false;
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component(
|
|
|
|
{selector: 'ng2-outer', template: '<div *ngIf="!destroyIt"><ng1></ng1></div>'})
|
|
|
|
class Ng2OuterComponent {
|
|
|
|
@Input() destroyIt = false;
|
|
|
|
}
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2-inner', template: 'test'})
|
|
|
|
class Ng2InnerComponent implements OnDestroy {
|
|
|
|
ngOnDestroy() { destroyed = true; }
|
|
|
|
}
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations:
|
|
|
|
[Ng2InnerComponent, Ng2OuterComponent, adapter.upgradeNg1Component('ng1')],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1', () => ({template: '<ng2-inner></ng2-inner>'}))
|
|
|
|
.directive('ng2Inner', adapter.downgradeNg2Component(Ng2InnerComponent))
|
|
|
|
.directive('ng2Outer', adapter.downgradeNg2Component(Ng2OuterComponent));
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<ng2-outer [destroy-it]="destroyIt"></ng2-outer>');
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, [ng1Module.name]).ready(ref => {
|
|
|
|
expect(element.textContent).toBe('test');
|
|
|
|
expect(destroyed).toBe(false);
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$apply(ref, 'destroyIt = true');
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(element.textContent).toBe('');
|
|
|
|
expect(destroyed).toBe(true);
|
|
|
|
});
|
|
|
|
}));
|
2018-02-23 08:18:21 -05:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should fallback to the root ng2.injector when compiled outside the dom', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-06-08 19:38:52 -04:00
|
|
|
|
|
|
|
ng1Module.directive('ng1', [
|
|
|
|
'$compile',
|
2016-10-27 13:40:02 -04:00
|
|
|
($compile: Function) => {
|
2016-06-08 19:38:52 -04:00
|
|
|
return {
|
2016-10-27 13:40:02 -04:00
|
|
|
link: function($scope: any, $element: any, $attrs: any) {
|
2016-10-10 12:18:33 -04:00
|
|
|
const compiled = $compile('<ng2></ng2>');
|
|
|
|
const template = compiled($scope);
|
2016-06-08 19:38:52 -04:00
|
|
|
$element.append(template);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({selector: 'ng2', template: 'test'})
|
|
|
|
class Ng2 {
|
2017-09-22 13:51:03 -04:00
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html('<ng1></ng1>');
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('test');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support multi-slot projection', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
|
|
|
'2b(<ng-content select=".ng1b"></ng-content>)'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// 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>');
|
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2017-02-03 05:11:16 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-02-03 05:11:16 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(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(`
|
2017-02-03 05:11:16 -05:00
|
|
|
<ng2 ng-repeat="item in items" [item-id]="item.id">
|
|
|
|
<div ng-repeat="subitem in item.subitems">{{ subitem }}</div>
|
|
|
|
</ng2>
|
|
|
|
`);
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, [ng1Module.name]).ready(ref => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-02-03 05:11:16 -05:00
|
|
|
|
2017-01-13 15:14:58 -05:00
|
|
|
it('should allow attribute selectors for components in ng2', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
|
|
|
const ng1Module = angular.module('myExample', []);
|
|
|
|
|
|
|
|
@Component({selector: '[works]', template: 'works!'})
|
|
|
|
class WorksComponent {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'root-component', template: 'It <div works></div>'})
|
|
|
|
class RootComponent {
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({imports: [BrowserModule], declarations: [RootComponent, WorksComponent]})
|
|
|
|
class MyNg2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
ng1Module.directive('rootComponent', adapter.downgradeNg2Component(RootComponent));
|
|
|
|
|
|
|
|
document.body.innerHTML = '<root-component></root-component>';
|
2017-03-24 12:59:18 -04:00
|
|
|
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
2017-01-13 15:14:58 -05:00
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('It works!');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2015-10-01 16:14:59 -04:00
|
|
|
});
|
2015-10-04 12:33:20 -04:00
|
|
|
|
2015-10-11 14:18:11 -04:00
|
|
|
describe('upgrade ng1 component', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support `@` bindings', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'Inside: {{ $ctrl.inputA }}, {{ $ctrl.inputB }}',
|
|
|
|
bindings: {inputA: '@inputAttrA', inputB: '@'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-04-18 09:49:35 -04:00
|
|
|
<ng1 inputAttrA="{{ dataA }}" inputB="{{ dataB }}"></ng1>
|
|
|
|
| Outside: {{ dataA }}, {{ dataB }}
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = 'foo';
|
|
|
|
dataB = 'bar';
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module = angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo, bar | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Controller.inputA = 'baz';
|
|
|
|
ng1Controller.inputB = 'qux';
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: baz, qux | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.dataA = 'foo2';
|
|
|
|
ng2ComponentInstance.dataB = 'bar2';
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support `<` bindings', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'Inside: {{ $ctrl.inputA.value }}, {{ $ctrl.inputB.value }}',
|
|
|
|
bindings: {inputA: '<inputAttrA', inputB: '<'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-04-18 09:49:35 -04:00
|
|
|
<ng1 [inputAttrA]="dataA" [inputB]="dataB"></ng1>
|
|
|
|
| Outside: {{ dataA.value }}, {{ dataB.value }}
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = {value: 'foo'};
|
|
|
|
dataB = {value: 'bar'};
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module = angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo, bar | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Controller.inputA = {value: 'baz'};
|
|
|
|
ng1Controller.inputB = {value: 'qux'};
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: baz, qux | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.dataA = {value: 'foo2'};
|
|
|
|
ng2ComponentInstance.dataB = {value: 'bar2'};
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support `=` bindings', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'Inside: {{ $ctrl.inputA.value }}, {{ $ctrl.inputB.value }}',
|
|
|
|
bindings: {inputA: '=inputAttrA', inputB: '='}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-04-18 09:49:35 -04:00
|
|
|
<ng1 [(inputAttrA)]="dataA" [(inputB)]="dataB"></ng1>
|
|
|
|
| Outside: {{ dataA.value }}, {{ dataB.value }}
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = {value: 'foo'};
|
|
|
|
dataB = {value: 'bar'};
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module = angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo, bar | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Controller.inputA = {value: 'baz'};
|
|
|
|
ng1Controller.inputB = {value: 'qux'};
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: baz, qux | Outside: baz, qux');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.dataA = {value: 'foo2'};
|
|
|
|
ng2ComponentInstance.dataB = {value: 'bar2'};
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support `&` bindings', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'Inside: -',
|
|
|
|
bindings: {outputA: '&outputAttrA', outputB: '&'}
|
|
|
|
};
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-04-18 09:49:35 -04:00
|
|
|
<ng1 (outputAttrA)="dataA = $event" (outputB)="dataB = $event"></ng1>
|
|
|
|
| Outside: {{ dataA }}, {{ dataB }}
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = 'foo';
|
|
|
|
dataB = 'bar';
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module = angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar');
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Controller.outputA('baz');
|
|
|
|
ng1Controller.outputB('qux');
|
|
|
|
$digest(ref);
|
2017-04-18 09:49:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: baz, qux');
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should bind properties, events', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
template: 'Hello {{fullName}}; A: {{modelA}}; B: {{modelB}}; C: {{modelC}}; | ',
|
|
|
|
scope:
|
|
|
|
{fullName: '@', modelA: '=dataA', modelB: '=dataB', modelC: '=', event: '&'},
|
|
|
|
link: function(scope: any) {
|
|
|
|
scope.$watch('modelB', (v: string) => {
|
|
|
|
if (v == 'Savkin') {
|
|
|
|
scope.modelB = 'SAVKIN';
|
|
|
|
scope.event('WORKS');
|
|
|
|
|
|
|
|
// Should not update because [model-a] is uni directional
|
|
|
|
scope.modelA = 'VICTOR';
|
|
|
|
}
|
|
|
|
});
|
2015-10-04 12:33:20 -04:00
|
|
|
}
|
2018-11-22 16:51:55 -05:00
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
|
|
|
'<ng1 fullName="{{last}}, {{first}}, {{city}}" [dataA]="first" [(dataB)]="last" [modelC]="city" ' +
|
|
|
|
'(event)="event=$event"></ng1>' +
|
|
|
|
'<ng1 fullName="{{\'TEST\'}}" dataA="First" dataB="Last" modelC="City"></ng1>' +
|
|
|
|
'{{event}}-{{last}}, {{first}}, {{city}}'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
first = 'Victor';
|
|
|
|
last = 'Savkin';
|
|
|
|
city = 'SF';
|
|
|
|
event = '?';
|
2015-10-11 14:18:11 -04:00
|
|
|
}
|
2016-09-29 12:45:28 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
|
|
|
|
// events, and so without this we would not see the events processed.
|
|
|
|
setTimeout(() => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual(
|
|
|
|
'Hello SAVKIN, Victor, SF; A: VICTOR; B: SAVKIN; C: SF; | Hello TEST; A: First; B: Last; C: City; | WORKS-SAVKIN, Victor, SF');
|
|
|
|
ref.dispose();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should bind optional properties', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
template: 'Hello; A: {{modelA}}; B: {{modelB}}; | ',
|
|
|
|
scope: {modelA: '=?dataA', modelB: '=?'}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1 [dataA]="first" [modelB]="last"></ng1>' +
|
|
|
|
'<ng1 dataA="First" modelB="Last"></ng1>' +
|
|
|
|
'<ng1></ng1>' +
|
|
|
|
'<ng1></ng1>'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
first = 'Victor';
|
|
|
|
last = 'Savkin';
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
|
|
|
|
// events, and so without this we would not see the events processed.
|
|
|
|
setTimeout(() => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual(
|
|
|
|
'Hello; A: Victor; B: Savkin; | Hello; A: First; B: Last; | Hello; A: ; B: ; | Hello; A: ; B: ; |');
|
|
|
|
ref.dispose();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should bind properties, events in controller when bindToController is not used',
|
|
|
|
async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
template: '{{someText}} - Length: {{data.length}}',
|
|
|
|
scope: {data: '='},
|
|
|
|
controller: function($scope: any) {
|
|
|
|
$scope.someText = 'ng1 - Data: ' + $scope.data;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
|
|
|
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
dataList = [1, 2, 3];
|
|
|
|
someText = 'ng2';
|
|
|
|
}
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
|
|
|
|
// events, and so without this we would not see the events processed.
|
|
|
|
setTimeout(() => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3');
|
|
|
|
ref.dispose();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should bind properties, events in link function', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
template: '{{someText}} - Length: {{data.length}}',
|
|
|
|
scope: {data: '='},
|
|
|
|
link: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
|
|
|
};
|
|
|
|
};
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
|
|
|
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
dataList = [1, 2, 3];
|
|
|
|
someText = 'ng2';
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-05-17 17:53:59 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
|
|
|
|
// events, and so without this we would not see the events processed.
|
|
|
|
setTimeout(() => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3');
|
|
|
|
ref.dispose();
|
|
|
|
}, 0);
|
2016-10-27 13:40:02 -04:00
|
|
|
});
|
2018-11-22 16:51:55 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support templateUrl fetched from $httpBackend', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.value(
|
|
|
|
'$httpBackend', (method: string, url: string, post: any, cbFn: Function) => {
|
|
|
|
cbFn(200, `${method}:${url}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('GET:url.html');
|
|
|
|
ref.dispose();
|
2016-10-27 13:40:02 -04:00
|
|
|
});
|
2018-11-22 16:51:55 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support templateUrl as a function', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.value(
|
|
|
|
'$httpBackend', (method: string, url: string, post: any, cbFn: Function) => {
|
|
|
|
cbFn(200, `${method}:${url}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
const ng1 = () => { return {templateUrl() { return 'url.html'; }}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-06-04 22:53:51 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('GET:url.html');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-06-04 22:53:51 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support empty template', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-12-10 07:23:47 -05:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => { return {template: ''}; };
|
2015-12-10 07:23:47 -05:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-12-10 07:23:47 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-12-10 07:23:47 -05:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support template as a function', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-06-04 22:53:51 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => { return {template() { return ''; }}; };
|
2016-06-04 22:53:51 -04:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-06-04 22:53:51 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2016-06-04 22:53:51 -04:00
|
|
|
}));
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support templateUrl fetched from $templateCache', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS'));
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
2017-08-08 17:03:27 -04:00
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support controller with controllerAs', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
scope: true,
|
|
|
|
template:
|
|
|
|
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: class {
|
|
|
|
scope: any; hasElement: string; $element: any; isClass: any;
|
|
|
|
constructor($scope: any, $element: any) {
|
|
|
|
this.verifyIAmAClass();
|
|
|
|
this.scope =
|
|
|
|
$scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope';
|
|
|
|
this.hasElement = $element[0].nodeName;
|
|
|
|
this.$element = $element;
|
|
|
|
} verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() {
|
|
|
|
return this.$element.controller('ng1') == this ? 'published' :
|
|
|
|
'not-published';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2015-11-13 12:55:40 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2015-11-13 12:55:40 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual('scope; isClass; NG1; published');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support bindToController', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
|
|
|
bindToController: true,
|
|
|
|
template: '{{ctl.title}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: class {}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support bindToController with bindings', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
scope: {},
|
|
|
|
bindToController: {title: '@'},
|
|
|
|
template: '{{ctl.title}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: class {}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
2015-10-11 14:18:11 -04:00
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support single require in linking fn', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = ($rootScope: any) => {
|
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
|
|
|
bindToController: true,
|
|
|
|
template: '{{ctl.status}}',
|
|
|
|
require: 'ng1',
|
|
|
|
controllerAs: 'ctrl',
|
|
|
|
controller: class {status = 'WORKS';},
|
|
|
|
link: function(scope: any, element: any, attrs: any, linkController: any) {
|
|
|
|
expect(scope.$root).toEqual($rootScope);
|
|
|
|
expect(element[0].nodeName).toEqual('NG1');
|
|
|
|
expect(linkController.status).toEqual('WORKS');
|
|
|
|
scope.ctl = linkController;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-724: upgraded ng1 components are not being rendered') &&
|
|
|
|
it('should support array require in linking fn', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const parent = () => { return {controller: class {parent = 'PARENT';}}; };
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
|
|
|
bindToController: true,
|
|
|
|
template: '{{parent.parent}}:{{ng1.status}}',
|
|
|
|
require: ['ng1', '^parent', '?^^notFound'],
|
|
|
|
controllerAs: 'ctrl',
|
|
|
|
controller: class {status = 'WORKS';},
|
|
|
|
link: function(scope: any, element: any, attrs: any, linkControllers: any) {
|
|
|
|
expect(linkControllers[0].status).toEqual('WORKS');
|
|
|
|
expect(linkControllers[1].parent).toEqual('PARENT');
|
|
|
|
expect(linkControllers[2]).toBe(undefined);
|
|
|
|
scope.ng1 = linkControllers[0];
|
|
|
|
scope.parent = linkControllers[1];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('parent', parent);
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2 {
|
2015-10-11 14:18:11 -04:00
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
const element = html(`<div><parent><ng2></ng2></parent></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
describe('with lifecycle hooks', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should call `$onInit()` on controller', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onInitSpyA = jasmine.createSpy('$onInitA');
|
|
|
|
const $onInitSpyB = jasmine.createSpy('$onInitB');
|
|
|
|
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: class {$onInit() { $onInitSpyA(); }}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function() { this.$onInit = $onInitSpyB; }
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($onInitSpyA).toHaveBeenCalled();
|
|
|
|
expect($onInitSpyB).toHaveBeenCalled();
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should not call `$onInit()` on scope', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onInitSpy = jasmine.createSpy('$onInit');
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
Object.getPrototypeOf($scope).$onInit = $onInitSpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
$scope['$onInit'] = $onInitSpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($onInitSpy).not.toHaveBeenCalled();
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should call `$doCheck()` on controller', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $doCheckSpyA = jasmine.createSpy('$doCheckA');
|
|
|
|
const $doCheckSpyB = jasmine.createSpy('$doCheckB');
|
|
|
|
let changeDetector: ChangeDetectorRef;
|
|
|
|
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: class {$doCheck() { $doCheckSpyA(); }}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function() { this.$doCheck = $doCheckSpyB; }
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($doCheckSpyA).toHaveBeenCalled();
|
|
|
|
expect($doCheckSpyB).toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$doCheckSpyA.calls.reset();
|
|
|
|
$doCheckSpyB.calls.reset();
|
|
|
|
changeDetector.detectChanges();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($doCheckSpyA).toHaveBeenCalled();
|
|
|
|
expect($doCheckSpyB).toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should not call `$doCheck()` on scope', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $doCheckSpyA = jasmine.createSpy('$doCheckA');
|
|
|
|
const $doCheckSpyB = jasmine.createSpy('$doCheckB');
|
|
|
|
let changeDetector: ChangeDetectorRef;
|
|
|
|
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
constructor(cd: ChangeDetectorRef) { changeDetector = cd; }
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
Object.getPrototypeOf($scope).$doCheck = $doCheckSpyA;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
$scope['$doCheck'] = $doCheckSpyB;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
$doCheckSpyA.calls.reset();
|
|
|
|
$doCheckSpyB.calls.reset();
|
|
|
|
changeDetector.detectChanges();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($doCheckSpyA).not.toHaveBeenCalled();
|
|
|
|
expect($doCheckSpyB).not.toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should call `$postLink()` on controller', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $postLinkSpyA = jasmine.createSpy('$postLinkA');
|
|
|
|
const $postLinkSpyB = jasmine.createSpy('$postLinkB');
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: class {$postLink() { $postLinkSpyA(); }}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function() { this.$postLink = $postLinkSpyB; }
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($postLinkSpyA).toHaveBeenCalled();
|
|
|
|
expect($postLinkSpyB).toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should not call `$postLink()` on scope', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $postLinkSpy = jasmine.createSpy('$postLink');
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
Object.getPrototypeOf($scope).$postLink = $postLinkSpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
$scope['$postLink'] = $postLinkSpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($postLinkSpy).not.toHaveBeenCalled();
|
|
|
|
ref.dispose();
|
2016-12-27 17:42:53 -05:00
|
|
|
});
|
2018-11-22 16:51:55 -05:00
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should call `$onChanges()` on binding destination', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onChangesControllerSpyA = jasmine.createSpy('$onChangesControllerA');
|
|
|
|
const $onChangesControllerSpyB = jasmine.createSpy('$onChangesControllerB');
|
|
|
|
const $onChangesScopeSpy = jasmine.createSpy('$onChangesScope');
|
|
|
|
let ng2Instance: any;
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1-a [valA]="val"></ng1-a> | <ng1-b [valB]="val"></ng1-b>'
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
constructor() { ng2Instance = this; }
|
|
|
|
}
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {valA: '<'},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
this.$onChanges = $onChangesControllerSpyA;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {valB: '<'},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: class {
|
|
|
|
$onChanges(changes: SimpleChanges) {
|
|
|
|
$onChangesControllerSpyB(changes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component))
|
|
|
|
.run(($rootScope: angular.IRootScopeService) => {
|
|
|
|
Object.getPrototypeOf($rootScope).$onChanges = $onChangesScopeSpy;
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
// Initial `$onChanges()` call
|
|
|
|
tick();
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
|
|
|
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valA: jasmine.any(SimpleChange)
|
|
|
|
});
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
|
|
|
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valB: jasmine.any(SimpleChange)
|
|
|
|
});
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$onChangesControllerSpyA.calls.reset();
|
|
|
|
$onChangesControllerSpyB.calls.reset();
|
|
|
|
$onChangesScopeSpy.calls.reset();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// `$onChanges()` call after a change
|
|
|
|
ng2Instance.val = 'new value';
|
|
|
|
tick();
|
|
|
|
ref.ng1RootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
|
|
|
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valA: jasmine.objectContaining({currentValue: 'new value'})
|
|
|
|
});
|
2016-12-12 12:29:23 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
|
|
|
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valB: jasmine.objectContaining({currentValue: 'new value'})
|
|
|
|
});
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should call `$onDestroy()` on controller', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onDestroySpyA = jasmine.createSpy('$onDestroyA');
|
|
|
|
const $onDestroySpyB = jasmine.createSpy('$onDestroyB');
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2016-12-27 17:42:53 -05:00
|
|
|
<div *ngIf="!ng2Destroy">
|
|
|
|
<ng1-a></ng1-a> | <ng1-b></ng1-b>
|
|
|
|
</div>
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
|
|
|
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
|
|
|
|
// on
|
|
|
|
// the queue at the end of the test, causing it to fail.
|
|
|
|
// Mocking animations (via `ngAnimateMock`) avoids the issue.
|
|
|
|
angular.module('ng1', ['ngAnimateMock'])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: class {$onDestroy() { $onDestroySpyA(); }}
|
|
|
|
}))
|
|
|
|
.directive(
|
|
|
|
'ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function() { this.$onDestroy = $onDestroySpyB; }
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div ng-if="!ng1Destroy"><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$rootScope.ng1Destroy = false;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onDestroySpyA).not.toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).not.toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$rootScope.ng1Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onDestroySpyA).toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$onDestroySpyA.calls.reset();
|
|
|
|
$onDestroySpyB.calls.reset();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
$rootScope.ng1Destroy = false;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onDestroySpyA).not.toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).not.toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.ng2Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onDestroySpyA).toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should not call `$onDestroy()` on scope', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onDestroySpy = jasmine.createSpy('$onDestroy');
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2016-12-27 17:42:53 -05:00
|
|
|
<div *ngIf="!ng2Destroy">
|
|
|
|
<ng1-a></ng1-a> | <ng1-b></ng1-b>
|
|
|
|
</div>
|
|
|
|
`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
|
|
|
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
|
|
|
|
// on
|
|
|
|
// the queue at the end of the test, causing it to fail.
|
|
|
|
// Mocking animations (via `ngAnimateMock`) avoids the issue.
|
|
|
|
angular.module('ng1', ['ngAnimateMock'])
|
|
|
|
.directive('ng1A', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: true,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
Object.getPrototypeOf($scope).$onDestroy =
|
|
|
|
$onDestroySpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng1B', () => ({
|
|
|
|
template: '',
|
|
|
|
scope: {},
|
|
|
|
bindToController: false,
|
|
|
|
controllerAs: '$ctrl',
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
$scope['$onDestroy'] = $onDestroySpy;
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
const element = html(`<div ng-if="!ng1Destroy"><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
|
|
|
|
|
|
|
$rootScope.ng1Destroy = false;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
$rootScope.ng1Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
$rootScope.ng1Destroy = false;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
ng2ComponentInstance.ng2Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect($onDestroySpy).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('destroying the upgraded component', () => {
|
|
|
|
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
|
|
|
it('should destroy `componentScope`', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const scopeDestroyListener = jasmine.createSpy('scopeDestroyListener');
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
@Component(
|
|
|
|
{selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
|
|
|
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
|
|
|
|
// on
|
|
|
|
// the queue at the end of the test, causing it to fail.
|
|
|
|
// Mocking animations (via `ngAnimateMock`) avoids the issue.
|
|
|
|
angular.module('ng1', ['ngAnimateMock'])
|
|
|
|
.component('ng1', {
|
|
|
|
controller: function($scope: angular.IScope) {
|
|
|
|
$scope.$on('$destroy', scopeDestroyListener);
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<ng2></ng2>');
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(scopeDestroyListener).not.toHaveBeenCalled();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.ng2Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2016-12-27 17:42:53 -05:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(scopeDestroyListener).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
|
|
|
it('should emit `$destroy` on `$element` and descendants', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const elementDestroyListener = jasmine.createSpy('elementDestroyListener');
|
|
|
|
const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener');
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
@Component(
|
|
|
|
{selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
|
|
|
|
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
|
|
|
|
// on
|
|
|
|
// the queue at the end of the test, causing it to fail.
|
|
|
|
// Mocking animations (via `ngAnimateMock`) avoids the issue.
|
|
|
|
angular.module('ng1', ['ngAnimateMock'])
|
|
|
|
.component('ng1', {
|
|
|
|
controller: class {
|
|
|
|
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
|
|
|
this.$element.on !('$destroy', elementDestroyListener);
|
|
|
|
this.$element.contents !().on !('$destroy', descendantDestroyListener);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
template: '<div></div>'
|
|
|
|
})
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
imports: [BrowserModule],
|
2018-08-07 10:38:35 -04:00
|
|
|
})
|
2018-11-22 16:51:55 -05:00
|
|
|
class Ng2Module {
|
|
|
|
}
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<ng2></ng2>');
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(elementDestroyListener).not.toHaveBeenCalled();
|
|
|
|
expect(descendantDestroyListener).not.toHaveBeenCalled();
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.ng2Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(elementDestroyListener).toHaveBeenCalledTimes(1);
|
|
|
|
expect(descendantDestroyListener).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
}));
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
|
|
|
it('should clear data on `$element` and descendants', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng1ComponentElement: angular.IAugmentedJQuery;
|
|
|
|
let ng2ComponentAInstance: Ng2ComponentA;
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
2018-10-02 00:29:38 -04:00
|
|
|
controller: class {
|
|
|
|
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
2018-11-22 16:51:55 -05:00
|
|
|
this.$element.data !('test', 1);
|
|
|
|
this.$element.contents !().data !('test', 2);
|
|
|
|
|
|
|
|
ng1ComponentElement = this.$element;
|
2018-10-02 00:29:38 -04:00
|
|
|
}
|
2018-08-07 10:38:35 -04:00
|
|
|
},
|
2018-10-02 00:29:38 -04:00
|
|
|
template: '<div></div>'
|
2018-11-22 16:51:55 -05:00
|
|
|
};
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
|
|
|
|
class Ng2ComponentA {
|
|
|
|
destroyIt = false;
|
2018-08-07 10:38:35 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
constructor() { ng2ComponentAInstance = this; }
|
|
|
|
}
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
|
|
|
class Ng2ComponentB {
|
|
|
|
}
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
|
|
|
|
entryComponents: [Ng2ComponentA],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
ngDoBootstrap() {}
|
2018-10-02 00:29:38 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2-a></ng2-a>`);
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
expect(ng1ComponentElement.data !('test')).toBe(1);
|
|
|
|
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentAInstance.destroyIt = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(ng1ComponentElement.data !('test')).toBeUndefined();
|
|
|
|
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
|
|
|
it('should clear dom listeners on `$element` and descendants`', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const elementClickListener = jasmine.createSpy('elementClickListener');
|
|
|
|
const descendantClickListener = jasmine.createSpy('descendantClickListener');
|
|
|
|
let ng1DescendantElement: angular.IAugmentedJQuery;
|
|
|
|
let ng2ComponentAInstance: Ng2ComponentA;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
controller: class {
|
|
|
|
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
|
|
|
|
ng1DescendantElement = this.$element.contents !();
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
this.$element.on !('click', elementClickListener);
|
|
|
|
ng1DescendantElement.on !('click', descendantClickListener);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
template: '<div></div>'
|
|
|
|
};
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
|
|
|
|
class Ng2ComponentA {
|
|
|
|
destroyIt = false;
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
constructor() { ng2ComponentAInstance = this; }
|
2018-10-02 00:29:38 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2B', template: '<ng1></ng1>'})
|
|
|
|
class Ng2ComponentB {
|
|
|
|
}
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
|
|
|
|
entryComponents: [Ng2ComponentA],
|
|
|
|
imports: [BrowserModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
ngDoBootstrap() {}
|
|
|
|
}
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2-a></ng2-a>`);
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
|
|
|
|
const $rootScope = ref.ng1RootScope as any;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
(ng1DescendantElement[0] as HTMLElement).click();
|
|
|
|
expect(elementClickListener).toHaveBeenCalledTimes(1);
|
|
|
|
expect(descendantClickListener).toHaveBeenCalledTimes(1);
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentAInstance.destroyIt = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
2018-10-02 00:29:38 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
(ng1DescendantElement[0] as HTMLElement).click();
|
|
|
|
expect(elementClickListener).toHaveBeenCalledTimes(1);
|
|
|
|
expect(descendantClickListener).toHaveBeenCalledTimes(1);
|
|
|
|
});
|
|
|
|
}));
|
2018-08-07 10:38:35 -04:00
|
|
|
});
|
|
|
|
|
2017-05-29 13:55:41 -04:00
|
|
|
describe('linking', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should run the pre-linking after instantiating the controller', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const log: string[] = [];
|
|
|
|
|
|
|
|
// Define `ng1Directive`
|
|
|
|
const ng1Directive: angular.IDirective = {
|
|
|
|
template: '',
|
|
|
|
link: {pre: () => log.push('ng1-pre')},
|
|
|
|
controller: class {constructor() { log.push('ng1-ctrl'); }}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1', () => ng1Directive)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(() => {
|
|
|
|
setTimeout(() => expect(log).toEqual(['ng1-ctrl', 'ng1-pre']), 1000);
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should run the pre-linking function before linking', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const log: string[] = [];
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Directive`
|
|
|
|
const ng1DirectiveA: angular.IDirective = {
|
|
|
|
template: '<ng1-b></ng1-b>',
|
|
|
|
link: {pre: () => log.push('ng1A-pre')}
|
|
|
|
};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ng1DirectiveA)
|
|
|
|
.directive('ng1B', () => ng1DirectiveB)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(() => {
|
|
|
|
expect(log).toEqual(['ng1A-pre', 'ng1B-post']);
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should run the post-linking function after linking (link: object)', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const log: string[] = [];
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Directive`
|
|
|
|
const ng1DirectiveA: angular.IDirective = {
|
|
|
|
template: '<ng1-b></ng1-b>',
|
|
|
|
link: {post: () => log.push('ng1A-post')}
|
|
|
|
};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ng1DirectiveA)
|
|
|
|
.directive('ng1B', () => ng1DirectiveB)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(() => {
|
|
|
|
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should run the post-linking function after linking (link: function)', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const log: string[] = [];
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Directive`
|
|
|
|
const ng1DirectiveA: angular.IDirective = {
|
|
|
|
template: '<ng1-b></ng1-b>',
|
|
|
|
link: () => log.push('ng1A-post')
|
|
|
|
};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1A', () => ng1DirectiveA)
|
|
|
|
.directive('ng1B', () => ng1DirectiveB)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1A'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(() => {
|
|
|
|
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should run the post-linking function before `$postLink`', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const log: string[] = [];
|
|
|
|
|
|
|
|
// Define `ng1Directive`
|
|
|
|
const ng1Directive: angular.IDirective = {
|
|
|
|
template: '',
|
|
|
|
link: () => log.push('ng1-post'),
|
|
|
|
controller: class {$postLink() { log.push('ng1-$post'); }}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', [])
|
|
|
|
.directive('ng1', () => ng1Directive)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready(() => {
|
|
|
|
expect(log).toEqual(['ng1-post', 'ng1-$post']);
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('transclusion', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support single-slot transclusion', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng2ComponentAInstance: Ng2ComponentA;
|
|
|
|
let ng2ComponentBInstance: Ng2ComponentB;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'ng1(<div ng-transclude></div>)',
|
|
|
|
transclude: true
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2A',
|
|
|
|
template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)'
|
|
|
|
})
|
|
|
|
class Ng2ComponentA {
|
|
|
|
value = 'foo';
|
|
|
|
showB = false;
|
|
|
|
constructor() { ng2ComponentAInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
|
|
|
class Ng2ComponentB {
|
|
|
|
value = 'bar';
|
|
|
|
constructor() { ng2ComponentBInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations:
|
|
|
|
[adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2-a></ng2-a>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
|
|
|
|
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentAInstance.value = 'baz';
|
|
|
|
ng2ComponentAInstance.showB = true;
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentBInstance.value = 'qux';
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support single-slot transclusion with fallback content', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng1ControllerInstances: any[] = [];
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'ng1(<div ng-transclude>{{ $ctrl.value }}</div>)',
|
|
|
|
transclude: true,
|
|
|
|
controller: class {
|
|
|
|
value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); }
|
|
|
|
}
|
|
|
|
};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng2(
|
|
|
|
<ng1><div>{{ value }}</div></ng1> |
|
|
|
|
|
|
|
|
<!-- Interpolation-only content should still be detected as transcluded content. -->
|
|
|
|
<ng1>{{ value }}</ng1> |
|
|
|
|
|
|
|
|
<ng1></ng1>
|
|
|
|
)`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
value = 'from-ng2';
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(from-ng2)|ng1(from-ng2)|ng1(from-ng1))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-foo');
|
|
|
|
ng2ComponentInstance.value = 'ng2-bar';
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(ng2-bar)|ng1(ng2-bar)|ng1(ng1-foo))');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support multi-slot transclusion', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template:
|
|
|
|
'ng1(x(<div ng-transclude="slotX"></div>) | y(<div ng-transclude="slotY"></div>))',
|
|
|
|
transclude: {slotX: 'contentX', slotY: 'contentY'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng2(
|
|
|
|
<ng1>
|
|
|
|
<content-x>{{ x }}1</content-x>
|
|
|
|
<content-y>{{ y }}1</content-y>
|
|
|
|
<content-x>{{ x }}2</content-x>
|
|
|
|
<content-y>{{ y }}2</content-y>
|
|
|
|
</ng1>
|
|
|
|
)`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
x = 'foo';
|
|
|
|
y = 'bar';
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(foo1foo2)|y(bar1bar2)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.x = 'baz';
|
|
|
|
ng2ComponentInstance.y = 'qux';
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(baz1baz2)|y(qux1qux2)))');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support default slot (with fallback content)', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng1ControllerInstances: any[] = [];
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'ng1(default(<div ng-transclude="">fallback-{{ $ctrl.value }}</div>))',
|
|
|
|
transclude: {slotX: 'contentX', slotY: 'contentY'},
|
|
|
|
controller:
|
|
|
|
class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng2(
|
|
|
|
<ng1>
|
|
|
|
({{ x }})
|
|
|
|
<content-x>ignored x</content-x>
|
|
|
|
{{ x }}-<span>{{ y }}</span>
|
|
|
|
<content-y>ignored y</content-y>
|
|
|
|
<span>({{ y }})</span>
|
|
|
|
</ng1> |
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Remove any whitespace, because in AngularJS versions prior to 1.6
|
|
|
|
even whitespace counts as transcluded content.
|
|
|
|
-->
|
|
|
|
<ng1><content-x>ignored x</content-x><content-y>ignored y</content-y></ng1> |
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Interpolation-only content should still be detected as transcluded content.
|
|
|
|
-->
|
|
|
|
<ng1>{{ x }}<content-x>ignored x</content-x>{{ y + x }}<content-y>ignored y</content-y>{{ y }}</ng1>
|
|
|
|
)`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
x = 'foo';
|
|
|
|
y = 'bar';
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe(
|
|
|
|
'ng2(ng1(default((foo)foo-bar(bar)))|ng1(default(fallback-ng1))|ng1(default(foobarfoobar)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-plus');
|
|
|
|
ng2ComponentInstance.x = 'baz';
|
|
|
|
ng2ComponentInstance.y = 'qux';
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe(
|
|
|
|
'ng2(ng1(default((baz)baz-qux(qux)))|ng1(default(fallback-ng1-plus))|ng1(default(bazquxbazqux)))');
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support optional transclusion slots (with fallback content)', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let ng1ControllerInstances: any[] = [];
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng1(
|
|
|
|
x(<div ng-transclude="slotX">{{ $ctrl.x }}</div>) |
|
|
|
|
y(<div ng-transclude="slotY">{{ $ctrl.y }}</div>)
|
|
|
|
)`,
|
2018-11-22 16:51:55 -05:00
|
|
|
transclude: {slotX: '?contentX', slotY: '?contentY'},
|
|
|
|
controller: class {
|
|
|
|
x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); }
|
|
|
|
}
|
|
|
|
};
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng2(
|
|
|
|
<ng1><content-x>{{ x }}</content-x></ng1> |
|
|
|
|
<ng1><content-y>{{ y }}</content-y></ng1>
|
|
|
|
)`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
x = 'ng2X';
|
|
|
|
y = 'ng2Y';
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(ng2X)|y(ng1Y))|ng1(x(ng1X)|y(ng2Y)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1ControllerInstances.forEach(ctrl => {
|
|
|
|
ctrl.x = 'ng1X-foo';
|
|
|
|
ctrl.y = 'ng1Y-bar';
|
|
|
|
});
|
|
|
|
ng2ComponentInstance.x = 'ng2X-baz';
|
|
|
|
ng2ComponentInstance.y = 'ng2Y-qux';
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(ng2X-baz)|y(ng1Y-bar))|ng1(x(ng1X-foo)|y(ng2Y-qux)))');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should throw if a non-optional slot is not filled', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
let errorMessage: string;
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: '',
|
|
|
|
transclude: {slotX: '?contentX', slotY: 'contentY'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({selector: 'ng2', template: '<ng1></ng1>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.value($EXCEPTION_HANDLER, (error: Error) => errorMessage = error.message)
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(errorMessage)
|
|
|
|
.toContain('Required transclusion slot \'slotY\' on directive: ng1');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should support structural directives in transcluded content', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
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 `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
2017-05-29 13:55:41 -04:00
|
|
|
ng2(
|
|
|
|
<ng1>
|
|
|
|
<content-x><div *ngIf="show">{{ x }}1</div></content-x>
|
|
|
|
<div *ngIf="!show">{{ y }}1</div>
|
|
|
|
<content-x><div *ngIf="!show">{{ x }}2</div></content-x>
|
|
|
|
<div *ngIf="show">{{ y }}2</div>
|
|
|
|
</ng1>
|
|
|
|
)`
|
2018-11-22 16:51:55 -05:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
x = 'foo';
|
|
|
|
y = 'bar';
|
|
|
|
show = true;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Define `ng1Module`
|
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1Module', [])
|
|
|
|
.component('ng1', ng1Component)
|
|
|
|
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
// Define `Ng2Module`
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule],
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2Component],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(foo1)|default(bar2)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.x = 'baz';
|
|
|
|
ng2ComponentInstance.y = 'qux';
|
|
|
|
ng2ComponentInstance.show = false;
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(baz2)|default(qux1)))');
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng2ComponentInstance.show = true;
|
|
|
|
$digest(ref);
|
2017-05-29 13:55:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
expect(multiTrim(element.textContent, true))
|
|
|
|
.toBe('ng2(ng1(x(baz1)|default(qux2)))');
|
|
|
|
});
|
|
|
|
}));
|
2017-05-29 13:55:41 -04:00
|
|
|
});
|
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should bind input properties (<) of components', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1 = {
|
|
|
|
bindings: {personProfile: '<'},
|
|
|
|
template:
|
|
|
|
'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
|
|
|
|
controller: class {}
|
|
|
|
};
|
|
|
|
ng1Module.component('ng1', ng1);
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'})
|
|
|
|
class Ng2 {
|
|
|
|
goku = {firstName: 'GOKU', lastName: 'SAN'};
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`);
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('unknown') &&
|
|
|
|
it('should support ng2 > ng1 > ng2', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const ng1 = {
|
|
|
|
template: 'ng1(<ng2b></ng2b>)',
|
|
|
|
};
|
|
|
|
ng1Module.component('ng1', ng1);
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2a', template: 'ng2a(<ng1></ng1>)'})
|
|
|
|
class Ng2a {
|
|
|
|
}
|
|
|
|
ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a));
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component({selector: 'ng2b', template: 'ng2b'})
|
|
|
|
class Ng2b {
|
|
|
|
}
|
|
|
|
ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b));
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html(`<div><ng2a></ng2a></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))');
|
|
|
|
});
|
|
|
|
}));
|
2015-10-13 00:32:41 -04:00
|
|
|
});
|
|
|
|
|
2015-10-15 15:04:19 -04:00
|
|
|
describe('injection', () => {
|
|
|
|
function SomeToken() {}
|
|
|
|
|
2016-08-12 15:30:08 -04:00
|
|
|
it('should export ng2 instance to ng1', async(() => {
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({
|
|
|
|
providers: [{provide: SomeToken, useValue: 'correct_value'}],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class MyNg2Module {
|
|
|
|
}
|
2016-08-12 15:30:08 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
2016-10-10 12:18:33 -04:00
|
|
|
const module = angular.module('myExample', []);
|
2015-10-15 15:04:19 -04:00
|
|
|
module.factory('someToken', adapter.downgradeNg2Provider(SomeToken));
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(html('<div>'), ['myExample']).ready((ref) => {
|
|
|
|
expect(ref.ng1Injector.get('someToken')).toBe('correct_value');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-15 15:04:19 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should export ng1 instance to ng2', async(() => {
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({imports: [BrowserModule]})
|
|
|
|
class MyNg2Module {
|
2017-09-22 13:51:03 -04:00
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
2016-10-10 12:18:33 -04:00
|
|
|
const module = angular.module('myExample', []);
|
2015-10-15 15:04:19 -04:00
|
|
|
module.value('testValue', 'secreteToken');
|
|
|
|
adapter.upgradeNg1Provider('testValue');
|
|
|
|
adapter.upgradeNg1Provider('testValue', {asToken: 'testToken'});
|
|
|
|
adapter.upgradeNg1Provider('testValue', {asToken: String});
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(html('<div>'), ['myExample']).ready((ref) => {
|
|
|
|
expect(ref.ng2Injector.get('testValue')).toBe('secreteToken');
|
|
|
|
expect(ref.ng2Injector.get(String)).toBe('secreteToken');
|
|
|
|
expect(ref.ng2Injector.get('testToken')).toBe('secreteToken');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-15 15:04:19 -04:00
|
|
|
}));
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should respect hierarchical dependency injection for ng2', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@Component(
|
|
|
|
{selector: 'ng2-parent', template: `ng2-parent(<ng-content></ng-content>)`})
|
|
|
|
class Ng2Parent {
|
|
|
|
}
|
|
|
|
@Component({selector: 'ng2-child', template: `ng2-child`})
|
|
|
|
class Ng2Child {
|
|
|
|
constructor(parent: Ng2Parent) {}
|
|
|
|
}
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const element = html('<ng2-parent><ng2-child></ng2-child></ng2-parent>');
|
2016-11-02 18:38:00 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
ng1Module.directive('ng2Parent', adapter.downgradeNg2Component(Ng2Parent))
|
|
|
|
.directive('ng2Child', adapter.downgradeNg2Component(Ng2Child));
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('ng2-parent(ng2-child)');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2015-10-15 15:04:19 -04:00
|
|
|
});
|
|
|
|
|
2016-03-14 17:36:41 -04:00
|
|
|
describe('testability', () => {
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should handle deferred bootstrap', async(() => {
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({imports: [BrowserModule]})
|
|
|
|
class MyNg2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
2016-08-23 13:52:40 -04:00
|
|
|
angular.module('ng1', []);
|
2016-10-10 12:18:33 -04:00
|
|
|
let bootstrapResumed: boolean = false;
|
2016-03-14 17:36:41 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html('<div></div>');
|
2016-03-14 17:36:41 -04:00
|
|
|
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
|
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(bootstrapResumed).toEqual(true);
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2016-03-14 17:36:41 -04:00
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
bootstrapResumed = true;
|
2016-04-28 20:50:03 -04:00
|
|
|
(<any>window).angular.resumeBootstrap();
|
2016-03-14 17:36:41 -04:00
|
|
|
}, 100);
|
|
|
|
}));
|
|
|
|
|
2018-03-14 03:17:09 -04:00
|
|
|
it('should propagate return value of resumeBootstrap', fakeAsync(() => {
|
|
|
|
@NgModule({imports: [BrowserModule]})
|
|
|
|
class MyNg2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
let a1Injector: angular.IInjectorService|undefined;
|
|
|
|
ng1Module.run([
|
|
|
|
'$injector', function($injector: angular.IInjectorService) { a1Injector = $injector; }
|
|
|
|
]);
|
|
|
|
|
|
|
|
const element = html('<div></div>');
|
|
|
|
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
|
|
|
|
|
|
|
|
adapter.bootstrap(element, [ng1Module.name]).ready((ref) => { ref.dispose(); });
|
|
|
|
|
|
|
|
tick(100);
|
|
|
|
|
|
|
|
const value = (<any>window).angular.resumeBootstrap();
|
|
|
|
expect(value).toBe(a1Injector);
|
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should wait for ng2 testability', async(() => {
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({imports: [BrowserModule]})
|
|
|
|
class MyNg2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
2016-08-23 13:52:40 -04:00
|
|
|
angular.module('ng1', []);
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html('<div></div>');
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng2Testability: Testability = ref.ng2Injector.get(Testability);
|
2016-06-08 19:38:52 -04:00
|
|
|
ng2Testability.increasePendingRequestCount();
|
2016-10-10 12:18:33 -04:00
|
|
|
let ng2Stable = false;
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
angular.getTestability(element).whenStable(() => {
|
2016-06-08 19:38:52 -04:00
|
|
|
expect(ng2Stable).toEqual(true);
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
ng2Stable = true;
|
|
|
|
ng2Testability.decreasePendingRequestCount();
|
|
|
|
}, 100);
|
|
|
|
});
|
2016-03-14 17:36:41 -04:00
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
describe('examples', () => {
|
2018-11-22 16:51:55 -05:00
|
|
|
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
|
|
|
it('should verify UpgradeAdapter example', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const module = angular.module('myExample', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
|
|
|
scope: {title: '='},
|
|
|
|
transclude: true,
|
|
|
|
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
module.directive('ng1', ng1);
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
inputs: ['name'],
|
|
|
|
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2018-11-22 16:51:55 -05:00
|
|
|
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual('ng2[ng1[Hello World!](transclude)](project)');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2015-10-04 12:33:20 -04:00
|
|
|
});
|
2016-12-16 18:14:16 -05:00
|
|
|
|
|
|
|
describe('registerForNg1Tests', () => {
|
|
|
|
let upgradeAdapterRef: UpgradeAdapterRef;
|
|
|
|
let $compile: angular.ICompileService;
|
|
|
|
let $rootScope: angular.IRootScopeService;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: 'Hello World',
|
|
|
|
})
|
|
|
|
class Ng2 {
|
|
|
|
}
|
2016-12-16 18:14:16 -05:00
|
|
|
|
2017-08-08 17:03:27 -04:00
|
|
|
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-12-16 18:14:16 -05:00
|
|
|
|
|
|
|
const upgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
ng1Module.directive('ng2', upgradeAdapter.downgradeNg2Component(Ng2));
|
|
|
|
|
|
|
|
upgradeAdapterRef = upgradeAdapter.registerForNg1Tests(['ng1']);
|
|
|
|
});
|
|
|
|
|
2018-02-15 12:21:18 -05:00
|
|
|
beforeEach(() => {
|
|
|
|
inject((_$compile_: angular.ICompileService, _$rootScope_: angular.IRootScopeService) => {
|
|
|
|
$compile = _$compile_;
|
|
|
|
$rootScope = _$rootScope_;
|
|
|
|
});
|
|
|
|
});
|
2016-12-16 18:14:16 -05:00
|
|
|
|
|
|
|
it('should be able to test ng1 components that use ng2 components', async(() => {
|
|
|
|
upgradeAdapterRef.ready(() => {
|
|
|
|
const element = $compile('<ng2></ng2>')($rootScope);
|
|
|
|
$rootScope.$digest();
|
|
|
|
expect(element[0].textContent).toContain('Hello World');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
2015-08-06 16:19:29 -04:00
|
|
|
});
|
2018-02-15 12:21:18 -05:00
|
|
|
});
|