0
# Async Utilities
1
2
Utilities for waiting for asynchronous operations and DOM changes in Vue components.
3
4
## Capabilities
5
6
### WaitFor Function
7
8
Wait for a condition to be true by repeatedly executing a callback function until it succeeds or times out.
9
10
```typescript { .api }
11
/**
12
* Wait for a condition to be true
13
* @param callback - Function to execute repeatedly
14
* @param options - Wait configuration options
15
* @returns Promise resolving to callback return value
16
*/
17
function waitFor<T>(
18
callback: () => T | Promise<T>,
19
options?: {
20
/** Element to observe for mutations */
21
container?: Element;
22
/** Maximum time to wait in milliseconds */
23
timeout?: number;
24
/** Interval between attempts in milliseconds */
25
interval?: number;
26
/** Custom timeout error handler */
27
onTimeout?: (error: Error) => Error;
28
/** MutationObserver configuration */
29
mutationObserverOptions?: MutationObserverInit;
30
}
31
): Promise<T>;
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { render, screen, waitFor, fireEvent } from "@testing-library/vue";
38
import AsyncComponent from "./AsyncComponent.vue";
39
40
render(AsyncComponent);
41
42
// Wait for element to appear
43
await waitFor(() => {
44
expect(screen.getByText("Loading complete")).toBeInTheDocument();
45
});
46
47
// Wait for API call to complete
48
const button = screen.getByRole("button", { name: "Load Data" });
49
await fireEvent.click(button);
50
51
await waitFor(() => {
52
expect(screen.getByText("Data loaded successfully")).toBeInTheDocument();
53
}, { timeout: 5000 });
54
55
// Wait for element to have specific attribute
56
await waitFor(() => {
57
const element = screen.getByTestId("status");
58
expect(element).toHaveAttribute("data-status", "complete");
59
});
60
```
61
62
### WaitForElementToBeRemoved Function
63
64
Wait for one or more elements to be removed from the DOM.
65
66
```typescript { .api }
67
/**
68
* Wait for element(s) to be removed from DOM
69
* @param element - Element, elements array, or callback returning elements to wait for removal
70
* @param options - Wait configuration options
71
* @returns Promise that resolves when elements are removed
72
*/
73
function waitForElementToBeRemoved<T>(
74
element: Element | Element[] | (() => Element | Element[] | null),
75
options?: {
76
/** Element to observe for mutations */
77
container?: Element;
78
/** Maximum time to wait in milliseconds */
79
timeout?: number;
80
/** Interval between attempts in milliseconds */
81
interval?: number;
82
/** Custom timeout error handler */
83
onTimeout?: (error: Error) => Error;
84
/** MutationObserver configuration */
85
mutationObserverOptions?: MutationObserverInit;
86
}
87
): Promise<void>;
88
```
89
90
**Usage Examples:**
91
92
```typescript
93
// Wait for loading spinner to disappear
94
const spinner = screen.getByTestId("loading-spinner");
95
await waitForElementToBeRemoved(spinner);
96
97
// Wait for multiple elements to be removed
98
const modals = screen.getAllByRole("dialog");
99
await waitForElementToBeRemoved(modals);
100
101
// Wait using a callback (handles cases where element might not exist initially)
102
await waitForElementToBeRemoved(() =>
103
screen.queryByText("Temporary message")
104
);
105
106
// With custom timeout
107
await waitForElementToBeRemoved(
108
screen.getByText("Processing..."),
109
{ timeout: 10000 }
110
);
111
```
112
113
## Configuration Options
114
115
### Default Configuration
116
117
```typescript { .api }
118
interface WaitForOptions {
119
/** Default container for mutation observation (document.body) */
120
container?: Element;
121
/** Default timeout in milliseconds (1000ms) */
122
timeout?: number;
123
/** Default interval between checks in milliseconds (50ms) */
124
interval?: number;
125
/** Custom error handler for timeouts */
126
onTimeout?: (error: Error) => Error;
127
/** MutationObserver options for DOM change detection */
128
mutationObserverOptions?: MutationObserverInit;
129
}
130
```
131
132
### MutationObserver Options
133
134
```typescript { .api }
135
interface MutationObserverInit {
136
/** Observe attribute changes */
137
attributes?: boolean;
138
/** List of attribute names to observe */
139
attributeFilter?: string[];
140
/** Include old attribute values in mutations */
141
attributeOldValue?: boolean;
142
/** Observe character data changes */
143
characterData?: boolean;
144
/** Include old character data in mutations */
145
characterDataOldValue?: boolean;
146
/** Observe child list changes */
147
childList?: boolean;
148
/** Observe all descendant mutations */
149
subtree?: boolean;
150
}
151
```
152
153
## Common Async Patterns
154
155
### API Loading States
156
157
```typescript
158
import { render, screen, waitFor, fireEvent } from "@testing-library/vue";
159
160
// Test loading → success flow
161
const loadButton = screen.getByRole("button", { name: "Load Data" });
162
await fireEvent.click(loadButton);
163
164
// Wait for loading to start
165
await waitFor(() => {
166
expect(screen.getByText("Loading...")).toBeInTheDocument();
167
});
168
169
// Wait for loading to finish
170
await waitForElementToBeRemoved(screen.getByText("Loading..."));
171
172
// Verify success state
173
expect(screen.getByText("Data loaded")).toBeInTheDocument();
174
```
175
176
### Form Validation
177
178
```typescript
179
// Test async form validation
180
const emailInput = screen.getByLabelText("Email");
181
const submitButton = screen.getByRole("button", { name: "Submit" });
182
183
await fireEvent.update(emailInput, "invalid-email");
184
await fireEvent.click(submitButton);
185
186
// Wait for validation error
187
await waitFor(() => {
188
expect(screen.getByText("Please enter a valid email")).toBeInTheDocument();
189
});
190
```
191
192
### Component State Changes
193
194
```typescript
195
// Test reactive state updates
196
const toggleButton = screen.getByRole("button", { name: "Toggle Theme" });
197
await fireEvent.click(toggleButton);
198
199
// Wait for theme to change
200
await waitFor(() => {
201
const root = document.documentElement;
202
expect(root).toHaveAttribute("data-theme", "dark");
203
});
204
```
205
206
### Navigation and Route Changes
207
208
```typescript
209
// Test Vue Router navigation
210
const navLink = screen.getByRole("link", { name: "About" });
211
await fireEvent.click(navLink);
212
213
// Wait for new page content
214
await waitFor(() => {
215
expect(screen.getByText("About Page")).toBeInTheDocument();
216
});
217
```
218
219
## Error Handling
220
221
### Timeout Errors
222
223
When `waitFor` times out, it throws a detailed error with the last error from the callback:
224
225
```typescript
226
try {
227
await waitFor(() => {
228
expect(screen.getByText("Never appears")).toBeInTheDocument();
229
}, { timeout: 1000 });
230
} catch (error) {
231
// Error includes timeout info and last expectation failure
232
console.log(error.message);
233
}
234
```
235
236
### Custom Error Handling
237
238
```typescript
239
await waitFor(() => {
240
expect(screen.getByText("Content")).toBeInTheDocument();
241
}, {
242
timeout: 5000,
243
onTimeout: (error) => {
244
return new Error(`Custom timeout message: ${error.message}`);
245
}
246
});
247
```
248
249
## Global Configuration
250
251
Configure default timeouts and intervals using DOM Testing Library's configure function:
252
253
```typescript { .api }
254
/**
255
* Configure global library behavior
256
* @param options - Configuration options
257
*/
258
function configure(options: {
259
/** Default timeout for async utilities in milliseconds */
260
asyncUtilTimeout?: number;
261
/** Attribute name used for getByTestId queries */
262
testIdAttribute?: string;
263
/** Whether to include hidden elements by default */
264
defaultHidden?: boolean;
265
/** CSS selector for elements to ignore */
266
defaultIgnore?: string;
267
/** Show original stack traces in errors */
268
showOriginalStackTrace?: boolean;
269
/** Enable query suggestions in error messages */
270
throwSuggestions?: boolean;
271
}): void;
272
273
/**
274
* Get current configuration
275
* @returns Current configuration object
276
*/
277
function getConfig(): {
278
asyncUtilTimeout: number;
279
testIdAttribute: string;
280
defaultHidden: boolean;
281
defaultIgnore: string;
282
showOriginalStackTrace: boolean;
283
throwSuggestions: boolean;
284
};
285
```
286
287
**Usage Examples:**
288
289
```typescript
290
import { configure } from "@testing-library/vue";
291
292
// Configure timeout for all async utilities
293
configure({
294
asyncUtilTimeout: 2000,
295
});
296
297
// Configure test ID attribute
298
configure({
299
testIdAttribute: 'data-cy',
300
});
301
302
// Enable query suggestions for better error messages
303
configure({
304
throwSuggestions: true,
305
});
306
```