Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0cbd0fb

Browse files
fix: invoke change detection after callback in waitForElementToBeRemoved (testing-library#236)
1 parent 78646b0 commit 0cbd0fb

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

‎projects/testing-library/src/lib/testing-library.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,9 @@ async function waitForElementToBeRemovedWrapper<T>(
352352
}
353353

354354
return await dtlWaitForElementToBeRemoved(() => {
355+
const result = cb();
355356
detectChanges();
356-
return cb();
357+
return result;
357358
}, options);
358359
}
359360

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core';
2+
import { TestBed } from '@angular/core/testing';
3+
import userEvent from '@testing-library/user-event';
4+
import { of, BehaviorSubject } from 'rxjs';
5+
import { debounceTime, switchMap, map, startWith } from 'rxjs/operators';
6+
import { render, screen, waitFor, waitForElementToBeRemoved, within } from '../src/lib/testing-library';
7+
8+
const DEBOUNCE_TIME = 1_000;
9+
10+
@Injectable()
11+
class EntitiesService {
12+
fetchAll() {
13+
return of([]);
14+
}
15+
}
16+
17+
@Injectable()
18+
class ModalService {
19+
open(...args: any[]) {
20+
console.log('open', ...args);
21+
}
22+
}
23+
24+
@Component({
25+
template: `
26+
<h1>Entities Title</h1>
27+
<button (click)="newEntityClicked()">Create New Entity</button>
28+
<label>
29+
Search entities
30+
<input type="text" (input)="query.next($event.target.value)" />
31+
</label>
32+
<atl-table [entities]="entities | async" (edit)="editEntityClicked($event)"></atl-table>
33+
`,
34+
})
35+
class EntitiesComponent {
36+
query = new BehaviorSubject<string>('');
37+
readonly entities = this.query.pipe(
38+
debounceTime(DEBOUNCE_TIME),
39+
switchMap((q) => this.entitiesService.fetchAll().pipe(map((ent) => ent.filter((e) => e.name.includes(q))))),
40+
startWith(entities),
41+
);
42+
43+
constructor(private entitiesService: EntitiesService, private modalService: ModalService) {}
44+
45+
newEntityClicked() {
46+
this.modalService.open('new entity');
47+
}
48+
49+
editEntityClicked(entity: string) {
50+
setTimeout(() => {
51+
this.modalService.open('edit entity', entity);
52+
}, 100);
53+
}
54+
}
55+
56+
@Component({
57+
selector: 'atl-table',
58+
template: `
59+
<table>
60+
<tr *ngFor="let entity of entities">
61+
<td>{{ entity.name }}</td>
62+
<td><button (click)="edit.next(entity.name)">Edit</button></td>
63+
</tr>
64+
</table>
65+
`,
66+
})
67+
class TableComponent {
68+
@Input() entities: any[];
69+
@Output() edit = new EventEmitter<string>();
70+
}
71+
72+
const entities = [
73+
{
74+
id: 1,
75+
name: 'Entity 1',
76+
},
77+
{
78+
id: 2,
79+
name: 'Entity 2',
80+
},
81+
{
82+
id: 3,
83+
name: 'Entity 3',
84+
},
85+
];
86+
87+
it('renders the table', async () => {
88+
jest.useFakeTimers();
89+
90+
await render(EntitiesComponent, {
91+
declarations: [TableComponent],
92+
providers: [
93+
{
94+
provide: EntitiesService,
95+
useValue: {
96+
fetchAll: jest.fn().mockReturnValue(of(entities)),
97+
},
98+
},
99+
{
100+
provide: ModalService,
101+
useValue: {
102+
open: jest.fn(),
103+
},
104+
},
105+
],
106+
});
107+
const modalMock = TestBed.inject(ModalService);
108+
109+
expect(await screen.findByRole('heading', { name: /EntitiesTitle/i })).toBeInTheDocument();
110+
111+
expect(await screen.findByRole('cell', { name: /Entity1/i })).toBeInTheDocument();
112+
expect(await screen.findByRole('cell', { name: /Entity2/i })).toBeInTheDocument();
113+
expect(await screen.findByRole('cell', { name: /Entity3/i })).toBeInTheDocument();
114+
115+
userEvent.type(await screen.findByRole('textbox', { name: /Searchentities/i }), 'Entity 2', {});
116+
117+
jest.advanceTimersByTime(DEBOUNCE_TIME);
118+
119+
await waitForElementToBeRemoved(() => screen.queryByRole('cell', { name: /Entity1/i }));
120+
expect(await screen.findByRole('cell', { name: /Entity2/i })).toBeInTheDocument();
121+
122+
userEvent.click(await screen.findByRole('button', { name: /NewEntity/i }));
123+
expect(modalMock.open).toHaveBeenCalledWith('new entity');
124+
125+
const row = await screen.findByRole('row', {
126+
name: /Entity2/i,
127+
});
128+
userEvent.click(
129+
await within(row).findByRole('button', {
130+
name: /edit/i,
131+
}),
132+
);
133+
waitFor(() => expect(modalMock.open).toHaveBeenCalledWith('edit entity', 'Entity 2'));
134+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /