-
-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #128 from satanTime/issues/127
fix: improving types and linter
- Loading branch information
Showing
67 changed files
with
433 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { Component, forwardRef, NgModule } from '@angular/core'; | ||
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; | ||
|
||
@Component({ | ||
selector: 'target', | ||
template: '<control [formControl]="control"></control>', | ||
}) | ||
export class TargetComponent { | ||
public readonly control = new FormControl(); | ||
} | ||
|
||
@Component({ | ||
providers: [ | ||
{ | ||
multi: true, | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => ControlComponent), | ||
}, | ||
], | ||
selector: 'control', | ||
template: '', | ||
}) | ||
export class ControlComponent implements ControlValueAccessor { | ||
public isDisabled = false; | ||
public value: any; | ||
public change: any = () => undefined; | ||
|
||
changeTouch(): void { | ||
this.touch(); | ||
} | ||
|
||
changeValue(obj: any): void { | ||
this.change(obj); | ||
} | ||
|
||
registerOnChange(fn: any): void { | ||
this.change = fn; | ||
} | ||
|
||
registerOnTouched(fn: any): void { | ||
this.touch = fn; | ||
} | ||
|
||
setDisabledState(isDisabled: boolean): void { | ||
this.isDisabled = isDisabled; | ||
} | ||
|
||
public touch: any = () => undefined; | ||
|
||
writeValue(obj: any): void { | ||
this.value = obj; | ||
} | ||
} | ||
|
||
@NgModule({ | ||
declarations: [TargetComponent, ControlComponent], | ||
exports: [TargetComponent], | ||
imports: [CommonModule, ReactiveFormsModule], | ||
}) | ||
export class TargetModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { ReactiveFormsModule } from '@angular/forms'; | ||
|
||
import { MockBuilder } from '../../lib/mock-builder'; | ||
import { MockComponent } from '../../lib/mock-component'; | ||
import { MockHelper } from '../../lib/mock-helper'; | ||
import { MockRender } from '../../lib/mock-render'; | ||
|
||
import { ControlComponent, TargetComponent, TargetModule } from './fixtures'; | ||
|
||
// a real case to check possible behavior. | ||
describe('control-value-accessor-form-control:real', () => { | ||
beforeEach(() => MockBuilder(TargetComponent).keep(TargetModule)); | ||
|
||
it('respects our formControl', () => { | ||
const fixture = MockRender(TargetComponent, {}, false); | ||
const mock = MockHelper.findOrFail(fixture.debugElement, ControlComponent).componentInstance; | ||
spyOn(mock, 'writeValue').and.callThrough(); | ||
spyOn(mock, 'setDisabledState').and.callThrough(); | ||
fixture.detectChanges(); | ||
|
||
// tslint:disable-next-line:no-null-keyword | ||
expect(mock.writeValue).toHaveBeenCalledWith(null); | ||
expect(mock.setDisabledState).not.toHaveBeenCalled(); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
// checking via original component | ||
fixture.point.componentInstance.control.setValue('test1'); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test1'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
fixture.point.componentInstance.control.setValue('test2'); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test2'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
// checking that touch works | ||
mock.changeTouch(); | ||
expect(fixture.point.componentInstance.control.touched).toBeTruthy(); | ||
|
||
// checking that reset works | ||
fixture.point.componentInstance.control.markAsUntouched(); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
// checking that disabled works | ||
fixture.point.componentInstance.control.disable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(true); | ||
fixture.point.componentInstance.control.enable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(false); | ||
|
||
// changeValue doesn't trigger anything else but the callback. Therefore it doesn't render new value. | ||
// It only updates the original control's value. | ||
mock.changeValue('test3'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test3'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
expect(fixture.point.componentInstance.control.value).toBe('test3'); | ||
}); | ||
}); | ||
|
||
// a way that ensures that a mocked component behaves the same way as real one. | ||
describe('control-value-accessor-form-control:mock', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(ReactiveFormsModule)); | ||
|
||
it('respects our formControl', () => { | ||
const fixture = MockRender(TargetComponent, {}, false); | ||
const mock = MockHelper.findOrFail(fixture.debugElement, MockComponent(ControlComponent)).componentInstance; | ||
spyOn(mock, 'writeValue').and.callThrough(); | ||
spyOn(mock, 'setDisabledState').and.callThrough(); | ||
spyOn(mock, 'registerOnChange').and.callThrough(); | ||
spyOn(mock, 'registerOnTouched').and.callThrough(); | ||
fixture.detectChanges(); | ||
|
||
// tslint:disable-next-line:no-null-keyword | ||
expect(mock.writeValue).toHaveBeenCalledWith(null); | ||
expect(mock.setDisabledState).not.toHaveBeenCalled(); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
// checking via original component | ||
fixture.point.componentInstance.control.setValue('test1'); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test1'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
fixture.point.componentInstance.control.setValue('test2'); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test2'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
|
||
// checking that touch works | ||
mock.__simulateTouch(); | ||
expect(fixture.point.componentInstance.control.touched).toBeTruthy(); | ||
fixture.point.componentInstance.control.markAsUntouched(); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
// a way through a spy | ||
MockHelper.mockService<jasmine.Spy>(mock, 'registerOnTouched').calls.first().args[0](); | ||
expect(fixture.point.componentInstance.control.touched).toBeTruthy(); | ||
fixture.point.componentInstance.control.markAsUntouched(); | ||
|
||
// checking that disabled works | ||
fixture.point.componentInstance.control.disable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(true); | ||
fixture.point.componentInstance.control.enable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(false); | ||
|
||
// changeValue doesn't trigger anything else but the callback. Therefore it doesn't render new value. | ||
// It only updates the original control's value. | ||
mock.__simulateChange('test3'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test3'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
expect(fixture.point.componentInstance.control.value).toBe('test3'); | ||
// a way through a spy | ||
MockHelper.mockService<jasmine.Spy>(mock, 'registerOnChange').calls.first().args[0]('test4'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test4'); | ||
expect(fixture.point.componentInstance.control.touched).toBeFalsy(); | ||
expect(fixture.point.componentInstance.control.value).toBe('test4'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { Component, forwardRef, NgModule } from '@angular/core'; | ||
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||
|
||
@Component({ | ||
selector: 'target', | ||
template: '<control [ngModel]="value" (ngModelChange)="value = $event" [disabled]="disabled"></control>', | ||
}) | ||
export class TargetComponent { | ||
public disabled = false; | ||
public realValue: null | string = null; // tslint:disable-line:no-null-keyword | ||
|
||
public get value(): null | string { | ||
return this.realValue; | ||
} | ||
|
||
public set value(value: null | string) { | ||
this.realValue = value; | ||
} | ||
} | ||
|
||
@Component({ | ||
providers: [ | ||
{ | ||
multi: true, | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => ControlComponent), | ||
}, | ||
], | ||
selector: 'control', | ||
template: '', | ||
}) | ||
export class ControlComponent implements ControlValueAccessor { | ||
public isDisabled = false; | ||
public value: any; | ||
public change: any = () => undefined; | ||
|
||
changeTouch(): void { | ||
this.touch(); | ||
} | ||
|
||
changeValue(obj: any): void { | ||
this.change(obj); | ||
} | ||
|
||
registerOnChange(fn: any): void { | ||
this.change = fn; | ||
} | ||
|
||
registerOnTouched(fn: any): void { | ||
this.touch = fn; | ||
} | ||
|
||
setDisabledState(isDisabled: boolean): void { | ||
this.isDisabled = isDisabled; | ||
} | ||
|
||
public touch: any = () => undefined; | ||
|
||
writeValue(obj: any): void { | ||
this.value = obj; | ||
} | ||
} | ||
|
||
@NgModule({ | ||
declarations: [TargetComponent, ControlComponent], | ||
exports: [TargetComponent], | ||
imports: [CommonModule, FormsModule], | ||
}) | ||
export class TargetModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { FormsModule, NgModel } from '@angular/forms'; | ||
|
||
import { MockBuilder } from '../../lib/mock-builder'; | ||
import { MockComponent } from '../../lib/mock-component'; | ||
import { MockHelper } from '../../lib/mock-helper'; | ||
import { MockRender } from '../../lib/mock-render'; | ||
|
||
import { ControlComponent, TargetComponent, TargetModule } from './fixtures'; | ||
|
||
// a real case to check possible behavior. | ||
describe('control-value-accessor-ng-model:real', () => { | ||
beforeEach(() => MockBuilder(TargetComponent).keep(TargetModule)); | ||
|
||
it('respects our ngModel', async () => { | ||
const fixture = MockRender(TargetComponent, {}, false); | ||
const mockElement = MockHelper.findOrFail(fixture.debugElement, ControlComponent); | ||
const mock = mockElement.componentInstance; | ||
spyOn(mock, 'writeValue').and.callThrough(); | ||
spyOn(mock, 'setDisabledState').and.callThrough(); | ||
const ngModel = MockHelper.getDirectiveOrFail(mockElement, NgModel); | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
|
||
// tslint:disable-next-line:no-null-keyword | ||
expect(mock.writeValue).toHaveBeenCalledWith(null); | ||
expect(mock.setDisabledState).not.toHaveBeenCalled(); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
// checking via original component | ||
fixture.point.componentInstance.value = 'test1'; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test1'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
fixture.point.componentInstance.value = 'test2'; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test2'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
// checking that touch works | ||
mock.changeTouch(); | ||
expect(ngModel.touched).toBeTruthy(); | ||
|
||
// checking that reset works | ||
ngModel.control.markAsUntouched(); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
// checking that disabled works | ||
fixture.point.componentInstance.disabled = true; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(true); | ||
fixture.point.componentInstance.disabled = false; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(false); | ||
|
||
// changeValue doesn't trigger anything else but the callback. Therefore it doesn't render new value. | ||
// It only updates the original control's value. | ||
mock.changeValue('test3'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test3'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
expect(fixture.point.componentInstance.value).toBe('test3'); | ||
}); | ||
}); | ||
|
||
// a way that ensures that a mocked component behaves the same way as real one. | ||
describe('control-value-accessor-ng-model:mock', () => { | ||
beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(FormsModule)); | ||
|
||
it('respects our ngModel', async () => { | ||
const fixture = MockRender(TargetComponent, {}, false); | ||
const mockElement = MockHelper.findOrFail(fixture.debugElement, MockComponent(ControlComponent)); | ||
const mock = mockElement.componentInstance; | ||
spyOn(mock, 'writeValue').and.callThrough(); | ||
spyOn(mock, 'setDisabledState').and.callThrough(); | ||
spyOn(mock, 'registerOnChange').and.callThrough(); | ||
spyOn(mock, 'registerOnTouched').and.callThrough(); | ||
const ngModel = MockHelper.getDirectiveOrFail(mockElement, NgModel); | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
|
||
// tslint:disable-next-line:no-null-keyword | ||
expect(mock.writeValue).toHaveBeenCalledWith(null); | ||
expect(mock.setDisabledState).not.toHaveBeenCalled(); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
// checking via original component | ||
fixture.point.componentInstance.value = 'test1'; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test1'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
fixture.point.componentInstance.value = 'test2'; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.writeValue).toHaveBeenCalledWith('test2'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
|
||
// checking that touch works | ||
mock.__simulateTouch(); | ||
expect(ngModel.touched).toBeTruthy(); | ||
ngModel.control.markAsUntouched(); | ||
expect(ngModel.touched).toBeFalsy(); | ||
// a way through a spy | ||
MockHelper.mockService<jasmine.Spy>(mock, 'registerOnTouched').calls.first().args[0](); | ||
expect(ngModel.touched).toBeTruthy(); | ||
ngModel.control.markAsUntouched(); | ||
|
||
// checking that disabled works | ||
fixture.point.componentInstance.disabled = true; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(true); | ||
fixture.point.componentInstance.disabled = false; | ||
fixture.detectChanges(); | ||
await fixture.whenStable(); | ||
expect(mock.setDisabledState).toHaveBeenCalledWith(false); | ||
|
||
// changeValue doesn't trigger anything else but the callback. Therefore it doesn't render new value. | ||
// It only updates the original control's value. | ||
mock.__simulateChange('test3'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test3'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
expect(fixture.point.componentInstance.value).toBe('test3'); | ||
// a way through a spy | ||
MockHelper.mockService<jasmine.Spy>(mock, 'registerOnChange').calls.first().args[0]('test4'); | ||
expect(mock.writeValue).not.toHaveBeenCalledWith('test4'); | ||
expect(ngModel.touched).toBeFalsy(); | ||
expect(ngModel.value).toBe('test4'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
// tslint:disable:max-classes-per-file | ||
|
||
import { Component } from '@angular/core'; | ||
|
||
@Component({ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.