or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdcommands.mdcontext.mdindex.mdinteractions.mdlocators.mdproviders.mdserver.mdutilities.md

assertions.mddocs/

0

# Enhanced Assertions

1

2

DOM-specific matchers extending Vitest's expect API with browser-aware assertions and element polling for reliable test assertions in browser environments.

3

4

## Capabilities

5

6

### Element Polling

7

8

Enhanced expectation API that polls for elements to handle asynchronous DOM changes.

9

10

```typescript { .api }

11

/**

12

* Element polling expectation - shorthand for expect.poll(() => locator.element())

13

* Waits for element to exist and be available for assertions

14

* @param element - Element, locator, or null to poll for

15

* @param options - Polling options including timeout and interval

16

* @returns Promise-based assertion interface for DOM elements

17

*/

18

expect.element<T extends Element | Locator | null>(

19

element: T,

20

options?: ExpectPollOptions

21

): PromisifyDomAssertion<Awaited<Element | null>>;

22

23

interface ExpectPollOptions {

24

/** Maximum time to wait for condition in milliseconds */

25

timeout?: number;

26

/** Polling interval in milliseconds */

27

interval?: number;

28

/** Custom error message */

29

message?: string;

30

}

31

```

32

33

**Usage Examples:**

34

35

```typescript

36

import { expect } from "vitest";

37

import { page } from "@vitest/browser/context";

38

39

// Wait for element to appear and assert visibility

40

await expect.element(page.getByText("Loading complete")).toBeVisible();

41

42

// Poll with custom timeout

43

await expect.element(

44

page.getByRole("alert"),

45

{ timeout: 10000 }

46

).toBeInTheDocument();

47

48

// Poll for element that might not exist

49

await expect.element(

50

page.getByText("Optional message").query(),

51

{ timeout: 2000 }

52

).toBeNull();

53

54

// Poll with custom interval and message

55

await expect.element(

56

page.getByTestId("data-loaded"),

57

{

58

timeout: 5000,

59

interval: 100,

60

message: "Data should load within 5 seconds"

61

}

62

).toBeVisible();

63

64

// Complex polling scenarios

65

test("async form submission", async () => {

66

await userEvent.click(page.getByRole("button", { name: "Submit" }));

67

68

// Wait for loading spinner

69

await expect.element(page.getByTestId("spinner")).toBeVisible();

70

71

// Wait for spinner to disappear

72

await expect.element(page.getByTestId("spinner")).not.toBeVisible();

73

74

// Wait for success message

75

await expect.element(page.getByText("Form submitted successfully")).toBeVisible();

76

});

77

```

78

79

### DOM-Specific Matchers

80

81

Extended assertion methods specifically designed for DOM elements and browser testing.

82

83

#### Visibility Matchers

84

85

```typescript { .api }

86

/**

87

* Assert element visibility states

88

*/

89

interface DOMMatchers {

90

/** Element is visible to the user */

91

toBeVisible(): Promise<void>;

92

/** Element exists in DOM but may not be visible */

93

toBeInTheDocument(): Promise<void>;

94

/** Element is hidden from the user */

95

toBeHidden(): Promise<void>;

96

}

97

```

98

99

**Usage Examples:**

100

101

```typescript

102

// Basic visibility assertions

103

await expect.element(page.getByRole("button")).toBeVisible();

104

await expect.element(page.getByTestId("hidden-panel")).toBeHidden();

105

await expect.element(page.getByText("Success")).toBeInTheDocument();

106

107

// Negative assertions

108

await expect.element(page.getByRole("alert")).not.toBeVisible();

109

await expect.element(page.getByText("Error")).not.toBeInTheDocument();

110

111

// Conditional visibility

112

test("menu visibility", async () => {

113

const menu = page.getByRole("menu");

114

const menuButton = page.getByRole("button", { name: "Menu" });

115

116

// Menu initially hidden

117

await expect.element(menu).toBeHidden();

118

119

// Click to show menu

120

await userEvent.click(menuButton);

121

await expect.element(menu).toBeVisible();

122

123

// Click to hide menu

124

await userEvent.click(menuButton);

125

await expect.element(menu).toBeHidden();

126

});

127

```

128

129

#### Content Matchers

130

131

```typescript { .api }

132

/**

133

* Assert element content and text

134

*/

135

interface ContentMatchers {

136

/** Element contains specific text content */

137

toHaveTextContent(text: string | RegExp): Promise<void>;

138

/** Input element has specific value */

139

toHaveValue(value: string | number): Promise<void>;

140

/** Element has specific attribute with value */

141

toHaveAttribute(name: string, value?: string | RegExp): Promise<void>;

142

/** Element has specific CSS class */

143

toHaveClass(className: string | RegExp | (string | RegExp)[]): Promise<void>;

144

/** Element has specific CSS styles */

145

toHaveStyle(style: string | object): Promise<void>;

146

/** Element is empty (has no content) */

147

toBeEmptyDOMElement(): Promise<void>;

148

/** Element contains another element */

149

toContainElement(element: Element): Promise<void>;

150

/** Element contains specific HTML content */

151

toContainHTML(html: string): Promise<void>;

152

}

153

```

154

155

**Usage Examples:**

156

157

```typescript

158

// Text content assertions

159

await expect.element(page.getByRole("heading")).toHaveTextContent("Welcome");

160

await expect.element(page.getByTestId("counter")).toHaveTextContent(/\d+/);

161

162

// Form value assertions

163

await expect.element(page.getByLabelText("Username")).toHaveValue("john");

164

await expect.element(page.getByRole("slider")).toHaveValue(50);

165

166

// Attribute assertions

167

await expect.element(page.getByRole("link")).toHaveAttribute("href", "/home");

168

await expect.element(page.getByRole("button")).toHaveAttribute("disabled");

169

await expect.element(page.getByTestId("image")).toHaveAttribute("alt", /profile/i);

170

171

// CSS class assertions

172

await expect.element(page.getByRole("alert")).toHaveClass("error");

173

await expect.element(page.getByTestId("card")).toHaveClass(["card", "shadow"]);

174

await expect.element(page.getByRole("button")).toHaveClass(/btn-primary/);

175

176

// Style assertions

177

await expect.element(page.getByTestId("modal")).toHaveStyle("display: block");

178

await expect.element(page.getByRole("progressbar")).toHaveStyle({

179

width: "75%",

180

backgroundColor: "green"

181

});

182

```

183

184

#### State Matchers

185

186

```typescript { .api }

187

/**

188

* Assert element states and properties

189

*/

190

interface StateMatchers {

191

/** Form control is disabled */

192

toBeDisabled(): Promise<void>;

193

/** Form control is enabled */

194

toBeEnabled(): Promise<void>;

195

/** Checkbox or radio is checked */

196

toBeChecked(): Promise<void>;

197

/** Checkbox is partially checked (indeterminate state) */

198

toBePartiallyChecked(): Promise<void>;

199

/** Element has focus */

200

toHaveFocus(): Promise<void>;

201

/** Select element has specific option selected */

202

toHaveDisplayValue(value: string | RegExp | (string | RegExp)[]): Promise<void>;

203

/** Form element is required */

204

toBeRequired(): Promise<void>;

205

/** Form element is invalid */

206

toBeInvalid(): Promise<void>;

207

/** Form element is valid */

208

toBeValid(): Promise<void>;

209

/** Form has specific values for all fields */

210

toHaveFormValues(values: Record<string, any>): Promise<void>;

211

/** Input has specific text selection */

212

toHaveSelection(selection: { start: number; end: number }): Promise<void>;

213

}

214

```

215

216

**Usage Examples:**

217

218

```typescript

219

// Form state assertions

220

await expect.element(page.getByLabelText("Submit")).toBeDisabled();

221

await expect.element(page.getByLabelText("Email")).toBeEnabled();

222

await expect.element(page.getByLabelText("Terms")).toBeChecked();

223

await expect.element(page.getByRole("textbox")).toHaveFocus();

224

225

// Validation state assertions

226

await expect.element(page.getByLabelText("Email")).toBeRequired();

227

await expect.element(page.getByLabelText("Invalid Email")).toBeInvalid();

228

await expect.element(page.getByLabelText("Valid Email")).toBeValid();

229

230

// Select assertions

231

await expect.element(page.getByLabelText("Country")).toHaveDisplayValue("United States");

232

await expect.element(page.getByLabelText("Multiple Select")).toHaveDisplayValue(["Option 1", "Option 2"]);

233

234

// Interactive state testing

235

test("form interaction states", async () => {

236

const input = page.getByLabelText("Username");

237

const button = page.getByRole("button", { name: "Submit" });

238

239

// Initially disabled

240

await expect.element(button).toBeDisabled();

241

242

// Enable after input

243

await userEvent.fill(input, "testuser");

244

await expect.element(button).toBeEnabled();

245

246

// Focus management

247

await userEvent.click(input);

248

await expect.element(input).toHaveFocus();

249

250

await userEvent.tab();

251

await expect.element(button).toHaveFocus();

252

});

253

```

254

255

### Accessibility Matchers

256

257

Specialized matchers for testing accessibility properties and ARIA attributes.

258

259

```typescript { .api }

260

/**

261

* Accessibility-focused assertions

262

*/

263

interface AccessibilityMatchers {

264

/** Element has specific accessible name */

265

toHaveAccessibleName(name: string | RegExp): Promise<void>;

266

/** Element has specific accessible description */

267

toHaveAccessibleDescription(description: string | RegExp): Promise<void>;

268

/** Element has specific accessible error message */

269

toHaveAccessibleErrorMessage(message: string | RegExp): Promise<void>;

270

/** Element has specific ARIA role */

271

toHaveRole(role: string): Promise<void>;

272

/** Element has specific ARIA attribute */

273

toHaveAriaAttribute(name: string, value?: string | RegExp): Promise<void>;

274

}

275

```

276

277

**Usage Examples:**

278

279

```typescript

280

// Accessible name assertions

281

await expect.element(page.getByRole("button")).toHaveAccessibleName("Submit Form");

282

await expect.element(page.getByRole("img")).toHaveAccessibleName(/profile picture/i);

283

284

// Accessible description

285

await expect.element(page.getByRole("textbox")).toHaveAccessibleDescription("Enter your email address");

286

287

// ARIA role assertions

288

await expect.element(page.getByTestId("alert")).toHaveRole("alert");

289

await expect.element(page.getByTestId("navigation")).toHaveRole("navigation");

290

291

// ARIA attribute assertions

292

await expect.element(page.getByRole("tab")).toHaveAriaAttribute("selected", "true");

293

await expect.element(page.getByRole("menu")).toHaveAriaAttribute("expanded", "false");

294

await expect.element(page.getByRole("progressbar")).toHaveAriaAttribute("valuenow", "75");

295

296

// Comprehensive accessibility testing

297

test("accessible form", async () => {

298

const form = page.getByRole("form");

299

const emailInput = form.getByLabelText("Email");

300

const submitButton = form.getByRole("button", { name: "Submit" });

301

302

// Form structure

303

await expect.element(form).toHaveRole("form");

304

await expect.element(form).toHaveAccessibleName("Contact Form");

305

306

// Input accessibility

307

await expect.element(emailInput).toHaveRole("textbox");

308

await expect.element(emailInput).toBeRequired();

309

await expect.element(emailInput).toHaveAccessibleDescription("We'll never share your email");

310

311

// Button accessibility

312

await expect.element(submitButton).toHaveRole("button");

313

await expect.element(submitButton).toHaveAccessibleName("Submit Contact Form");

314

});

315

```

316

317

### Advanced Assertion Patterns

318

319

Complex assertion scenarios for comprehensive browser testing.

320

321

**Waiting for Multiple Elements:**

322

323

```typescript

324

test("page load completion", async () => {

325

// Wait for multiple elements to appear

326

await Promise.all([

327

expect.element(page.getByRole("navigation")).toBeVisible(),

328

expect.element(page.getByRole("main")).toBeVisible(),

329

expect.element(page.getByRole("contentinfo")).toBeVisible()

330

]);

331

332

// Ensure loading indicators are gone

333

await Promise.all([

334

expect.element(page.getByTestId("loading-spinner")).not.toBeVisible(),

335

expect.element(page.getByText("Loading...")).not.toBeInTheDocument()

336

]);

337

});

338

```

339

340

**Conditional Assertions:**

341

342

```typescript

343

test("conditional error display", async () => {

344

await userEvent.click(page.getByRole("button", { name: "Submit" }));

345

346

// Wait for either success or error

347

const successMessage = page.getByText("Success").query();

348

const errorMessage = page.getByRole("alert").query();

349

350

if (successMessage) {

351

await expect.element(successMessage).toBeVisible();

352

await expect.element(errorMessage).not.toBeInTheDocument();

353

} else {

354

await expect.element(errorMessage).toBeVisible();

355

await expect.element(errorMessage).toHaveTextContent(/error/i);

356

}

357

});

358

```

359

360

**List and Collection Assertions:**

361

362

```typescript

363

test("dynamic list updates", async () => {

364

const list = page.getByRole("list");

365

366

// Initial state

367

await expect.element(list).toBeVisible();

368

369

// Add item

370

await userEvent.click(page.getByRole("button", { name: "Add Item" }));

371

372

// Wait for new item to appear

373

await expect.element(page.getByText("New Item")).toBeVisible();

374

375

// Count items

376

const items = page.getByRole("listitem").elements();

377

expect(items).toHaveLength(4);

378

379

// Check each item

380

for (let i = 0; i < items.length; i++) {

381

await expect.element(page.getByRole("listitem").nth(i)).toBeVisible();

382

}

383

});

384

```

385

386

### Configuration

387

388

Configure polling behavior globally or per-test:

389

390

```typescript

391

// In test setup or vitest.config.ts

392

expect.poll.timeout = 10000; // Default timeout for expect.element

393

expect.poll.interval = 200; // Default polling interval

394

395

// Per-test configuration

396

test("slow loading test", async () => {

397

// Override for this test

398

await expect.element(

399

page.getByText("Slow content"),

400

{ timeout: 30000, interval: 500 }

401

).toBeVisible();

402

});

403

```

404

405

## Types

406

407

Enhanced assertion type definitions:

408

409

```typescript { .api }

410

/**

411

* Options for element polling expectations

412

*/

413

interface ExpectPollOptions {

414

timeout?: number;

415

interval?: number;

416

message?: string;

417

}

418

419

/**

420

* Promise-based DOM assertion interface

421

*/

422

type PromisifyDomAssertion<T> = {

423

[K in keyof Assertion<T>]: Assertion<T>[K] extends (...args: infer A) => infer R

424

? (...args: A) => Promise<R>

425

: Assertion<T>[K];

426

};

427

428

/**

429

* Extended expect interface with element method

430

*/

431

interface ExpectStatic {

432

element<T extends Element | Locator | null>(

433

element: T,

434

options?: ExpectPollOptions

435

): PromisifyDomAssertion<Awaited<Element | null>>;

436

}

437

```