web-dev-qa-db-fra.com

Mise à jour du champ HTML d'entrée à partir d'un test Angular 2

Je voudrais changer la valeur d'un champ de saisie à l'intérieur d'un Angular 2 unités de test.

<input type="text" class="form-control" [(ngModel)]="abc.value" />

Je ne peux pas simplement changer le ngModel car l'objet abc est privé:

 private abc: Abc = new Abc();

Dans Angular 2 tests, puis-je simuler la saisie de l'utilisateur dans le champ de saisie de sorte que le ngModel soit mis à jour avec ce que l'utilisateur a saisi à partir d'un test unitaire?

Je peux saisir le DebugElement et le nativeElement du champ de saisie sans problème. (Le fait de définir une propriété value sur le nativeElement du champ de saisie ne semble pas fonctionner car il ne met pas à jour le ngModel avec ce que j'ai défini pour la valeur).

Peut être inputDebugEl.triggerEventHandler _ peut être appelé, mais je ne suis pas sûr des arguments à donner, cela simulera donc l'utilisateur ayant saisi une chaîne d'entrée particulière.

30
Daniel

Vous avez raison de dire que vous ne pouvez pas simplement définir l'entrée, vous devez également envoyer l'événement 'input'. Voici une fonction que j'ai écrite plus tôt ce soir pour saisir du texte:

function sendInput(text: string) {
  inputElement.value = text;
  inputElement.dispatchEvent(new Event('input'));
  fixture.detectChanges();
  return fixture.whenStable();
}

Ici fixture est le ComponentFixture et inputElement est le HTTPInputElement correspondant à partir du nativeElement du projecteur. Cela retourne une promesse et vous devrez probablement la résoudre sendInput('whatever').then(...).

Dans le contexte: https: //github.com/textbook/known-for-web/blob/52c8aec4c2699c2f146a33c07786e1e32891c8b6/src/app/actor/actor.component.spec.ts#L134


Mise à jour :

Nous avons eu quelques problèmes pour que cela fonctionne dans Angular 2.1, cela ne lui a pas plu de créer une new Event(...)] _, alors nous avons plutôt:

import { dispatchEvent } from '@angular/platform-browser/testing/browser-util';

...

function sendInput(text: string) {
  inputElement.value = text;
  dispatchEvent(fixture.nativeElement, 'input');
  fixture.detectChanges();
  return fixture.whenStable();
}
44
jonrsharpe

La solution acceptée ne fonctionnait pas vraiment pour moi dans Angular 2.4. La valeur que j'avais définie n'apparaissait pas dans l'interface utilisateur (de test), même après l'appel de detectChanges ().

Pour que cela fonctionne, j'ai configuré mon test de la manière suivante:

describe('TemplateComponent', function () {
  let comp: TemplateComponent;
  let fixture: ComponentFixture<TemplateComponent>;

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

  beforeEach(() => {
    fixture = TestBed.createComponent(TemplateComponent);
    comp = fixture.componentInstance;
  });

  it('should allow us to set a bound input field', fakeAsync(() => {
    setInputValue('#test2', 'Tommy');

    expect(comp.personName).toEqual('Tommy');
  }));

  // must be called from within fakeAsync due to use of tick()
  function setInputValue(selector: string, value: string) {
    fixture.detectChanges();
    tick();

    let input = fixture.debugElement.query(By.css(selector)).nativeElement;
    input.value = value;
    input.dispatchEvent(new Event('input'));
    tick();
  }
});

Mon composant TemplateComponent a une propriété nommée personName dans cet exemple, qui correspond à la propriété de modèle que je lie dans mon modèle:

<input id="test2" type="text" [(ngModel)]="personName" />

13
Paul

J'ai également eu du mal à obtenir la réponse de jonrsharpe avec Angular 2.4. J'ai constaté que les appels à fixture.detectChanges() et fixture.whenStable() provoquaient la réinitialisation du composant de formulaire. Il semble qu’une fonction d’initialisation soit toujours en attente au début du test, ce que j’ai résolu en ajoutant des appels supplémentaires à ces méthodes avant chaque test. Voici un extrait de mon code:

beforeEach(() => {
    TestBed.configureTestingModule({
        // ...etc...
    });
    fixture = TestBed.createComponent(LoginComponent);
    comp = fixture.componentInstance;
    usernameBox = fixture.debugElement.query(By.css('input[name="username"]'));
    passwordBox = fixture.debugElement.query(By.css('input[type="password"]'));
    loginButton = fixture.debugElement.query(By.css('.btn-primary'));
    formElement = fixture.debugElement.query(By.css('form'));
});

beforeEach(async(() => {
    // The magic sauce!!
    // Because this is in an async wrapper it will automatically wait
    // for the call to whenStable() to complete
    fixture.detectChanges();
    fixture.whenStable();
}));

function sendInput(inputElement: any, text: string) {
    inputElement.value = text;
    inputElement.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    return fixture.whenStable();
}

it('should log in correctly', async(() => {

    sendInput(usernameBox.nativeElement, 'User1')
    .then(() => {
        return sendInput(passwordBox.nativeElement, 'Password1')
    }).then(() => {
        formElement.triggerEventHandler('submit', null);
        fixture.detectChanges();

        let spinner = fixture.debugElement.query(By.css('img'));
        expect(Helper.isHidden(spinner)).toBeFalsy('Spinner should be visible');

        // ...etc...
    });
}));
3
Philip Beber