or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-utilities.mdasync.mdconfig.mdconfiguration.mddebugging.mdevents.mdindex.mdqueries.mdquery-helpers.mdrole-utilities.mdscreen.mdutilities.mdwithin.md

async.mddocs/

0

# Async Utilities

1

2

Wait for conditions or elements to appear/disappear in the DOM for testing asynchronous behavior.

3

4

## waitFor

5

6

Wait for a callback to succeed without throwing, with polling and MutationObserver optimization.

7

8

```typescript

9

function waitFor<T>(

10

callback: () => Promise<T> | T,

11

options?: waitForOptions

12

): Promise<T>;

13

14

interface waitForOptions {

15

container?: HTMLElement; // Element to observe for mutations (default: document)

16

timeout?: number; // Max wait time in ms (default: 1000)

17

interval?: number; // Polling interval in ms (default: 50)

18

onTimeout?: (error: Error) => Error; // Custom timeout error

19

mutationObserverOptions?: MutationObserverInit;

20

}

21

```

22

23

Usage:

24

```javascript

25

import {waitFor, screen} from '@testing-library/dom';

26

27

// Wait for element to appear

28

await waitFor(() => {

29

expect(screen.getByText('Loaded')).toBeInTheDocument();

30

});

31

32

// Wait for specific condition

33

await waitFor(() => {

34

const counter = screen.getByTestId('counter');

35

expect(counter.textContent).toBe('5');

36

});

37

38

// Custom timeout

39

await waitFor(

40

() => expect(screen.getByText('Slow')).toBeInTheDocument(),

41

{timeout: 5000}

42

);

43

44

// With MutationObserver optimization

45

await waitFor(

46

() => expect(screen.getByText('Content')).toBeInTheDocument(),

47

{container: screen.getByTestId('dynamic-content')}

48

);

49

50

// Custom error

51

await waitFor(

52

() => expect(screen.getByText('Ready')).toBeInTheDocument(),

53

{

54

timeout: 5000,

55

onTimeout: (error) => new Error(`Custom: ${error.message}`)

56

}

57

);

58

```

59

60

## waitForElementToBeRemoved

61

62

Wait for elements to be removed from the DOM. Throws immediately if elements not present.

63

64

```typescript

65

function waitForElementToBeRemoved<T>(

66

callback: T | (() => T),

67

options?: waitForOptions

68

): Promise<void>;

69

```

70

71

Usage:

72

```javascript

73

import {waitForElementToBeRemoved, screen} from '@testing-library/dom';

74

75

// Wait for specific element

76

const spinner = screen.getByRole('status', {name: /loading/i});

77

await waitForElementToBeRemoved(spinner);

78

79

// Using query function

80

await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));

81

82

// Multiple elements

83

const errors = screen.getAllByRole('alert');

84

await waitForElementToBeRemoved(errors);

85

86

// With timeout

87

await waitForElementToBeRemoved(

88

() => screen.queryByTestId('modal'),

89

{timeout: 3000}

90

);

91

```

92

93

## Find Queries (Built-in Async)

94

95

All `findBy*` and `findAllBy*` queries are async and use `waitFor` internally:

96

97

```javascript

98

// Using findBy (recommended for async content)

99

const element = await screen.findByText('Loaded content');

100

101

// Equivalent to:

102

const element = await waitFor(() => screen.getByText('Loaded content'));

103

104

// With options

105

const element = await screen.findByRole(

106

'alert',

107

{name: /error/i},

108

{timeout: 3000} // pass to waitForOptions

109

);

110

111

// findAllBy for multiple elements

112

const items = await screen.findAllByRole('listitem');

113

```

114

115

## Best Practices

116

117

### Use `findBy*` for async content

118

```javascript

119

// Good

120

const element = await screen.findByText('Async content');

121

122

// Works but less idiomatic

123

await waitFor(() => {

124

expect(screen.getByText('Async content')).toBeInTheDocument();

125

});

126

```

127

128

### Use `waitFor` for complex conditions

129

```javascript

130

// Multiple related assertions

131

await waitFor(() => {

132

expect(screen.getByText('Username')).toBeInTheDocument();

133

expect(screen.getByText('Email')).toBeInTheDocument();

134

expect(screen.getByText('Status: Active')).toBeInTheDocument();

135

});

136

137

// Complex logic

138

await waitFor(() => {

139

const items = screen.getAllByRole('listitem');

140

expect(items.length).toBe(5);

141

expect(items[0]).toHaveTextContent('First item');

142

});

143

```

144

145

### Use `waitForElementToBeRemoved` for removal

146

```javascript

147

// Good - explicit removal check

148

await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));

149

150

// Less clear

151

await waitFor(() => {

152

expect(screen.queryByText('Loading...')).not.toBeInTheDocument();

153

});

154

```

155

156

## Common Patterns

157

158

### Waiting after user interaction

159

```javascript

160

fireEvent.click(screen.getByRole('button', {name: /load/i}));

161

162

await waitFor(() => {

163

expect(screen.getByText('Data loaded')).toBeInTheDocument();

164

});

165

```

166

167

### API response handling

168

```javascript

169

// Submit form

170

fireEvent.submit(screen.getByRole('form'));

171

172

// Wait for loading to finish

173

await waitForElementToBeRemoved(() =>

174

screen.queryByRole('status', {name: /loading/i})

175

);

176

177

// Verify result

178

expect(screen.getByText('Success!')).toBeInTheDocument();

179

```

180

181

### Polling for state changes

182

```javascript

183

// Start process

184

fireEvent.click(screen.getByRole('button', {name: /start/i}));

185

186

// Poll for completion

187

await waitFor(

188

() => {

189

const progress = screen.getByTestId('progress');

190

expect(progress).toHaveTextContent('100%');

191

},

192

{interval: 200, timeout: 10000}

193

);

194

```

195

196

## Error Handling

197

198

```javascript

199

try {

200

await waitFor(

201

() => expect(screen.getByText('Never appears')).toBeInTheDocument(),

202

{timeout: 1000}

203

);

204

} catch (error) {

205

console.error('Element did not appear within 1 second');

206

}

207

208

// Custom timeout messages

209

await waitFor(

210

() => {

211

const element = screen.queryByTestId('status');

212

if (!element || element.textContent !== 'ready') {

213

throw new Error('Status is not ready');

214

}

215

},

216

{

217

timeout: 5000,

218

onTimeout: (error) => {

219

const status = screen.queryByTestId('status');

220

const currentStatus = status?.textContent || 'not found';

221

return new Error(

222

`Timeout waiting for ready status. Current: ${currentStatus}`

223

);

224

}

225

}

226

);

227

```

228

229