From 0cbd0fb1f8370743cf32a67ae1ef30a6af868af5 Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Tue, 6 Jul 2021 20:25:36 +0200 Subject: [PATCH] fix: invoke change detection after callback in waitForElementToBeRemoved (#236) --- .../src/lib/testing-library.ts | 3 +- .../testing-library/tests/integration.spec.ts | 134 ++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 projects/testing-library/tests/integration.spec.ts diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts index b521ab5d..13ed320e 100644 --- a/projects/testing-library/src/lib/testing-library.ts +++ b/projects/testing-library/src/lib/testing-library.ts @@ -352,8 +352,9 @@ async function waitForElementToBeRemovedWrapper( } return await dtlWaitForElementToBeRemoved(() => { + const result = cb(); detectChanges(); - return cb(); + return result; }, options); } diff --git a/projects/testing-library/tests/integration.spec.ts b/projects/testing-library/tests/integration.spec.ts new file mode 100644 index 00000000..49e9094e --- /dev/null +++ b/projects/testing-library/tests/integration.spec.ts @@ -0,0 +1,134 @@ +import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import userEvent from '@testing-library/user-event'; +import { of, BehaviorSubject } from 'rxjs'; +import { debounceTime, switchMap, map, startWith } from 'rxjs/operators'; +import { render, screen, waitFor, waitForElementToBeRemoved, within } from '../src/lib/testing-library'; + +const DEBOUNCE_TIME = 1_000; + +@Injectable() +class EntitiesService { + fetchAll() { + return of([]); + } +} + +@Injectable() +class ModalService { + open(...args: any[]) { + console.log('open', ...args); + } +} + +@Component({ + template: ` +

Entities Title

+ + + + `, +}) +class EntitiesComponent { + query = new BehaviorSubject(''); + readonly entities = this.query.pipe( + debounceTime(DEBOUNCE_TIME), + switchMap((q) => this.entitiesService.fetchAll().pipe(map((ent) => ent.filter((e) => e.name.includes(q))))), + startWith(entities), + ); + + constructor(private entitiesService: EntitiesService, private modalService: ModalService) {} + + newEntityClicked() { + this.modalService.open('new entity'); + } + + editEntityClicked(entity: string) { + setTimeout(() => { + this.modalService.open('edit entity', entity); + }, 100); + } +} + +@Component({ + selector: 'atl-table', + template: ` + + + + + +
{{ entity.name }}
+ `, +}) +class TableComponent { + @Input() entities: any[]; + @Output() edit = new EventEmitter(); +} + +const entities = [ + { + id: 1, + name: 'Entity 1', + }, + { + id: 2, + name: 'Entity 2', + }, + { + id: 3, + name: 'Entity 3', + }, +]; + +it('renders the table', async () => { + jest.useFakeTimers(); + + await render(EntitiesComponent, { + declarations: [TableComponent], + providers: [ + { + provide: EntitiesService, + useValue: { + fetchAll: jest.fn().mockReturnValue(of(entities)), + }, + }, + { + provide: ModalService, + useValue: { + open: jest.fn(), + }, + }, + ], + }); + const modalMock = TestBed.inject(ModalService); + + expect(await screen.findByRole('heading', { name: /Entities Title/i })).toBeInTheDocument(); + + expect(await screen.findByRole('cell', { name: /Entity 1/i })).toBeInTheDocument(); + expect(await screen.findByRole('cell', { name: /Entity 2/i })).toBeInTheDocument(); + expect(await screen.findByRole('cell', { name: /Entity 3/i })).toBeInTheDocument(); + + userEvent.type(await screen.findByRole('textbox', { name: /Search entities/i }), 'Entity 2', {}); + + jest.advanceTimersByTime(DEBOUNCE_TIME); + + await waitForElementToBeRemoved(() => screen.queryByRole('cell', { name: /Entity 1/i })); + expect(await screen.findByRole('cell', { name: /Entity 2/i })).toBeInTheDocument(); + + userEvent.click(await screen.findByRole('button', { name: /New Entity/i })); + expect(modalMock.open).toHaveBeenCalledWith('new entity'); + + const row = await screen.findByRole('row', { + name: /Entity 2/i, + }); + userEvent.click( + await within(row).findByRole('button', { + name: /edit/i, + }), + ); + waitFor(() => expect(modalMock.open).toHaveBeenCalledWith('edit entity', 'Entity 2')); +});