1

I have a specific issue with using ng-mocks and testing ngModel.

The following is my unit test, with marked line which fails:

        it('should not move focus, and should delete current input value when backspace clicked inside filled input', fakeAsync(() => {
            const fixture = MockRender(SixDigitInputComponent);
            const inputs = ngMocks.findAll('input');
            const focusSpy = spyOn(inputs[0].nativeElement, 'focus');
            
            ngMocks.change(inputs[0], 3);
            ngMocks.change(inputs[1], 2);

            expect(inputs[0].nativeElement.value).toBe('3');
            expect(inputs[1].nativeElement.value).toBe('2');

            ngMocks.trigger(inputs[1], 'keydown.backspace');
            fixture.detectChanges();
            tick();
            expect(inputs[0].nativeElement.value).toBe('3'); // fails here "expects '' to be '3'"
            expect(inputs[1].nativeElement.value).toBe('');

            expect(focusSpy).not.toHaveBeenCalled();
        }));

All other expect lines work fine.

But when i replaced ngMocks.change with these lines, all passed:

            inputs[0].nativeElement.value = 3;
            inputs[0].nativeElement.dispatchEvent(new Event('input'));
            inputs[1].nativeElement.value = 2;
            inputs[1].nativeElement.dispatchEvent(new Event('input'));

some.component.html

<div class="code-input-container">
    <input
        #inputField
        (paste)="pasteValue($event)"
        (keydown.backspace)="clearFieldAndBacktrack(0)"
        (ngModelChange)="writeAndMoveToNext($event, 0)"
        [ngModel]="displayNumbers[0]"
        type="text"
        maxlength="1"
        placeholder="0"
    />
    <input
        #inputField
        (paste)="pasteValue($event)"
        (keydown.backspace)="clearFieldAndBacktrack(1)"
        (ngModelChange)="writeAndMoveToNext($event, 1)"
        [ngModel]="displayNumbers[1]"
        maxlength="1"
        type="text"
        placeholder="0"
    />
    <input
        #inputField
        (paste)="pasteValue($event)"
        (keydown.backspace)="clearFieldAndBacktrack(2)"
        (ngModelChange)="writeAndMoveToNext($event, 2)"
        [ngModel]="displayNumbers[2]"
        maxlength="1"
        type="text"
        placeholder="0"
    />
</div>

some.component.ts

    public writeAndMoveToNext(digit: string, i: number): void {
        if (!digit || !this.regex.test(digit)) {
            return;
        }
        this.displayNumbers[i] = Number(digit);
        this.focusOn(i + 1);
        this.onTouch();
        this.onChangeFn(this.displayNumbers.join(''));
    }

    public clearFieldAndBacktrack(i: number): void {
        if (i === -1) {
            return;
        }
        // on second backspace, clear and focus on previous input field
        if (this.displayNumbers[i] === null) {
            this.clearFieldAndBacktrack(i - 1);
            return;
        }
        this.focusOn(i);
        this.displayNumbers[i] = null;
        this.onTouch();
        this.onChangeFn(this.displayNumbers.join(''));
    }

Why this didnt trigger ngModel change in DOM:

            ngMocks.change(inputs[0], 3);
            ngMocks.change(inputs[1], 2);

And this did:

            inputs[0].nativeElement.value = 3;
            inputs[0].nativeElement.dispatchEvent(new Event('input'));
            inputs[1].nativeElement.value = 2;
            inputs[1].nativeElement.dispatchEvent(new Event('input'));
Tomas J
  • 21
  • 4

0 Answers0