dispatchEvent doesn't trigger ngModel changes
See original GitHub issueI’m submitting a … (check one with “x”)
[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
The dispatchEvent
function from @angular/platform-browser/testing/browser_util
and EventTarget.dispatchEvent
doesn’t trigger NgModel
changes. I have following output in karma (I trimmed debug backtrace):
✖ should modify message
Chrome 55.0.2883 (Mac OS X 10.11.4)
Expected undefined to be 'The longest content.'.
...
Expected undefined to be '4'.
...
Expected behavior Pass the test
Minimal reproduction of the problem with instructions My code:
review-form.component.ts:
import {
Input,
Output,
Component,
ViewChild,
EventEmitter,
} from '@angular/core';
import { NgForm } from '@angular/forms';
import { Product } from 'app/model';
import {
AbstractForm,
FormErrorList,
ProductReviewMessage,
} from 'app/core';
@Component({
selector: 'l-product-show-review-form',
templateUrl: './review-form.component.html',
})
export class ProductShowReviewFormComponent extends AbstractForm {
@Input()
public model: ProductReviewMessage;
@Input()
public product: Product;
@Output()
public formSubmit = new EventEmitter<any>();
@Input()
public set errors(errors: FormErrorList) {
this.applyFormErrorList(errors);
}
@ViewChild('form')
public set ngForm(ngForm: NgForm) {
this.form = ngForm.form;
}
public doFormSubmit() {
this.formSubmit.emit();
}
}
review-form.component.html:
<form #form="ngForm" (ngSubmit)="doFormSubmit()">
<div [lControlWrapper]="content">
<div class="clearfix m-b-1">
<label class="form-control-static" for="reviewContent" i18n>l.product.show.reviewForm.content</label>
<div>
<textarea
class="form-control"
id="reviewContent"
name="content"
required
[(ngModel)]="model.content"
#content="ngModel"
></textarea>
</div>
</div>
<l-control-error [control]="content"></l-control-error>
</div>
<div [lControlWrapper]="rating">
<div class="clearfix m-b-1">
<label class="form-control-static" for="reviewRating" i18n>l.product.show.reviewForm.rating</label>
<div>
<input
type="number"
class="form-control l-number-input"
id="reviewRating"
name="rating"
required
[(ngModel)]="model.rating"
#rating="ngModel"
/>
</div>
</div>
<l-control-error [control]="rating"></l-control-error>
</div>
<div>
<button
type="submit"
class="btn btn-primary btn-lg btn-block"
[disabled]="!form.form.valid"
i18n
>l.product.show.reviewForm.submit</button>
</div>
</form>
[other file that executes just one time in test environment]:
const { TestBed } = require('@angular/core/testing');
const { platformBrowserDynamicTesting, BrowserDynamicTestingModule } = require('@angular/platform-browser-dynamic/testing');
TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
review-form.component.spec.ts:
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { dispatchEvent } from '@angular/platform-browser/testing/browser_util';
import { ComponentFixture, TestBed, async, fakeAsync } from '@angular/core/testing';
import { Product } from 'app/model';
import { SharedModule } from 'app/shared';
import { ProductReviewMessage } from 'app/core';
import { ProductShowReviewFormComponent } from './review-form.component';
function newEvent(eventName: string, bubbles = false, cancelable = false) {
let evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
evt.initCustomEvent(eventName, bubbles, cancelable, null);
return evt;
}
describe('ProductShowReviewFormComponent', () => {
let fixture: ComponentFixture<ProductShowReviewFormComponent>;
let comp: ProductShowReviewFormComponent;
let submitSpy: jasmine.Spy;
let contentInput: HTMLTextAreaElement;
let ratingInput: HTMLInputElement;
let form: DebugElement;
let product: Product;
let message: ProductReviewMessage;
beforeEach(async(() => {
TestBed
.configureTestingModule({
imports: [SharedModule],
declarations: [ProductShowReviewFormComponent],
})
.compileComponents()
;
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductShowReviewFormComponent);
comp = fixture.componentInstance;
submitSpy = spyOn(comp.formSubmit, 'emit');
contentInput = fixture.debugElement.query(By.css('#reviewContent')).nativeElement;
ratingInput = fixture.debugElement.query(By.css('#reviewRating')).nativeElement;
form = fixture.debugElement.query(By.css('form'));
product = new Product();
product.id = 1;
comp.product = product;
message = new ProductReviewMessage();
message.product = product;
comp.model = message;
});
it('should modify message', fakeAsync(() => {
const expectedContent = 'The longest content.';
const expectedRating = '4';
contentInput.value = expectedContent;
dispatchEvent(contentInput, newEvent('input'));
// contentInput.dispatchEvent(newEvent('input')); // doesn't trigger too
ratingInput.value = expectedRating;
dispatchEvent(ratingInput, 'input');
fixture.detectChanges();
// tick(); // doesn't help
form.triggerEventHandler('submit', null);
expect(submitSpy.calls.any()).toBe(true);
expect(comp.model.content).toBe(expectedContent);
expect(comp.model.rating).toBe(expectedRating);
}));
});
What is the motivation / use case for changing the behavior? I need to test a form.
Please tell us about your environment:
-
OS: OSX El Capitan v. 10.11.4
-
Package Manager: npm 3.10.9
-
Angular version: 2.0.1
-
Browser: Chrome 55.0.2883 (Mac OS X 10.11.4)
-
Language: TypeScript ^2.0.2
-
Node (for AoT issues):
node --version
= v4.4.2
Issue Analytics
- State:
- Created 7 years ago
- Comments:6 (1 by maintainers)
First i guess your second
beforeEach
shoud be async as well as yourit
case and it should wait until zone gets stable Second: yourmodel.rating
property is mapped toinput[type="number"]
so you’re trying to compare string with number.Here is how it could look (Live Example):
This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.