As I describe in this answer, I created a custom ControlValueAccessor directive to have control on when to fire the onChange event of my component, and everything works perfectly, except when I test it, registerOnChange is never called, and thus, my test fails.
My directive looks like this:
export const MASK_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MaskDirective),
multi: true
};
@Directive({
selector: "[testMask]",
providers: [MASK_CONTROL_VALUE_ACCESSOR]
})
export class MaskDirective implements ControlValueAccessor {
private onChange;
private nativeElement;
constructor(private element: ElementRef) {
this.nativeElement = this.element.nativeElement;
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.nativeElement.disabled = isDisabled;
}
writeValue(newValue) {
newValue = newValue == null ? "" : newValue;
this.nativeElement.value = newValue;
}
@HostListener("input", ["$event"])
onInput(event: KeyboardEvent) {
/*DO YOUR STUFF HERE*/
// Call onChange to fire the valueChanged listeners
this.onChange(newValue);
}
}
And my test:
describe("Test Custom Directive", () => {
@Component({
template:
`<input type="text" [formControl]=inputFormControl testMask/>`
})
class TestComponent {
_inputFormControl: FormControl;
constructor(private element: ElementRef) {
this._inputFormControl = new FormControl();
}
get inputFormControl() {
return this._inputFormControl;
}
}
let fixture: ComponentFixture<TestComponent>;
let inputField: HTMLInputElement;
const getInput = (fix: ComponentFixture<TestComponent>) => {
const inputDebug = fix.debugElement.query(By.directive(MaskDirective));
return inputDebug.nativeElement as HTMLInputElement;
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestComponent,
MaskDirective
]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
inputField = getInput(fixture);
});
it("test", async () => {
//Tests fails because there's no onChange callback
});
}
Based on what I read on "Never again be confused when implementing ControlValueAccessor in Angular forms" I was under the assumption that just adding a FormControl to my input field should've triggered setupControl, but that's apparently not the case. What am I missing?
you need to import FormsModule in the configureTestingModule declaration.
I made a little diferent. By using ReactiveFormsModule and a directive named 'directiveForm'
@Component({
template: `<form [formGroup]="testForm"><input type="text" formControlName="testControl" directiveForm></form>`
})
class TestComponent {
public testForm: FormGroup;
constructor(private fb: FormBuilder) {
this.testForm = this.fb.group({
testControl: new FormControl()
});
}
}
describe('Directive Test', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
DirectiveForm,
TestComponent
],
imports: [DirectiveFormModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents();
}));
This way works for me
got same problem here, the cause for me was that registerOnChange was invoked after main test and the fix was wrapping TestBed.createComponent(TestComponent) in fakeAsync
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With