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
|
|
|
|
*/
|
|
|
|
|
2017-05-03 11:18:40 -04:00
|
|
|
import {ChangeDetectorRef, Class, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgZone, OnChanges, 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';
|
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';
|
2017-01-19 06:04:24 -05:00
|
|
|
import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter';
|
2017-04-18 09:49:35 -04:00
|
|
|
import {$digest, html, multiTrim} from './test_helpers';
|
2015-08-05 13:32:14 -04:00
|
|
|
|
|
|
|
export function main() {
|
2015-10-13 00:32:41 -04:00
|
|
|
describe('adapter: ng1 to ng2', () => {
|
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
|
|
|
|
|
|
|
it('should instantiate ng2 in ng1 template and project content', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'NG2' }}(<ng-content></ng-content>)`,
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
|
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
const element =
|
|
|
|
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
|
|
|
|
|
|
|
|
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();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should instantiate ng1 in ng2 template and project content', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
|
|
|
|
}).Class({constructor: function Ng2() {}});
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function Ng2Module() {}});
|
|
|
|
|
|
|
|
ng1Module.directive('ng1', () => {
|
|
|
|
return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'};
|
|
|
|
});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
|
|
|
|
const element = html('<div>{{\'ng1(\'}}<ng2></ng2>{{\')\'}}</div>');
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('supports the compilerOptions argument', async(() => {
|
|
|
|
const platformRef = platformBrowserDynamic();
|
|
|
|
spyOn(platformRef, '_bootstrapModuleWithZone').and.callThrough();
|
|
|
|
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'NG2' }}(<ng-content></ng-content>)`
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
const element =
|
|
|
|
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
|
|
|
|
|
|
|
|
const Ng2AppModule =
|
|
|
|
NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function Ng2AppModule() {}, ngDoBootstrap: function() {}});
|
|
|
|
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2AppModule, {providers: []});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect((platformRef as any)._bootstrapModuleWithZone)
|
2017-01-19 06:04:24 -05:00
|
|
|
.toHaveBeenCalledWith(jasmine.any(Function), {providers: []}, jasmine.any(Object));
|
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
|
|
|
|
2016-10-27 13:40:02 -04:00
|
|
|
const ng2Component = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `<BAD TEMPLATE div></div>`,
|
|
|
|
}).Class({constructor: function() {}});
|
2016-10-26 22:24:47 -04:00
|
|
|
|
2016-10-27 13:40:02 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [ng2Component],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-10-26 22:24:47 -04:00
|
|
|
|
|
|
|
adapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should throw an uncaught error', fakeAsync(() => {
|
|
|
|
const resolveSpy = jasmine.createSpy('resolveSpy');
|
2016-11-22 16:29:53 -05:00
|
|
|
spyOn(console, 'error');
|
2016-10-26 22:24:47 -04:00
|
|
|
|
|
|
|
expect(() => {
|
2016-10-27 13:40:02 -04:00
|
|
|
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']).ready(resolveSpy);
|
2016-10-26 22:24:47 -04:00
|
|
|
flushMicrotasks();
|
|
|
|
}).toThrowError();
|
|
|
|
expect(resolveSpy).not.toHaveBeenCalled();
|
|
|
|
}));
|
|
|
|
|
2016-11-22 16:29:53 -05:00
|
|
|
it('should output an error message to the console and re-throw', fakeAsync(() => {
|
2017-01-13 15:14:58 -05:00
|
|
|
const consoleErrorSpy: jasmine.Spy = spyOn(console, 'error');
|
2016-10-26 22:24:47 -04:00
|
|
|
expect(() => {
|
2016-10-27 13:40:02 -04:00
|
|
|
adapter.bootstrap(html('<ng2></ng2>'), ['ng1']);
|
2016-10-26 22:24:47 -04:00
|
|
|
flushMicrotasks();
|
|
|
|
}).toThrowError();
|
2017-01-13 15:14:58 -05:00
|
|
|
const args: any[] = consoleErrorSpy.calls.mostRecent().args;
|
2016-12-15 16:07:12 -05:00
|
|
|
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', () => {
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should interleave scope and component expressions', async(() => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-10-27 13:40:02 -04:00
|
|
|
const log: string[] = [];
|
|
|
|
const l = (value: string) => {
|
2015-09-30 18:42:02 -04:00
|
|
|
log.push(value);
|
|
|
|
return value + ';';
|
|
|
|
};
|
|
|
|
|
2016-10-04 18:57:37 -04:00
|
|
|
ng1Module.directive('ng1a', () => ({template: '{{ l(\'ng1a\') }}'}));
|
|
|
|
ng1Module.directive('ng1b', () => ({template: '{{ l(\'ng1b\') }}'}));
|
2016-10-27 13:40:02 -04:00
|
|
|
ng1Module.run(($rootScope: any) => {
|
2015-09-30 18:42:02 -04:00
|
|
|
$rootScope.l = l;
|
|
|
|
$rootScope.reset = () => log.length = 0;
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
|
|
|
|
}).Class({constructor: function() { this.l = l; }});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module =
|
2016-08-19 16:51:45 -04:00
|
|
|
NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2
|
|
|
|
],
|
2016-08-23 13:52:40 -04:00
|
|
|
imports: [BrowserModule],
|
2016-08-19 16:51:45 -04:00
|
|
|
}).Class({constructor: function() {}});
|
2015-10-13 00:32:41 -04:00
|
|
|
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2015-09-30 18:42:02 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const element =
|
2016-06-08 19:38:52 -04:00
|
|
|
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
|
2016-11-02 18:38:00 -04:00
|
|
|
expect(log).toEqual(['1A', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']);
|
2016-06-08 19:38:52 -04:00
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-09-30 18:42:02 -04:00
|
|
|
}));
|
2017-01-13 15:19:57 -05:00
|
|
|
|
|
|
|
|
|
|
|
it('should propagate changes to a downgraded component inside the ngZone', async(() => {
|
|
|
|
let appComponent: AppComponent;
|
|
|
|
let upgradeRef: UpgradeAdapterRef;
|
|
|
|
|
|
|
|
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
|
|
|
class AppComponent {
|
|
|
|
value: number;
|
|
|
|
constructor() { appComponent = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'my-child',
|
|
|
|
template: '<div>{{valueFromPromise}}',
|
|
|
|
})
|
|
|
|
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;
|
|
|
|
|
|
|
|
this.zone.onMicrotaskEmpty.subscribe(() => {
|
|
|
|
expect(element.textContent).toEqual('5');
|
|
|
|
upgradeRef.dispose();
|
|
|
|
});
|
|
|
|
|
|
|
|
Promise.resolve().then(() => this.valueFromPromise = changes['value'].currentValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({declarations: [AppComponent, ChildComponent], 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) => {
|
|
|
|
upgradeRef = ref;
|
|
|
|
appComponent.value = 5;
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
// This test demonstrates https://github.com/angular/angular/issues/6385
|
|
|
|
// which was invalidly fixed by https://github.com/angular/angular/pull/6386
|
|
|
|
// 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
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should bind properties, events', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2017-02-04 10:19:09 -05:00
|
|
|
const ng1Module =
|
|
|
|
angular.module('ng1', []).value('$exceptionHandler', (err: any) => { throw err; });
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2016-10-27 13:40:02 -04:00
|
|
|
ng1Module.run(($rootScope: any) => {
|
2017-02-04 10:19:09 -05:00
|
|
|
$rootScope.name = 'world';
|
2015-10-01 16:14:59 -04:00
|
|
|
$rootScope.dataA = 'A';
|
|
|
|
$rootScope.dataB = 'B';
|
|
|
|
$rootScope.modelA = 'initModelA';
|
|
|
|
$rootScope.modelB = 'initModelB';
|
|
|
|
$rootScope.eventA = '?';
|
|
|
|
$rootScope.eventB = '?';
|
|
|
|
});
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = 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({
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor: function() {
|
|
|
|
this.ngOnChangesCount = 0;
|
|
|
|
this.ignore = '-';
|
|
|
|
this.literal = '?';
|
|
|
|
this.interpolate = '?';
|
|
|
|
this.oneWayA = '?';
|
|
|
|
this.oneWayB = '?';
|
|
|
|
this.twoWayA = '?';
|
|
|
|
this.twoWayB = '?';
|
|
|
|
this.eventA = new EventEmitter();
|
|
|
|
this.eventB = new EventEmitter();
|
|
|
|
this.twoWayAEmitter = new EventEmitter();
|
|
|
|
this.twoWayBEmitter = new EventEmitter();
|
|
|
|
},
|
2016-10-27 13:40:02 -04:00
|
|
|
ngOnChanges: function(changes: SimpleChanges) {
|
|
|
|
const assert = (prop: string, value: any) => {
|
2016-06-08 19:38:52 -04:00
|
|
|
if (this[prop] != value) {
|
|
|
|
throw new Error(`Expected: '${prop}' to be '${value}' but was '${this[prop]}'`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-10-27 13:40:02 -04:00
|
|
|
const assertChange = (prop: string, value: any) => {
|
2016-06-08 19:38:52 -04:00
|
|
|
assert(prop, value);
|
|
|
|
if (!changes[prop]) {
|
|
|
|
throw new Error(`Changes record for '${prop}' not found.`);
|
|
|
|
}
|
2016-10-10 12:18:33 -04:00
|
|
|
const actValue = changes[prop].currentValue;
|
2016-06-08 19:38:52 -04:00
|
|
|
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');
|
2017-02-04 10:19:09 -05:00
|
|
|
assertChange('twoWayB', 'newB');
|
2016-06-08 19:38:52 -04:00
|
|
|
break;
|
|
|
|
case 2:
|
2017-02-04 10:19:09 -05:00
|
|
|
assertChange('interpolate', 'Hello everyone');
|
2016-06-08 19:38:52 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04: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>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
2017-03-24 12:59:18 -04:00
|
|
|
expect(multiTrim(document.body.textContent !))
|
2016-06-08 19:38:52 -04:00
|
|
|
.toEqual(
|
|
|
|
'ignore: -; ' +
|
|
|
|
'literal: Text; interpolate: Hello world; ' +
|
|
|
|
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
|
2016-07-21 20:12:00 -04:00
|
|
|
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
2017-02-04 10:19:09 -05:00
|
|
|
|
|
|
|
ref.ng1RootScope.$apply('name = "everyone"');
|
2017-03-24 12:59:18 -04:00
|
|
|
expect(multiTrim(document.body.textContent !))
|
2017-02-04 10:19:09 -05:00
|
|
|
.toEqual(
|
|
|
|
'ignore: -; ' +
|
|
|
|
'literal: Text; interpolate: Hello everyone; ' +
|
|
|
|
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' +
|
|
|
|
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-01 16:14:59 -04:00
|
|
|
|
|
|
|
}));
|
2016-01-13 09:35:48 -05:00
|
|
|
|
2017-05-03 11:18:40 -04:00
|
|
|
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
|
|
|
ngOnChangesCount: {{ ngOnChangesCount }} |
|
|
|
|
firstChangesCount: {{ firstChangesCount }} |
|
|
|
|
initialValue: {{ initialValue }}`
|
|
|
|
})
|
|
|
|
class Ng2Component implements OnChanges {
|
|
|
|
ngOnChangesCount = 0;
|
|
|
|
firstChangesCount = 0;
|
|
|
|
initialValue: string;
|
|
|
|
@Input() foo: string;
|
|
|
|
|
|
|
|
ngOnChanges(changes: SimpleChanges) {
|
|
|
|
this.ngOnChangesCount++;
|
|
|
|
|
|
|
|
if (this.ngOnChangesCount === 1) {
|
|
|
|
this.initialValue = this.foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
|
|
|
this.firstChangesCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
const ng1Module = angular.module('ng1', []).directive(
|
|
|
|
'ng2', adapter.downgradeNg2Component(Ng2Component));
|
|
|
|
|
|
|
|
const element = html(`
|
|
|
|
<ng2 [foo]="'foo'"></ng2>
|
|
|
|
<ng2 foo="bar"></ng2>
|
|
|
|
<ng2 [foo]="'baz'" ng-if="true"></ng2>
|
|
|
|
<ng2 foo="qux" ng-if="true"></ng2>
|
|
|
|
`);
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready(ref => {
|
|
|
|
const nodes = element.querySelectorAll('ng2');
|
|
|
|
const expectedTextWith = (value: string) =>
|
|
|
|
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
|
|
|
|
|
|
|
expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo'));
|
|
|
|
expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar'));
|
|
|
|
expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz'));
|
|
|
|
expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux'));
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
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 = () => {};
|
|
|
|
constructor() { ng2Instance = this; }
|
|
|
|
writeValue(value: any) { this._value = value; }
|
|
|
|
registerOnChange(fn: any) { this._onChangeCallback = fn; }
|
|
|
|
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>`);
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
schemas: [NO_ERRORS_SCHEMA],
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: 'test'}).Class({
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor: function() {},
|
|
|
|
ngOnDestroy: function() { onDestroyed.emit('destroyed'); }
|
|
|
|
});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 =
|
2016-06-08 19:38:52 -04:00
|
|
|
Component({selector: 'ng2', template: 'test'}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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
|
|
|
|
|
|
|
it('should support multi-slot projection', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
|
|
|
'2b(<ng-content select=".ng1b"></ng-content>)'
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
|
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
});
|
|
|
|
}));
|
2017-01-13 15:14:58 -05:00
|
|
|
|
2017-02-03 05:11:16 -05:00
|
|
|
it('should correctly project structural directives', async(() => {
|
|
|
|
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
|
|
|
class Ng2Component {
|
|
|
|
@Input() itemId: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({imports: [BrowserModule], declarations: [Ng2Component]})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
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(`
|
|
|
|
<ng2 ng-repeat="item in items" [item-id]="item.id">
|
|
|
|
<div ng-repeat="subitem in item.subitems">{{ subitem }}</div>
|
|
|
|
</ng2>
|
|
|
|
`);
|
|
|
|
|
|
|
|
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-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', () => {
|
2017-04-18 09:49:35 -04:00
|
|
|
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: `
|
|
|
|
<ng1 inputAttrA="{{ dataA }}" inputB="{{ dataB }}"></ng1>
|
|
|
|
| Outside: {{ dataA }}, {{ dataB }}
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = 'foo';
|
|
|
|
dataB = 'bar';
|
|
|
|
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng1Controller.inputA = 'baz';
|
|
|
|
ng1Controller.inputB = 'qux';
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: baz, qux | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng2ComponentInstance.dataA = 'foo2';
|
|
|
|
ng2ComponentInstance.dataB = 'bar2';
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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.value }}, {{ $ctrl.inputB.value }}',
|
|
|
|
bindings: {inputA: '<inputAttrA', inputB: '<'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
|
|
|
<ng1 [inputAttrA]="dataA" [inputB]="dataB"></ng1>
|
|
|
|
| Outside: {{ dataA.value }}, {{ dataB.value }}
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = {value: 'foo'};
|
|
|
|
dataB = {value: 'bar'};
|
|
|
|
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng1Controller.inputA = {value: 'baz'};
|
|
|
|
ng1Controller.inputB = {value: 'qux'};
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: baz, qux | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng2ComponentInstance.dataA = {value: 'foo2'};
|
|
|
|
ng2ComponentInstance.dataB = {value: 'bar2'};
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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.value }}, {{ $ctrl.inputB.value }}',
|
|
|
|
bindings: {inputA: '=inputAttrA', inputB: '='}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
|
|
|
<ng1 [(inputAttrA)]="dataA" [(inputB)]="dataB"></ng1>
|
|
|
|
| Outside: {{ dataA.value }}, {{ dataB.value }}
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = {value: 'foo'};
|
|
|
|
dataB = {value: 'bar'};
|
|
|
|
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: foo, bar | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng1Controller.inputA = {value: 'baz'};
|
|
|
|
ng1Controller.inputB = {value: 'qux'};
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: baz, qux | Outside: baz, qux');
|
|
|
|
|
|
|
|
ng2ComponentInstance.dataA = {value: 'foo2'};
|
|
|
|
ng2ComponentInstance.dataB = {value: 'bar2'};
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent))
|
|
|
|
.toBe('Inside: foo2, bar2 | Outside: foo2, bar2');
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should support `&` bindings', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
|
|
|
|
// Define `ng1Component`
|
|
|
|
const ng1Component: angular.IComponent = {
|
|
|
|
template: 'Inside: -',
|
|
|
|
bindings: {outputA: '&outputAttrA', outputB: '&'}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Define `Ng2Component`
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
|
|
|
<ng1 (outputAttrA)="dataA = $event" (outputB)="dataB = $event"></ng1>
|
|
|
|
| Outside: {{ dataA }}, {{ dataB }}
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
dataA = 'foo';
|
|
|
|
dataB = 'bar';
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bootstrap
|
|
|
|
const element = html(`<ng2></ng2>`);
|
|
|
|
|
|
|
|
adapter.bootstrap(element, ['ng1Module']).ready(ref => {
|
|
|
|
const ng1 = element.querySelector('ng1') !;
|
|
|
|
const ng1Controller = angular.element(ng1).controller !('ng1');
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: foo, bar');
|
|
|
|
|
|
|
|
ng1Controller.outputA('baz');
|
|
|
|
ng1Controller.outputB('qux');
|
|
|
|
$digest(ref);
|
|
|
|
|
|
|
|
expect(multiTrim(element.textContent)).toBe('Inside: - | Outside: baz, qux');
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should bind properties, events', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-09-29 12:45:28 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2016-09-29 12:45:28 -04:00
|
|
|
const ng1 = () => {
|
2015-10-04 12:33:20 -04:00
|
|
|
return {
|
2017-04-18 09:49:35 -04:00
|
|
|
template: 'Hello {{fullName}}; A: {{modelA}}; B: {{modelB}}; C: {{modelC}}; | ',
|
2016-09-29 12:45:28 -04:00
|
|
|
scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', modelC: '=', event: '&'},
|
2016-10-27 13:40:02 -04:00
|
|
|
link: function(scope: any) {
|
2017-04-18 09:49:35 -04:00
|
|
|
scope.$watch('modelB', (v: string) => {
|
2015-10-11 14:18:11 -04:00
|
|
|
if (v == 'Savkin') {
|
2017-04-18 09:49:35 -04:00
|
|
|
scope.modelB = 'SAVKIN';
|
2015-10-11 14:18:11 -04:00
|
|
|
scope.event('WORKS');
|
|
|
|
|
2015-12-16 02:47:48 -05:00
|
|
|
// Should not update because [model-a] is uni directional
|
2017-04-18 09:49:35 -04:00
|
|
|
scope.modelA = 'VICTOR';
|
2015-10-04 12:33:20 -04:00
|
|
|
}
|
2016-07-21 20:12:00 -04:00
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}
|
|
|
|
};
|
2015-10-04 12:33:20 -04:00
|
|
|
};
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-09-29 12:45:28 -04:00
|
|
|
const Ng2 =
|
2015-10-13 00:32:41 -04:00
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
2017-04-18 09:49:35 -04:00
|
|
|
'<ng1 fullName="{{last}}, {{first}}, {{city}}" [dataA]="first" [(dataB)]="last" [modelC]="city" ' +
|
2016-06-08 19:38:52 -04:00
|
|
|
'(event)="event=$event"></ng1>' +
|
2017-04-18 09:49:35 -04:00
|
|
|
'<ng1 fullName="{{\'TEST\'}}" dataA="First" dataB="Last" modelC="City"></ng1>' +
|
2016-09-29 12:45:28 -04:00
|
|
|
'{{event}}-{{last}}, {{first}}, {{city}}'
|
2016-06-08 19:38:52 -04:00
|
|
|
}).Class({
|
|
|
|
constructor: function() {
|
|
|
|
this.first = 'Victor';
|
|
|
|
this.last = 'Savkin';
|
2016-09-29 12:45:28 -04:00
|
|
|
this.city = 'SF';
|
2016-06-08 19:38:52 -04:00
|
|
|
this.event = '?';
|
|
|
|
}
|
|
|
|
});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-09-29 12:45:28 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
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);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should bind optional properties', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const ng1 = () => {
|
|
|
|
return {
|
2017-04-18 09:49:35 -04:00
|
|
|
template: 'Hello; A: {{modelA}}; B: {{modelB}}; | ',
|
2016-09-29 12:45:28 -04:00
|
|
|
scope: {modelA: '=?dataA', modelB: '=?'}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
2017-04-18 09:49:35 -04:00
|
|
|
template: '<ng1 [dataA]="first" [modelB]="last"></ng1>' +
|
|
|
|
'<ng1 dataA="First" modelB="Last"></ng1>' +
|
2016-09-29 12:45:28 -04:00
|
|
|
'<ng1></ng1>' +
|
|
|
|
'<ng1></ng1>'
|
|
|
|
}).Class({
|
|
|
|
constructor: function() {
|
|
|
|
this.first = 'Victor';
|
|
|
|
this.last = 'Savkin';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-09-29 12:45:28 -04:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
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(
|
2016-09-29 12:45:28 -04:00
|
|
|
'Hello; A: Victor; B: Savkin; | Hello; A: First; B: Last; | Hello; A: ; B: ; | Hello; A: ; B: ; |');
|
2016-06-08 19:38:52 -04:00
|
|
|
ref.dispose();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should bind properties, events in controller when bindToController is not used',
|
2016-08-19 16:51:45 -04:00
|
|
|
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
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
template: '{{someText}} - Length: {{data.length}}',
|
|
|
|
scope: {data: '='},
|
2016-10-27 13:40:02 -04:00
|
|
|
controller: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
2016-06-08 19:38:52 -04:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 =
|
2016-06-08 19:38:52 -04:00
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
2016-08-19 16:51:45 -04:00
|
|
|
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
|
2016-06-08 19:38:52 -04:00
|
|
|
}).Class({
|
|
|
|
|
|
|
|
constructor: function() {
|
|
|
|
this.dataList = [1, 2, 3];
|
2016-07-21 20:12:00 -04:00
|
|
|
this.someText = 'ng2';
|
2016-06-08 19:38:52 -04:00
|
|
|
}
|
2016-04-12 12:40:37 -04:00
|
|
|
});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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(`<div><ng2></ng2></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
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);
|
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
}));
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should bind properties, events in link function', 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
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
template: '{{someText}} - Length: {{data.length}}',
|
|
|
|
scope: {data: '='},
|
2016-10-27 13:40:02 -04:00
|
|
|
link: function($scope: any) { $scope.someText = 'ng1 - Data: ' + $scope.data; }
|
2016-06-08 19:38:52 -04:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 =
|
2016-06-08 19:38:52 -04:00
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
2016-08-19 16:51:45 -04:00
|
|
|
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
|
2016-06-08 19:38:52 -04:00
|
|
|
}).Class({
|
|
|
|
|
|
|
|
constructor: function() {
|
|
|
|
this.dataList = [1, 2, 3];
|
2016-07-21 20:12:00 -04:00
|
|
|
this.someText = 'ng2';
|
2016-06-08 19:38:52 -04:00
|
|
|
}
|
|
|
|
});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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(`<div><ng2></ng2></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
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-05-17 17:53:59 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support templateUrl fetched from $httpBackend', 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.value(
|
2016-10-27 13:40:02 -04:00
|
|
|
'$httpBackend', (method: string, url: string, post: any, cbFn: Function) => {
|
|
|
|
cbFn(200, `${method}:${url}`);
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
2015-10-11 14:18:11 -04:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -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('GET:url.html');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support templateUrl 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-08 19:38:52 -04:00
|
|
|
ng1Module.value(
|
2016-10-27 13:40:02 -04:00
|
|
|
'$httpBackend', (method: string, url: string, post: any, cbFn: Function) => {
|
|
|
|
cbFn(200, `${method}:${url}`);
|
|
|
|
});
|
2016-06-04 22:53:51 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => { return {templateUrl() { return 'url.html'; }}; };
|
2016-06-04 22:53:51 -04:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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('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
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support templateUrl fetched from $templateCache', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-10-27 13:40:02 -04:00
|
|
|
ng1Module.run(($templateCache: any) => $templateCache.put('url.html', 'WORKS'));
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => { return {templateUrl: 'url.html'}; };
|
2015-10-11 14:18:11 -04:00
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -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('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support controller with controllerAs', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2015-10-11 14:18:11 -04:00
|
|
|
return {
|
|
|
|
scope: true,
|
|
|
|
template:
|
|
|
|
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: Class({
|
2016-10-27 13:40:02 -04:00
|
|
|
constructor: function($scope: any, $element: any) {
|
2015-10-11 14:18:11 -04:00
|
|
|
(<any>this).verifyIAmAClass();
|
|
|
|
this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope';
|
|
|
|
this.hasElement = $element[0].nodeName;
|
|
|
|
this.$element = $element;
|
|
|
|
},
|
|
|
|
verifyIAmAClass: function() { this.isClass = 'isClass'; },
|
|
|
|
isPublished: function() {
|
|
|
|
return this.$element.controller('ng1') == this ? 'published' : 'not-published';
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -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('scope; isClass; NG1; published');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support bindToController', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2015-10-11 14:18:11 -04:00
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
2015-10-28 03:59:19 -04:00
|
|
|
bindToController: true,
|
|
|
|
template: '{{ctl.title}}',
|
2015-10-11 14:18:11 -04:00
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: Class({constructor: function() {}})
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-11-13 12:55:40 -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('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-11-13 12:55:40 -05:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support bindToController with bindings', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-11-13 12:55:40 -05:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2015-11-13 12:55:40 -05:00
|
|
|
return {
|
|
|
|
scope: {},
|
|
|
|
bindToController: {title: '@'},
|
|
|
|
template: '{{ctl.title}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: Class({constructor: function() {}})
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -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('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support single require in linking fn', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-27 13:40:02 -04:00
|
|
|
const ng1 = ($rootScope: any) => {
|
2015-10-11 14:18:11 -04:00
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
2015-10-28 03:59:19 -04:00
|
|
|
bindToController: true,
|
|
|
|
template: '{{ctl.status}}',
|
2015-10-11 14:18:11 -04:00
|
|
|
require: 'ng1',
|
2015-10-13 18:10:20 -04:00
|
|
|
controllerAs: 'ctrl',
|
2015-10-11 14:18:11 -04:00
|
|
|
controller: Class({constructor: function() { this.status = 'WORKS'; }}),
|
2016-10-27 13:40:02 -04:00
|
|
|
link: function(scope: any, element: any, attrs: any, linkController: any) {
|
2015-10-11 14:18:11 -04:00
|
|
|
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
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -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('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2015-10-11 14:18:11 -04:00
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support array require in linking fn', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2015-10-11 14:18:11 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const parent = () => {
|
2015-10-11 14:18:11 -04:00
|
|
|
return {controller: Class({constructor: function() { this.parent = 'PARENT'; }})};
|
|
|
|
};
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2015-10-11 14:18:11 -04:00
|
|
|
return {
|
|
|
|
scope: {title: '@'},
|
2015-10-28 03:59:19 -04:00
|
|
|
bindToController: true,
|
|
|
|
template: '{{parent.parent}}:{{ng1.status}}',
|
2015-10-11 14:18:11 -04:00
|
|
|
require: ['ng1', '^parent', '?^^notFound'],
|
2015-10-13 18:10:20 -04:00
|
|
|
controllerAs: 'ctrl',
|
2015-10-11 14:18:11 -04:00
|
|
|
controller: Class({constructor: function() { this.status = 'WORKS'; }}),
|
2016-10-27 13:40:02 -04:00
|
|
|
link: function(scope: any, element: any, attrs: any, linkControllers: any) {
|
2015-10-11 14:18:11 -04:00
|
|
|
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);
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
|
2016-08-19 16:51:45 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-11 14:18:11 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html(`<div><parent><ng2></ng2></parent></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
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', () => {
|
|
|
|
it('should call `$onInit()` on controller', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onInitSpyA = jasmine.createSpy('$onInitA');
|
|
|
|
const $onInitSpyB = jasmine.createSpy('$onInitB');
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-12-27 17:42:53 -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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($onInitSpy).not.toHaveBeenCalled();
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
$doCheckSpyA.calls.reset();
|
|
|
|
$doCheckSpyB.calls.reset();
|
|
|
|
changeDetector.detectChanges();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
expect($doCheckSpyA).toHaveBeenCalled();
|
|
|
|
expect($doCheckSpyB).toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
expect($doCheckSpyA).not.toHaveBeenCalled();
|
|
|
|
expect($doCheckSpyB).not.toHaveBeenCalled();
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
@Component({selector: 'ng2', template: '<ng1-a></ng1-a> | <ng1-b></ng1-b>'})
|
|
|
|
class Ng2Component {
|
|
|
|
}
|
2016-11-18 16:46:49 -05:00
|
|
|
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
|
|
|
|
|
|
|
const element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect($postLinkSpy).not.toHaveBeenCalled();
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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
|
|
|
|
2016-12-27 17:42:53 -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;
|
|
|
|
});
|
2016-11-22 12:21:03 -05:00
|
|
|
|
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
@NgModule({
|
|
|
|
declarations: [
|
|
|
|
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
|
|
|
Ng2Component
|
|
|
|
],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
}
|
2016-11-22 12:21:03 -05:00
|
|
|
|
2016-12-27 17:42:53 -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
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
|
|
|
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valA: jasmine.any(SimpleChange)
|
|
|
|
});
|
|
|
|
|
|
|
|
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
|
|
|
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valB: jasmine.any(SimpleChange)
|
|
|
|
});
|
|
|
|
|
|
|
|
$onChangesControllerSpyA.calls.reset();
|
|
|
|
$onChangesControllerSpyB.calls.reset();
|
|
|
|
$onChangesScopeSpy.calls.reset();
|
|
|
|
|
|
|
|
// `$onChanges()` call after a change
|
|
|
|
ng2Instance.val = 'new value';
|
|
|
|
tick();
|
|
|
|
ref.ng1RootScope.$digest();
|
2016-12-12 12:29:23 -05:00
|
|
|
|
2016-12-27 17:42:53 -05:00
|
|
|
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
|
|
|
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valA: jasmine.objectContaining({currentValue: 'new value'})
|
|
|
|
});
|
|
|
|
|
|
|
|
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
|
|
|
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
|
|
|
valB: jasmine.objectContaining({currentValue: 'new value'})
|
|
|
|
});
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
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: `
|
|
|
|
<div *ngIf="!ng2Destroy">
|
|
|
|
<ng1-a></ng1-a> | <ng1-b></ng1-b>
|
|
|
|
</div>
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
2016-12-12 12:29:23 -05:00
|
|
|
}
|
2016-12-27 17:42:53 -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));
|
|
|
|
|
|
|
|
@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();
|
|
|
|
|
|
|
|
expect($onDestroySpyA).not.toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
$rootScope.ng1Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
expect($onDestroySpyA).toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).toHaveBeenCalled();
|
|
|
|
|
|
|
|
$onDestroySpyA.calls.reset();
|
|
|
|
$onDestroySpyB.calls.reset();
|
|
|
|
|
|
|
|
$rootScope.ng1Destroy = false;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
expect($onDestroySpyA).not.toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
ng2ComponentInstance.ng2Destroy = true;
|
|
|
|
tick();
|
|
|
|
$rootScope.$digest();
|
|
|
|
|
|
|
|
expect($onDestroySpyA).toHaveBeenCalled();
|
|
|
|
expect($onDestroySpyB).toHaveBeenCalled();
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should not call `$onDestroy()` on scope', fakeAsync(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
|
|
|
const $onDestroySpy = jasmine.createSpy('$onDestroy');
|
|
|
|
let ng2ComponentInstance: Ng2Component;
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `
|
|
|
|
<div *ngIf="!ng2Destroy">
|
|
|
|
<ng1-a></ng1-a> | <ng1-b></ng1-b>
|
|
|
|
</div>
|
|
|
|
`
|
|
|
|
})
|
|
|
|
class Ng2Component {
|
|
|
|
ng2Destroy: boolean = false;
|
|
|
|
constructor() { ng2ComponentInstance = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
expect($onDestroySpy).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
ref.dispose();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should bind input properties (<) of components', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-03-14 02:51:04 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = {
|
2016-03-14 02:51:04 -04:00
|
|
|
bindings: {personProfile: '<'},
|
|
|
|
template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
|
|
|
|
controller: Class({constructor: function() {}})
|
|
|
|
};
|
|
|
|
ng1Module.component('ng1', ng1);
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 =
|
2016-08-19 16:51:45 -04:00
|
|
|
Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'}).Class({
|
|
|
|
constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; }
|
|
|
|
});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-03-14 02:51:04 -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(`Hello GOKU SAN`);
|
|
|
|
ref.dispose();
|
|
|
|
});
|
2016-03-14 02:51:04 -04:00
|
|
|
}));
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should support ng2 > ng1 > ng2', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1Module = angular.module('ng1', []);
|
2016-06-01 18:58:40 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = {
|
2016-06-01 18:58:40 -04:00
|
|
|
template: 'ng1(<ng2b></ng2b>)',
|
|
|
|
};
|
|
|
|
ng1Module.component('ng1', ng1);
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2a = Component({selector: 'ng2a', template: 'ng2a(<ng1></ng1>)'}).Class({
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor: function() {}
|
|
|
|
});
|
2016-08-19 16:51:45 -04:00
|
|
|
ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a));
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2b =
|
2016-08-19 16:51:45 -04:00
|
|
|
Component({selector: 'ng2b', template: 'ng2b'}).Class({constructor: function() {}});
|
2016-06-01 18:58:40 -04:00
|
|
|
ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b));
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const element = html(`<div><ng2a></ng2a></div>`);
|
2016-06-08 19:38:52 -04:00
|
|
|
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))');
|
|
|
|
});
|
2016-06-01 18:58:40 -04:00
|
|
|
}));
|
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(() => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const MyNg2Module = NgModule({
|
|
|
|
providers: [{provide: SomeToken, useValue: 'correct_value'}],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
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(() => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const MyNg2Module =
|
2016-08-19 16:51:45 -04:00
|
|
|
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
it('should respect hierarchical dependency injection for ng2', async(() => {
|
|
|
|
const ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
const Ng2Parent = Component({
|
|
|
|
selector: 'ng2-parent',
|
|
|
|
template: `ng2-parent(<ng-content></ng-content>)`
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
const Ng2Child = Component({selector: 'ng2-child', template: `ng2-child`}).Class({
|
|
|
|
constructor: [Ng2Parent, function(parent: any) {}]
|
|
|
|
});
|
|
|
|
|
|
|
|
const Ng2Module =
|
|
|
|
NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}).Class({
|
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
const element = html('<ng2-parent><ng2-child></ng2-child></ng2-parent>');
|
|
|
|
|
|
|
|
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(() => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const MyNg2Module =
|
2016-08-19 16:51:45 -04:00
|
|
|
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
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);
|
|
|
|
}));
|
|
|
|
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should wait for ng2 testability', async(() => {
|
2016-10-10 12:18:33 -04:00
|
|
|
const MyNg2Module =
|
2016-08-19 16:51:45 -04:00
|
|
|
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
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', () => {
|
2016-08-19 16:51:45 -04:00
|
|
|
it('should verify UpgradeAdapter example', async(() => {
|
|
|
|
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
2016-10-10 12:18:33 -04:00
|
|
|
const module = angular.module('myExample', []);
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const ng1 = () => {
|
2015-10-13 00:32:41 -04:00
|
|
|
return {
|
2015-10-11 14:18:11 -04:00
|
|
|
scope: {title: '='},
|
2015-10-28 03:59:19 -04:00
|
|
|
transclude: true,
|
|
|
|
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
2015-10-13 00:32:41 -04:00
|
|
|
};
|
2016-10-10 12:18:33 -04:00
|
|
|
};
|
|
|
|
module.directive('ng1', ng1);
|
2015-10-13 00:32:41 -04:00
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2 =
|
2015-10-13 00:32:41 -04:00
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
inputs: ['name'],
|
2016-08-19 16:51:45 -04:00
|
|
|
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
|
2015-10-13 00:32:41 -04:00
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
2016-10-10 12:18:33 -04:00
|
|
|
const Ng2Module = NgModule({
|
|
|
|
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
|
|
|
imports: [BrowserModule],
|
|
|
|
}).Class({constructor: function() {}});
|
2016-08-19 16:51:45 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
|
|
|
|
document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
|
|
|
|
2017-03-24 12:59:18 -04:00
|
|
|
adapter.bootstrap(document.body.firstElementChild !, ['myExample']).ready((ref) => {
|
2016-06-08 19:38:52 -04:00
|
|
|
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', []);
|
|
|
|
|
|
|
|
const Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: 'Hello World',
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
|
|
|
|
constructor: function() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
const upgradeAdapter = new UpgradeAdapter(Ng2Module);
|
|
|
|
ng1Module.directive('ng2', upgradeAdapter.downgradeNg2Component(Ng2));
|
|
|
|
|
|
|
|
upgradeAdapterRef = upgradeAdapter.registerForNg1Tests(['ng1']);
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(
|
|
|
|
inject((_$compile_: angular.ICompileService, _$rootScope_: angular.IRootScopeService) => {
|
|
|
|
$compile = _$compile_;
|
|
|
|
$rootScope = _$rootScope_;
|
|
|
|
}));
|
|
|
|
|
|
|
|
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
|
|
|
});
|
|
|
|
}
|