I have an Angular module with a list, card, detail, and edit components.
The list template would look like this:
<own-list>
<button [routerLink]="['/', 'mod', 'create']" data-testid="new-request">Create</button>
<own-card *ngFor="let elem of elements" [routerLink]="['/', 'mod', elem.id]"></own-card>
<own-list>
When testing that when clicking the card it goes to its detail page:
test('it should navigate to detail when clicking a card', fakeAsync(() => {
const cardElems = rootFixture.debugElement.queryAll(By.css('own-card'));
// Navigate to top card detail
cardElems[0].nativeElement.click();
tick();
fixture.detectChanges();
// Check route is correct
expect(location.path()).toBe(`/mod/${sortedElements[0].id}`);
}));
works fine.
When testing that when clicking the create button goes to edit:
test('it should navigate to edit when clicking new request', fakeAsync(() => {
const debugCreateBtn = rootFixture.debugElement.query(By.css(`[data-testid="new-request"]`));
// Navigate to edit
debugCreateBtn.nativeElement.click();
tick();
fixture.detectChanges();
// Check route is correct
expect(location.path()).toBe(`/mod/create`);
}));
it fails with 1 periodic timer(s) still in the queue
.
If I add flush
as such:
test('it should navigate to edit when clicking new request', fakeAsync(() => {
const debugCreateBtn = rootFixture.debugElement.query(By.css(`[data-testid="new-request"]`));
// Navigate to edit
debugCreateBtn.nativeElement.click();
tick();
fixture.detectChanges();
flush(); // <-- new flush
// Check route is correct
expect(location.path()).toBe(`/mod/create`);
}));
it works well.
And if I switch to triggerEventHandler
:
test('it should navigate to edit when clicking new request', fakeAsync(() => {
const debugCreateBtn = rootFixture.debugElement.query(By.css(`[data-testid="new-request"]`));
// Navigate to edit
// Run in ngZone to avoid warning
fixture.ngZone.run(() => debugCreateBtn.triggerEventHandler('click', { button: 0 }));
tick();
fixture.detectChanges();
// Check route is correct
expect(location.path()).toBe(`/mod/create`);
}));
then it works, without the flush.
Could someone explain why it works or doesn't in each situation?
flushMicrotasks
flushes pending microtasks, tick
flushes pending microtasks and progresses time, flush
flushes pending microtasks, macrotasks and progresses time, all within the context of fakeAsync
.
Native events like click
are macrotasks. triggerEventHandler
and EventEmitter
s without async
set to true
use microtasks.