2015-08-05 13:32:14 -04:00
|
|
|
import {
|
|
|
|
AsyncTestCompleter,
|
|
|
|
beforeEach,
|
|
|
|
ddescribe,
|
|
|
|
describe,
|
|
|
|
expect,
|
|
|
|
iit,
|
|
|
|
inject,
|
|
|
|
it,
|
|
|
|
xdescribe,
|
|
|
|
xit,
|
2015-10-13 03:29:13 -04:00
|
|
|
} from 'angular2/testing_internal';
|
2015-11-19 18:09:34 -05:00
|
|
|
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
2015-08-05 13:32:14 -04:00
|
|
|
|
2015-12-11 08:51:01 -05:00
|
|
|
import {Component, Class, Inject, EventEmitter, ApplicationRef, provide} from 'angular2/core';
|
2015-10-26 23:17:46 -04:00
|
|
|
import {UpgradeAdapter} from 'angular2/upgrade';
|
|
|
|
import * as angular from 'angular2/src/upgrade/angular_js';
|
2015-08-05 13:32:14 -04:00
|
|
|
|
|
|
|
export function main() {
|
2015-10-13 00:32:41 -04:00
|
|
|
describe('adapter: ng1 to ng2', () => {
|
2015-08-06 16:19:29 -04:00
|
|
|
it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1));
|
|
|
|
|
2015-10-09 17:53:04 -04:00
|
|
|
it('should instantiate ng2 in ng1 template and project content',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
var Ng2 = Component({selector: 'ng2', template: `{{ 'NG2' }}(<ng-content></ng-content>)`})
|
2015-09-23 12:56:38 -04:00
|
|
|
.Class({constructor: function() {}});
|
|
|
|
|
2015-10-09 17:53:04 -04:00
|
|
|
var element = html("<div>{{ 'ng1[' }}<ng2>~{{ 'ng-content' }}~</ng2>{{ ']' }}</div>");
|
2015-08-06 16:19:29 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
var adapter: UpgradeAdapter = new UpgradeAdapter();
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
expect(document.body.textContent).toEqual("ng1[NG2(~ng-content~)]");
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
});
|
2015-08-06 16:19:29 -04:00
|
|
|
}));
|
|
|
|
|
2015-10-10 00:31:42 -04:00
|
|
|
it('should instantiate ng1 in ng2 template and project content',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
var adapter: UpgradeAdapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
2015-09-23 12:56:38 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
2015-09-23 12:56:38 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng1', () => {
|
2015-10-10 00:31:42 -04:00
|
|
|
return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'};
|
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2015-09-23 12:56:38 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
var element = html("<div>{{'ng1('}}<ng2></ng2>{{')'}}</div>");
|
2015-08-06 16:19:29 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
adapter.bootstrap(element, ['ng1'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
expect(document.body.textContent).toEqual("ng1(ng2(ng1(transclude)))");
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
});
|
2015-08-06 16:19:29 -04:00
|
|
|
}));
|
2015-09-30 18:42:02 -04:00
|
|
|
|
|
|
|
describe('scope/component change-detection', () => {
|
2015-10-05 19:02:21 -04:00
|
|
|
it('should interleave scope and component expressions',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
var ng1Module = angular.module('ng1', []);
|
2015-09-30 18:42:02 -04:00
|
|
|
var log = [];
|
|
|
|
var l = function(value) {
|
|
|
|
log.push(value);
|
|
|
|
return value + ';';
|
|
|
|
};
|
2015-10-13 00:32:41 -04:00
|
|
|
var adapter: UpgradeAdapter = new UpgradeAdapter();
|
2015-09-30 18:42:02 -04:00
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng1a', () => { return {template: "{{ l('ng1a') }}"}; });
|
|
|
|
ng1Module.directive('ng1b', () => { return {template: "{{ l('ng1b') }}"}; });
|
|
|
|
ng1Module.run(($rootScope) => {
|
2015-09-30 18:42:02 -04:00
|
|
|
$rootScope.l = l;
|
|
|
|
$rootScope.reset = () => log.length = 0;
|
|
|
|
});
|
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
var Ng2 =
|
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`,
|
|
|
|
directives:
|
|
|
|
[adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b')]
|
|
|
|
}).Class({constructor: function() { this.l = l; }});
|
|
|
|
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2015-09-30 18:42:02 -04:00
|
|
|
|
|
|
|
var element = html("<div>{{reset(); l('1A');}}<ng2>{{l('1B')}}</ng2>{{l('1C')}}</div>");
|
2015-10-13 00:32:41 -04:00
|
|
|
adapter.bootstrap(element, ['ng1'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
expect(document.body.textContent).toEqual("1A;2A;ng1a;2B;ng1b;2C;1C;");
|
|
|
|
// https://github.com/angular/angular.js/issues/12983
|
|
|
|
expect(log).toEqual(['1A', '1B', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']);
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
});
|
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', () => {
|
2015-10-05 19:02:21 -04:00
|
|
|
it('should bind properties, events', inject([AsyncTestCompleter], (async) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
var adapter: UpgradeAdapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
ng1Module.run(($rootScope) => {
|
2015-10-01 16:14:59 -04:00
|
|
|
$rootScope.dataA = 'A';
|
|
|
|
$rootScope.dataB = 'B';
|
|
|
|
$rootScope.modelA = 'initModelA';
|
|
|
|
$rootScope.modelB = 'initModelB';
|
|
|
|
$rootScope.eventA = '?';
|
|
|
|
$rootScope.eventB = '?';
|
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
var Ng2 =
|
2015-10-01 16:14:59 -04:00
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
2015-10-10 22:56:22 -04:00
|
|
|
outputs: [
|
|
|
|
'eventA',
|
|
|
|
'eventB',
|
|
|
|
'twoWayAEmitter: twoWayAChange',
|
|
|
|
'twoWayBEmitter: twoWayBChange'
|
2015-10-13 00:32:41 -04:00
|
|
|
],
|
|
|
|
template: "ignore: {{ignore}}; " +
|
|
|
|
"literal: {{literal}}; interpolate: {{interpolate}}; " +
|
|
|
|
"oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; " +
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 20:04:36 -05:00
|
|
|
"twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})"
|
2015-10-01 16:14:59 -04:00
|
|
|
})
|
|
|
|
.Class({
|
|
|
|
constructor: function() {
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 20:04:36 -05:00
|
|
|
this.ngOnChangesCount = 0;
|
2015-10-01 16:14:59 -04:00
|
|
|
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();
|
|
|
|
},
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 20:04:36 -05:00
|
|
|
ngOnChanges: function(changes) {
|
2015-10-05 19:02:21 -04:00
|
|
|
var assert = (prop, value) => {
|
|
|
|
if (this[prop] != value) {
|
|
|
|
throw new Error(
|
|
|
|
`Expected: '${prop}' to be '${value}' but was '${this[prop]}'`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var assertChange = (prop, value) => {
|
|
|
|
assert(prop, value);
|
|
|
|
if (!changes[prop]) {
|
|
|
|
throw new Error(`Changes record for '${prop}' not found.`);
|
|
|
|
}
|
|
|
|
var actValue = changes[prop].currentValue;
|
|
|
|
if (actValue != value) {
|
|
|
|
throw new Error(
|
|
|
|
`Expected changes record for'${prop}' to be '${value}' but was '${actValue}'`);
|
|
|
|
}
|
|
|
|
};
|
2015-10-01 16:14:59 -04:00
|
|
|
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 20:04:36 -05:00
|
|
|
switch (this.ngOnChangesCount++) {
|
2015-10-01 16:14:59 -04:00
|
|
|
case 0:
|
|
|
|
assert('ignore', '-');
|
|
|
|
assertChange('literal', 'Text');
|
|
|
|
assertChange('interpolate', 'Hello world');
|
|
|
|
assertChange('oneWayA', 'A');
|
|
|
|
assertChange('oneWayB', 'B');
|
|
|
|
assertChange('twoWayA', 'initModelA');
|
|
|
|
assertChange('twoWayB', 'initModelB');
|
|
|
|
|
2015-11-16 02:58:59 -05:00
|
|
|
this.twoWayAEmitter.emit('newA');
|
|
|
|
this.twoWayBEmitter.emit('newB');
|
|
|
|
this.eventA.emit('aFired');
|
|
|
|
this.eventB.emit('bFired');
|
2015-10-01 16:14:59 -04:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
assertChange('twoWayA', 'newA');
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
assertChange('twoWayB', 'newB');
|
|
|
|
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));
|
2015-10-01 16:14:59 -04:00
|
|
|
var element = html(`<div>
|
|
|
|
<ng2 literal="Text" interpolate="Hello {{'world'}}"
|
|
|
|
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>`);
|
2015-10-13 00:32:41 -04:00
|
|
|
adapter.bootstrap(element, ['ng1'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual(
|
|
|
|
"ignore: -; " + "literal: Text; interpolate: Hello world; " +
|
|
|
|
"oneWayA: A; oneWayB: B; twoWayA: initModelA; twoWayB: initModelB; (1) | " +
|
|
|
|
"modelA: initModelA; modelB: initModelB; eventA: ?; eventB: ?;");
|
|
|
|
setTimeout(() => {
|
|
|
|
// we need to do setTimeout, because the EventEmitter uses setTimeout to schedule
|
|
|
|
// events, and so without this we would not see the events processed.
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual("ignore: -; " + "literal: Text; interpolate: Hello world; " +
|
|
|
|
"oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | " +
|
|
|
|
"modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;");
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
});
|
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', () => {
|
2015-10-04 12:33:20 -04:00
|
|
|
it('should bind properties, events', inject([AsyncTestCompleter], (async) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
2015-10-04 12:33:20 -04:00
|
|
|
var ng1 = function() {
|
|
|
|
return {
|
|
|
|
template: 'Hello {{fullName}}; A: {{dataA}}; B: {{dataB}}; | ',
|
2015-10-11 14:18:11 -04:00
|
|
|
scope: {fullName: '@', modelA: '=dataA', modelB: '=dataB', event: '&'},
|
|
|
|
link: function(scope) {
|
|
|
|
scope.$watch('dataB', (v) => {
|
|
|
|
if (v == 'Savkin') {
|
|
|
|
scope.dataB = 'SAVKIN';
|
|
|
|
scope.event('WORKS');
|
|
|
|
|
2015-12-16 02:47:48 -05:00
|
|
|
// Should not update because [model-a] is uni directional
|
2015-10-11 14:18:11 -04:00
|
|
|
scope.dataA = 'VICTOR';
|
2015-10-04 12:33:20 -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);
|
|
|
|
var Ng2 =
|
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template:
|
2015-11-23 19:02:19 -05:00
|
|
|
'<ng1 fullName="{{last}}, {{first}}" [modelA]="first" [(modelB)]="last" ' +
|
2015-10-13 00:32:41 -04:00
|
|
|
'(event)="event=$event"></ng1>' +
|
2015-11-23 19:02:19 -05:00
|
|
|
'<ng1 fullName="{{\'TEST\'}}" modelA="First" modelB="Last"></ng1>' +
|
2015-10-13 00:32:41 -04:00
|
|
|
'{{event}}-{{last}}, {{first}}',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
})
|
2015-10-04 12:33:20 -04:00
|
|
|
.Class({
|
|
|
|
constructor: function() {
|
|
|
|
this.first = 'Victor';
|
|
|
|
this.last = 'Savkin';
|
|
|
|
this.event = '?';
|
|
|
|
}
|
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
2015-10-04 12:33:20 -04:00
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
2015-10-13 00:32:41 -04:00
|
|
|
adapter.bootstrap(element, ['ng1'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
// 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; A: VICTOR; B: SAVKIN; | Hello TEST; A: First; B: Last; | WORKS-SAVKIN, Victor");
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
}));
|
2015-10-11 14:18:11 -04:00
|
|
|
|
|
|
|
it('should support templateUrl fetched from $httpBackend',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.value('$httpBackend',
|
|
|
|
(method, url, post, cbFn) => { cbFn(200, `${method}:${url}`); });
|
|
|
|
|
|
|
|
var ng1 = function() { return {templateUrl: 'url.html'}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('GET:url.html');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-12-10 07:23:47 -05:00
|
|
|
it('should support empty template', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
var ng1 = function() { return {template: ''}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-10-11 14:18:11 -04:00
|
|
|
it('should support templateUrl fetched from $templateCache',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
ng1Module.run(($templateCache) => $templateCache.put('url.html', 'WORKS'));
|
|
|
|
|
|
|
|
var ng1 = function() { return {templateUrl: 'url.html'}; };
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should support controller with controllerAs', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
var ng1 = function() {
|
|
|
|
return {
|
|
|
|
scope: true,
|
|
|
|
template:
|
|
|
|
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
|
|
|
|
controllerAs: 'ctl',
|
|
|
|
controller: Class({
|
|
|
|
constructor: function($scope, $element) {
|
|
|
|
(<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);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual('scope; isClass; NG1; published');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should support bindToController', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
var ng1 = function() {
|
|
|
|
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);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1 title="WORKS"></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should support single require in linking fn', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
var ng1 = function($rootScope) {
|
|
|
|
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'; }}),
|
|
|
|
link: function(scope, element, attrs, linkController) {
|
|
|
|
expect(scope.$root).toEqual($rootScope);
|
|
|
|
expect(element[0].nodeName).toEqual('NG1');
|
|
|
|
expect(linkController.status).toEqual('WORKS');
|
|
|
|
scope.ctl = linkController;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
ng1Module.directive('ng1', ng1);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><ng2></ng2></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should support array require in linking fn', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var ng1Module = angular.module('ng1', []);
|
|
|
|
|
|
|
|
var parent = function() {
|
|
|
|
return {controller: Class({constructor: function() { this.parent = 'PARENT'; }})};
|
|
|
|
};
|
|
|
|
var ng1 = function() {
|
|
|
|
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'; }}),
|
|
|
|
link: function(scope, element, attrs, linkControllers) {
|
|
|
|
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);
|
|
|
|
var Ng2 = Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
template: '<ng1></ng1>',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
var element = html(`<div><parent><ng2></ng2></parent></div>`);
|
|
|
|
adapter.bootstrap(element, ['ng1'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
});
|
|
|
|
|
2015-10-15 15:04:19 -04:00
|
|
|
describe('injection', () => {
|
|
|
|
function SomeToken() {}
|
|
|
|
|
|
|
|
it('should export ng2 instance to ng1', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var module = angular.module('myExample', []);
|
|
|
|
adapter.addProvider(provide(SomeToken, {useValue: 'correct_value'}));
|
|
|
|
module.factory('someToken', adapter.downgradeNg2Provider(SomeToken));
|
|
|
|
adapter.bootstrap(html('<div>'), ['myExample'])
|
|
|
|
.ready((ref) => {
|
|
|
|
expect(ref.ng1Injector.get('someToken')).toBe('correct_value');
|
|
|
|
ref.dispose();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should export ng1 instance to ng2', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var module = angular.module('myExample', []);
|
|
|
|
module.value('testValue', 'secreteToken');
|
|
|
|
adapter.upgradeNg1Provider('testValue');
|
|
|
|
adapter.upgradeNg1Provider('testValue', {asToken: 'testToken'});
|
|
|
|
adapter.upgradeNg1Provider('testValue', {asToken: String});
|
|
|
|
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();
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
2015-10-13 00:32:41 -04:00
|
|
|
describe('examples', () => {
|
|
|
|
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var adapter = new UpgradeAdapter();
|
|
|
|
var module = angular.module('myExample', []);
|
|
|
|
|
|
|
|
module.directive('ng1', function() {
|
|
|
|
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
|
|
|
};
|
2015-10-04 12:33:20 -04:00
|
|
|
});
|
2015-10-13 00:32:41 -04:00
|
|
|
|
|
|
|
|
|
|
|
var Ng2 =
|
|
|
|
Component({
|
|
|
|
selector: 'ng2',
|
|
|
|
inputs: ['name'],
|
|
|
|
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)',
|
|
|
|
directives: [adapter.upgradeNg1Component('ng1')]
|
|
|
|
}).Class({constructor: function() {}});
|
|
|
|
|
|
|
|
module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
|
|
|
|
|
|
|
document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
|
|
|
|
|
|
|
adapter.bootstrap(document.body, ['myExample'])
|
2015-10-11 14:18:11 -04:00
|
|
|
.ready((ref) => {
|
2015-10-13 00:32:41 -04:00
|
|
|
expect(multiTrim(document.body.textContent))
|
|
|
|
.toEqual("ng2[ng1[Hello World!](transclude)](project)");
|
2015-10-11 14:18:11 -04:00
|
|
|
ref.dispose();
|
2015-10-13 00:32:41 -04:00
|
|
|
async.done();
|
|
|
|
});
|
2015-10-04 12:33:20 -04:00
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
2015-08-06 16:19:29 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-10-01 16:14:59 -04:00
|
|
|
function multiTrim(text: string): string {
|
|
|
|
return text.replace(/\n/g, '').replace(/\s\s+/g, ' ').trim();
|
|
|
|
}
|
2015-08-06 16:19:29 -04:00
|
|
|
|
|
|
|
function html(html: string): Element {
|
|
|
|
var body = document.body;
|
|
|
|
body.innerHTML = html;
|
|
|
|
if (body.childNodes.length == 1 && body.firstChild instanceof HTMLElement)
|
|
|
|
return <Element>body.firstChild;
|
|
|
|
return body;
|
2015-08-05 13:32:14 -04:00
|
|
|
}
|