5

I have fairly extensive testing in our Angular 2.4 based application. One area we have not been able to test though is forms with inputs. I finally tracked this down to a small test case. I have a plnkr that shows the issue: https://plnkr.co/edit/NyeAoh2Uo5rJT9if6j64 (see form_bug.spec.ts)

Note: I based this example off the way the code in the testing guide works.

The problem is that when we hook an input up with ngModel, and then in the test suite we update the input the data will flow into the component only if the input is not inside a form. If we add it to a form, then the data will not flow correctly.

The core pieces of the code are:

@Component({
   selector: 'with-form-test',
   template: `
   <div>
      <div class="show_val">{{testVal}}</div>
      <form>
         <input name="test_val" id="val_id" [(ngModel)]="testVal" />
      </form>
   </div>
   `,
})
class WithFormTestComp {
   testVal: string = 'start';
}

and the test. The two expect() checks in this one fail.

describe('WithFormTest', () => {
   let fixture: ComponentFixture<WithFormTestComp>;
   let comp:    WithFormTestComp;
   let comp_de: DebugElement;

   function getInput(): DebugElement {
      return comp_de.query(By.css('input'));
   }
   function getShowVal(): DebugElement {
      return comp_de.query(By.css('.show_val'));
   }

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

      fixture = TestBed.createComponent(WithFormTestComp);
      comp    = fixture.componentInstance;
      comp_de = fixture.debugElement;
   });

   it('should update testVal', () => {
      fixture.detectChanges();
      const input_elt: HTMLInputElement = getInput().nativeElement;

      input_elt.value = 'set_val';
      input_elt.dispatchEvent(newEvent('input'));
      fixture.detectChanges();

      const show_val_elt = getShowVal().nativeElement;

      expect(comp.testVal).toEqual('set_val');
      expect(show_val_elt.textContent).toBe('set_val');
   });

});

Does anyone see how to fix this up so that it will work correctly? I have looked around and can't find any bug reports that explain why this would work without the form but fail with it.

Allen
  • 3,134
  • 5
  • 29
  • 49
  • 3
    Here you go: https://plnkr.co/edit/6GDgummUcyLPVvOudHNp?p=preview. Don't ask me why you need this whenStable(), though. – JB Nizet Apr 07 '17 at 21:54
  • @JBNizet Thanks!!! Works perfect. I have no clue why that makes a difference either but happy to know how to fix it. – Allen Apr 08 '17 at 00:29
  • I'm sure it's because of the asynchronicity of Angular detecting and reacting to changes. Without whenStable, Angular may not have had time to react to the previous changes (from initialization) yet before you interacted with the form (and thus made it have to detect further changes based on your input). Somewhat obscure explanation here: https://angular.io/docs/ts/latest/testing/#!#_whenstable_ – Will Apr 10 '17 at 14:46
  • @Will perhaps so, but the AP seems to confuse people. I'm sure there's a way this could be made to work without having to call that function or a way to make sure that not calling it produced a warning. If this scenario is too specific for such a warning then the API should be split up. – Aluan Haddad Apr 12 '17 at 07:45

0 Answers0