What is the best way to evaluate typing into an input field?

My objective is to test the 'typing' functionality in an input element. The aim is to insert a value into the input element, verify that its binding successfully captures the value, and observe the entered value within the input element.

Below is the code snippet:

In app.component:

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  template: `
    <span>My input: </span>
    <input name="name-input" placeholder="Enter name" [(ngModel)]="name" />
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  name = "";
}

The testing block:

import { TestBed } from "@angular/core/testing";
import { FormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";

describe("AppComponent", () => {
  var fixture: any;
  var app: AppComponent;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [AppComponent],
      imports: [FormsModule]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    app = fixture.componentInstance;
  });

  it("should create the app", () => {
    expect(app).toBeTruthy();
  });

  it("should type text into input element", () => {
    let inputElement = fixture.nativeElement.querySelector(
      `input[name='name-input']`
    );

    inputElement.value = "someValue";
    inputElement.dispatchEvent(new Event("input"));
    fixture.detectChanges();

    expect(app.name).toBe("someValue");
  });
});

Upon executing:

inputElement.value = "someValue";
inputElement.dispatchEvent(new Event("input"));
fixture.detectChanges();

Expected Result: app.name should be equal to "someValue". Current Outcome: app.name remains an empty string: "".

You can access a StackBlitz demo illustrating this issue: https://stackblitz.com/edit/stackoverflow-input-question1q2w3e?

Answer №1

Here is the specific code you need:

it("should input text into element after initialization", () => {
  fixture.detectChanges(); // Runs ngOnInit Lifecycle hook

  let inputElement = fixture.nativeElement.querySelector(
    `input[name='name-input']`
  );

  inputElement.value = "someValue";
  inputElement.dispatchEvent(new Event("input"));

  fixture.detectChanges();
  expect(app.name).toBe("someValue");
});

The reason this works: As stated in the official documentation, calling fixture.detectChanges() triggers the ngOnInit() lifecycle hook, which solves the issue.

By modifying the value outside of ngOnInit() before triggering the hook, changes are reverted as the view resets to its original state upon the first fixture.detectChanges() call, undoing previous modifications (such as setting the input value).

Best regards.

Answer №2

To trigger a KeyboardEvent for the HTMLInputElement, you should do so within a fakeAsync environment.

This is how your unit test would appear:

it('#keydown should update app#name', fakeAsync(() => {

  // given
  let inputElement = fixture.nativeElement.querySelector(`input[name='name-input']`);
  
  // when
  let event = new KeyboardEvent('keydown', { key: 'x' });
  inputElement.dispatchEvent(event);
  tick();

  // then
  expect(app.name).toBe("x");
}));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ensuring Consistency of Values Between Child and Parent Components

Is there a way to ensure that the value of submitted in the child component always matches the value of submitted in the parent component? Appreciate any help! @Component({ selector: 'child-cmp', template: ` child:{{submitted}} ...

Encountering a cloning error while using React Typescript and React Router DOM when calling props.history.push

When using props.history.push without passing state, everything works perfectly fine. However, when trying to pass data with state, an error occurs. The error message reads: DOMException: Failed to execute 'pushState' on 'History': func ...

Error encountered with the Angular 2 routing system

Currently, I am facing an issue with my Angular 2 router module. Whenever I try to access the link /city, I encounter an error message saying 'ERROR Error: Uncaught (in promise): Error: Cannot activate an already activated outlet Error: Cannot activat ...

What is the reason for instances being compatible even if their class constructors do not match?

Why are the constructors in the example below not compatible, but their instances are? class Individual { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } class Worker { name: st ...

Button for liking and disliking with Angular, Node.js, and

On my Twitter-esque website, I am developing YouTube-style (like-dislike) buttons. However, when it comes to implementing these like-dislike buttons using Angular, Node.js, and MYSQL with NgFor loop and ngIf conditions, I encountered a problem. My database ...

Facing problem with Angular 7 when making a GET request for non-JSON data

Currently, I am retrieving JSON data from a URL using the following method: this.http.get('http://localhost:3200/mydata').subscribe(data => { console.log(data); }); The response is in JSON format, and everything seems to be working fine. ...

The firebase-admin module encounters compatibility issues with middleware due to the OS Module

I'm facing a major issue with my API implementation. I am working on integrating an authentication and verification middleware, but the problem arises when using firebase-admin due to its dependencies on Edge Runtime modules that are incompatible with ...

Upon successful Google login, Angular 2 fails to initiate the callback function

import {Component,Directive,OnInit,NgZone} from 'angular2/core'; declare const gapi:any; declare const $:any; @Component({ selector: 'mysite', templateUrl:'./app/template.html' }) export class Test{ userAuthToken; ...

Gathering user key event input for a duration of 2 seconds before resetting it

I need help implementing a feature where I can clear the user's input text after 500ms if they are entering characters consecutively. private userInputTimer; private userInputText = ''; private handleEvent(event: KeyboardEvent): void { if ...

Unable to resolve host name for registry.npmjs.org on port 80

Hello everyone! I'm currently working on getting my angular library published on npm, but I seem to be running into an issue. When I try to log in, I encounter the following error: npm ERR! code EAI_AGAIN npm ERR! errno EAI_AGAIN npm ERR! request to h ...

Steering clear of the generic Function type in React with TypeScript

Can anyone help me find a guideline that prohibits the use of "Function" as a type? myMethod: Function; I have searched but couldn't locate any information on this. Appreciate any suggestions :) ...

Extracting information from an object retrieved through an http.get response can be accomplished by utilizing various methods and

I am working with an API that returns a JSON object like this: { "triggerCount": { "ignition_state_off": 16, "ignition_state_on": 14, "exit_an_area": 12, "enter_an_area": 19, "door_unlocked": 1, "door_l ...

Activate / Deactivate controls

My task involves creating a feature that displays multiple audio files, each with its own play button. When a specific button is clicked, the corresponding audio should play and the button should change to a stop icon. How can I manage the behavior of each ...

What is the key to mastering any concept in Angular2 for optimal results?

When creating a Pipe to filter data, I often use the datatype any, allowing for flexibility with types. However, I have some questions regarding this approach: Is it considered a best practice? Does it impact performance when handling large amounts of da ...

Use rowSelected to set the initial selected rows in an Angular Kendo UI grid

Within my Kendo UI Grid, the ability to select individual rows is made possible by clicking a checkbox that adds them to an array. However, my goal is to initially set selected rows based on whether or not the dataItem for each row exists in a specified ar ...

I'm struggling to set up break points in both my Angular application and library within VSCode. I can only seem to get them working in either one or the other, but not both

My goal is to enable debugging in vscode for both my Angular 16 application and my library at the same time. The file structure looks like this: ./root ./root/my-app/src ./root/lib/projects/my-lib I have successfully added my lib to the app's pr ...

Registering a function for chart.js plugins that manipulates external data

Is there a way to pass external data to the chart.plugins.register function? I'm struggling because I can't access the necessary context: Chart.plugins.register( { beforeDraw: function (chart) { //implementation } }); I attempted using ...

JavaScript - Employing the .every function with an array containing objects

Is it possible to use the array.every method on multidimensional arrays? The structure of my array is as follows: tabs=[ {label: string, icon: icon, routerLink: link}, {label: string, icon: icon, routerLink: link}, {label: string, icon: icon, routerLink: ...

Ways to extract information from a database using a parameter

I am currently working with angular cli version 8.1.0 and I have a requirement to pass parameters in the URL and retrieve data from a PHP MySQL database. On the PHP side, everything is functioning correctly and the URL looks like this: http://localhost/rep ...

Why is it that the component passed in props fails to function properly when invoked as a function? React is signaling a shift in the order of Hooks being called

Here is a simple example I've prepared to illustrate how I am passing a component and then calling it like a function, as well as another example where it works just by calling it normally. You can switch between the working and not working examples b ...