Skip to content

Commit 3940428

Browse files
committed
feat: add wait feature
depracates: waitForDomChange and waitForElement"
1 parent 959c03f commit 3940428

File tree

3 files changed

+109
-24
lines changed

3 files changed

+109
-24
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
FireObject,
77
Queries,
88
queries,
9+
wait,
910
waitForElement,
1011
waitForElementToBeRemoved,
1112
waitForDomChange,
@@ -67,13 +68,15 @@ export interface RenderResult<ComponentType, WrapperType = ComponentType>
6768
*/
6869
rerender: (componentProperties: Partial<ComponentType>) => void;
6970
/**
71+
* @deprecated `waitForDomChange` has been deprecated. Use `wait` instead: https://testing-library.com/docs/dom-testing-library/api-async#wait.
7072
* @description
7173
* Wait for the DOM to change.
7274
*
7375
* For more info see https://testing-library.com/docs/dom-testing-library/api-async#waitfordomchange
7476
*/
7577
waitForDomChange: typeof waitForDomChange;
7678
/**
79+
* @deprecated `waitForElement` has been deprecated. Use a `find*` query (preferred: https://testing-library.com/docs/dom-testing-library/api-queries#findby) or use `wait` instead (it's the same API, so you can find/replace): https://testing-library.com/docs/dom-testing-library/api-async#wait
7780
* @description
7881
* Wait for DOM elements to appear, disappear, or change.
7982
*
@@ -87,6 +90,13 @@ export interface RenderResult<ComponentType, WrapperType = ComponentType>
8790
* For more info see https://testing-library.com/docs/dom-testing-library/api-async#waitforelementtoberemoved
8891
*/
8992
waitForElementToBeRemoved: typeof waitForElementToBeRemoved;
93+
/**
94+
* @description
95+
* When in need to wait for non-deterministic periods of time you can use wait, to wait for your expectations to pass.
96+
*
97+
* For more info see https://testing-library.com/docs/dom-testing-library/api-async#wait
98+
*/
99+
wait: typeof wait;
90100
}
91101

92102
export interface RenderComponentOptions<ComponentType, Q extends Queries = typeof queries> {

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

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
waitForDomChange,
1414
waitForElement,
1515
waitForElementToBeRemoved,
16+
wait,
1617
} from '@testing-library/dom';
1718
import { RenderComponentOptions, RenderDirectiveOptions, RenderResult } from './models';
1819
import { createSelectOptions, createType, tab } from './user-events';
@@ -111,11 +112,11 @@ export async function render<SutType, WrapperType = SutType>(
111112
detectChanges();
112113
};
113114

114-
let router = routes ? (TestBed.get<Router>(Router) as Router) : null;
115-
const zone = TestBed.get<NgZone>(NgZone) as NgZone;
115+
let router = routes ? TestBed.inject(Router) : null;
116+
const zone = TestBed.inject(NgZone);
116117
const navigate = async (elementOrPath: Element | string, basePath = '') => {
117118
if (!router) {
118-
router = TestBed.get<Router>(Router) as Router;
119+
router = TestBed.inject(Router);
119120
}
120121

121122
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
@@ -126,41 +127,75 @@ export async function render<SutType, WrapperType = SutType>(
126127
return result;
127128
};
128129

129-
function componentWaitForDomChange(options?: {
130-
container?: HTMLElement;
131-
timeout?: number;
132-
mutationObserverOptions?: MutationObserverInit;
133-
}): Promise<void> {
134-
const interval = setInterval(detectChanges, 10);
135-
return waitForDomChange({ container: fixture.nativeElement, ...options }).finally(() => clearInterval(interval));
130+
function componentWait(
131+
callback: () => void,
132+
options: {
133+
container?: HTMLElement;
134+
timeout?: number;
135+
interval?: number;
136+
mutationObserverOptions?: {
137+
subtree: boolean;
138+
childList: boolean;
139+
attributes: boolean;
140+
characterData: boolean;
141+
};
142+
} = { container: fixture.nativeElement, interval: 50 },
143+
): Promise<void> {
144+
const interval = setInterval(detectChanges, options.interval);
145+
return wait(callback, options).finally(() => clearInterval(interval));
146+
}
147+
148+
function componentWaitForDomChange(
149+
options: {
150+
container?: HTMLElement;
151+
timeout?: number;
152+
interval?: number;
153+
mutationObserverOptions?: {
154+
subtree: boolean;
155+
childList: boolean;
156+
attributes: boolean;
157+
characterData: boolean;
158+
};
159+
} = { container: fixture.nativeElement, interval: 50 },
160+
): Promise<void> {
161+
const interval = setInterval(detectChanges, options.interval);
162+
return waitForDomChange(options).finally(() => clearInterval(interval));
136163
}
137164

138165
function componentWaitForElement<Result>(
139166
callback: () => Result,
140-
options?: {
167+
options: {
141168
container?: HTMLElement;
142169
timeout?: number;
143-
mutationObserverOptions?: MutationObserverInit;
144-
},
170+
interval?: number;
171+
mutationObserverOptions?: {
172+
subtree: boolean;
173+
childList: boolean;
174+
attributes: boolean;
175+
characterData: boolean;
176+
};
177+
} = { container: fixture.nativeElement, interval: 50 },
145178
): Promise<Result> {
146-
const interval = setInterval(detectChanges, 10);
147-
return waitForElement(callback, { container: fixture.nativeElement, ...options }).finally(() =>
148-
clearInterval(interval),
149-
);
179+
const interval = setInterval(detectChanges, options.interval);
180+
return waitForElement(callback, options).finally(() => clearInterval(interval));
150181
}
151182

152183
function componentWaitForElementToBeRemoved<Result>(
153184
callback: () => Result,
154-
options?: {
185+
options: {
155186
container?: HTMLElement;
156187
timeout?: number;
157-
mutationObserverOptions?: MutationObserverInit;
158-
},
188+
interval?: number;
189+
mutationObserverOptions?: {
190+
subtree: boolean;
191+
childList: boolean;
192+
attributes: boolean;
193+
characterData: boolean;
194+
};
195+
} = { container: fixture.nativeElement, interval: 50 },
159196
): Promise<Result> {
160-
const interval = setInterval(detectChanges, 10);
161-
return waitForElementToBeRemoved(callback, { container: fixture.nativeElement, ...options }).finally(() =>
162-
clearInterval(interval),
163-
);
197+
const interval = setInterval(detectChanges, options.interval);
198+
return waitForElementToBeRemoved(callback, options).finally(() => clearInterval(interval));
164199
}
165200

166201
return {
@@ -174,6 +209,7 @@ export async function render<SutType, WrapperType = SutType>(
174209
type: createType(eventsWithDetectChanges),
175210
selectOptions: createSelectOptions(eventsWithDetectChanges),
176211
tab,
212+
wait: componentWait,
177213
waitForDomChange: componentWaitForDomChange,
178214
waitForElement: componentWaitForElement,
179215
waitForElementToBeRemoved: componentWaitForElementToBeRemoved,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Component } from '@angular/core';
2+
import { timer } from 'rxjs';
3+
import { render } from '../src/public_api';
4+
5+
@Component({
6+
selector: 'fixture',
7+
template: `
8+
<button data-testid="button" (click)="load()">Load</button>
9+
<div>{{ result }}</div>
10+
`,
11+
})
12+
class FixtureComponent {
13+
result = '';
14+
15+
load() {
16+
timer(500).subscribe(() => (this.result = 'Success'));
17+
}
18+
}
19+
20+
test('waits for assertion to become true', async () => {
21+
const { queryByText, getByTestId, click, wait, getByText } = await render(FixtureComponent);
22+
23+
expect(queryByText('Success')).toBeNull();
24+
25+
click(getByTestId('button'));
26+
27+
await wait(() => getByText('Success'));
28+
getByText('Success');
29+
});
30+
31+
test('allows to override options', async () => {
32+
const { getByTestId, click, wait, getByText } = await render(FixtureComponent);
33+
34+
click(getByTestId('button'));
35+
36+
await expect(wait(() => getByText('Success'), { timeout: 200 })).rejects.toThrow(
37+
/Unable to find an element with the text: Success/i,
38+
);
39+
});

0 commit comments

Comments
 (0)