fix(ivy): fix property names of ngOnChanges (#27714)
- #reslove FW-812 - #reslove FW-844 PR Close #27714
This commit is contained in:

committed by
Miško Hevery

parent
4774a1abff
commit
1c93afe956
@ -1915,194 +1915,189 @@ withEachNg1Version(() => {
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-844: Directive input bindings cannot be assigned after the `@Directive` decorator has been compiled')
|
||||
.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;
|
||||
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; }
|
||||
}
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '<ng1-a [valA]="val"></ng1-a> | <ng1-b [valB]="val"></ng1-b>'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() { ng2Instance = this; }
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
angular.module('ng1', [])
|
||||
.directive('ng1A', () => ({
|
||||
template: '',
|
||||
scope: {valA: '<'},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function($scope: angular.IScope) {
|
||||
this.$onChanges = $onChangesControllerSpyA;
|
||||
}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B',
|
||||
() => ({
|
||||
template: '',
|
||||
scope: {valB: '<'},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {
|
||||
$onChanges(changes: SimpleChanges) { $onChangesControllerSpyB(changes); }
|
||||
}
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
Object.getPrototypeOf($rootScope).$onChanges = $onChangesScopeSpy;
|
||||
});
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
adapter.upgradeNg1Component('ng1A'), adapter.upgradeNg1Component('ng1B'),
|
||||
Ng2Component
|
||||
],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
}
|
||||
@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) => {
|
||||
// Initial `$onChanges()` call
|
||||
tick();
|
||||
const element = html(`<div><ng2></ng2></div>`);
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
// Initial `$onChanges()` call
|
||||
tick();
|
||||
|
||||
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
||||
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
||||
valA: jasmine.any(SimpleChange)
|
||||
});
|
||||
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
||||
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
||||
valA: jasmine.any(SimpleChange)
|
||||
});
|
||||
|
||||
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
||||
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
||||
|
||||
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
||||
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
||||
valB: jasmine.any(SimpleChange)
|
||||
});
|
||||
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();
|
||||
$onChangesControllerSpyA.calls.reset();
|
||||
$onChangesControllerSpyB.calls.reset();
|
||||
$onChangesScopeSpy.calls.reset();
|
||||
|
||||
// `$onChanges()` call after a change
|
||||
ng2Instance.val = 'new value';
|
||||
tick();
|
||||
ref.ng1RootScope.$digest();
|
||||
// `$onChanges()` call after a change
|
||||
ng2Instance.val = 'new value';
|
||||
tick();
|
||||
ref.ng1RootScope.$digest();
|
||||
|
||||
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
||||
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
||||
valA: jasmine.objectContaining({currentValue: 'new value'})
|
||||
});
|
||||
expect($onChangesControllerSpyA.calls.count()).toBe(1);
|
||||
expect($onChangesControllerSpyA.calls.argsFor(0)[0]).toEqual({
|
||||
valA: jasmine.objectContaining({currentValue: 'new value'})
|
||||
});
|
||||
|
||||
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
||||
expect($onChangesControllerSpyB).not.toHaveBeenCalled();
|
||||
|
||||
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
||||
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
||||
valB: jasmine.objectContaining({currentValue: 'new value'})
|
||||
});
|
||||
expect($onChangesScopeSpy.calls.count()).toBe(1);
|
||||
expect($onChangesScopeSpy.calls.argsFor(0)[0]).toEqual({
|
||||
valB: jasmine.objectContaining({currentValue: 'new value'})
|
||||
});
|
||||
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
|
||||
fixmeIvy(
|
||||
'FW-843: destroy hooks are not registered on upgraded ng1 components contained in ng2 component templates under ivy')
|
||||
.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;
|
||||
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: `
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
<div *ngIf="!ng2Destroy">
|
||||
<ng1-a></ng1-a> | <ng1-b></ng1-b>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class Ng2Component {
|
||||
ng2Destroy: boolean = false;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
}
|
||||
})
|
||||
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: class {$onDestroy() { $onDestroySpyA(); }}
|
||||
}))
|
||||
.directive(
|
||||
'ng1B', () => ({
|
||||
template: '',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: function() { this.$onDestroy = $onDestroySpyB; }
|
||||
}))
|
||||
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
||||
// 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 {
|
||||
}
|
||||
@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;
|
||||
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 = false;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($onDestroySpyA).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyB).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyA).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyB).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.ng1Destroy = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
$rootScope.ng1Destroy = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($onDestroySpyA).toHaveBeenCalled();
|
||||
expect($onDestroySpyB).toHaveBeenCalled();
|
||||
expect($onDestroySpyA).toHaveBeenCalled();
|
||||
expect($onDestroySpyB).toHaveBeenCalled();
|
||||
|
||||
$onDestroySpyA.calls.reset();
|
||||
$onDestroySpyB.calls.reset();
|
||||
$onDestroySpyA.calls.reset();
|
||||
$onDestroySpyB.calls.reset();
|
||||
|
||||
$rootScope.ng1Destroy = false;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
$rootScope.ng1Destroy = false;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($onDestroySpyA).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyB).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyA).not.toHaveBeenCalled();
|
||||
expect($onDestroySpyB).not.toHaveBeenCalled();
|
||||
|
||||
ng2ComponentInstance.ng2Destroy = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
ng2ComponentInstance.ng2Destroy = true;
|
||||
tick();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($onDestroySpyA).toHaveBeenCalled();
|
||||
expect($onDestroySpyB).toHaveBeenCalled();
|
||||
expect($onDestroySpyA).toHaveBeenCalled();
|
||||
expect($onDestroySpyB).toHaveBeenCalled();
|
||||
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not call `$onDestroy()` on scope', fakeAsync(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
@ -3053,40 +3048,37 @@ withEachNg1Version(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
fixmeIvy(
|
||||
'FW-844: Directive input bindings cannot be assigned after the `@Directive` decorator has been compiled')
|
||||
.it('should bind input properties (<) of components', async(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module('ng1', []);
|
||||
it('should bind input properties (<) of components', async(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
const ng1Module = angular.module('ng1', []);
|
||||
|
||||
const ng1 = {
|
||||
bindings: {personProfile: '<'},
|
||||
template:
|
||||
'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
|
||||
controller: class {}
|
||||
};
|
||||
ng1Module.component('ng1', ng1);
|
||||
const ng1 = {
|
||||
bindings: {personProfile: '<'},
|
||||
template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
|
||||
controller: class {}
|
||||
};
|
||||
ng1Module.component('ng1', ng1);
|
||||
|
||||
@Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'})
|
||||
class Ng2 {
|
||||
goku = {firstName: 'GOKU', lastName: 'SAN'};
|
||||
}
|
||||
@Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'})
|
||||
class Ng2 {
|
||||
goku = {firstName: 'GOKU', lastName: 'SAN'};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
}
|
||||
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
||||
|
||||
const element = html(`<div><ng2></ng2></div>`);
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`);
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
const element = html(`<div><ng2></ng2></div>`);
|
||||
adapter.bootstrap(element, ['ng1']).ready((ref) => {
|
||||
expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`);
|
||||
ref.dispose();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support ng2 > ng1 > ng2', async(() => {
|
||||
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
|
||||
|
Reference in New Issue
Block a user