or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# @open-wc/testing

1

2

@open-wc/testing is an opinionated testing package that combines and configures multiple testing libraries to minimize ceremony when writing tests for web components and modern JavaScript applications. It provides Chai assertions, fixture management utilities, and testing helpers in a single, cohesive package. The package offers two import modes: standard (with automatic chai plugin registration) and pure (manual setup required).

3

4

## Package Information

5

6

- **Package Name**: @open-wc/testing

7

- **Package Type**: npm

8

- **Language**: JavaScript/TypeScript

9

- **Installation**: `npm install --save-dev @open-wc/testing`

10

11

## Core Imports

12

13

**Recommended approach (pure imports without automatic plugin registration):**

14

15

```javascript

16

import { expect, fixture, html, chai, assert, should } from '@open-wc/testing/pure';

17

```

18

19

**Standard imports (with automatic chai plugin registration - requires built plugins):**

20

21

```javascript

22

import { expect, fixture, html, chai, assert, should } from '@open-wc/testing';

23

```

24

25

**Note:** The main import may fail at runtime if chai plugins haven't been built. Use the pure import for reliability.

26

27

CommonJS (if using pre-ES6 environment):

28

29

```javascript

30

const { expect, fixture, html, chai, assert, should } = require('@open-wc/testing/pure');

31

```

32

33

## Basic Usage

34

35

```javascript

36

import { expect, fixture, html, elementUpdated } from '@open-wc/testing/pure';

37

38

describe('MyElement', () => {

39

it('renders correctly', async () => {

40

// Create a fixture with lit-html template

41

const el = await fixture(html`<my-element name="test"></my-element>`);

42

43

// Test DOM structure with semantic comparison (requires main import for plugin)

44

// expect(el).dom.to.equal('<my-element name="test"></my-element>');

45

46

// Test accessibility (requires main import for plugin)

47

// await expect(el).to.be.accessible();

48

49

// Basic DOM testing (always works)

50

expect(el.tagName.toLowerCase()).to.equal('my-element');

51

expect(el.getAttribute('name')).to.equal('test');

52

53

// Test element behavior

54

el.name = 'updated';

55

await elementUpdated(el);

56

expect(el.textContent).to.include('updated');

57

});

58

59

it('handles events', async () => {

60

const el = await fixture(html`<button>Click me</button>`);

61

let clicked = false;

62

63

el.addEventListener('click', () => clicked = true);

64

el.click();

65

66

expect(clicked).to.be.true;

67

});

68

});

69

```

70

71

## Architecture

72

73

@open-wc/testing is built around several key components:

74

75

- **Chai Integration**: Pre-configured Chai assertion library with optional automatic plugin registration

76

- **Fixture System**: DOM fixture creation and management for component testing

77

- **Testing Helpers**: Utilities for element lifecycle, events, timing, and browser compatibility

78

- **Plugin System**: Optional semantic DOM diffing and accessibility testing (requires main import)

79

- **Dual Export Modes**:

80

- **Standard** (`@open-wc/testing`): Automatic chai plugin registration, requires built plugins

81

- **Pure** (`@open-wc/testing/pure`): No automatic setup, manual plugin configuration needed

82

83

## Capabilities

84

85

### Chai Assertions

86

87

Core assertion library with pre-configured plugins for comprehensive testing capabilities.

88

89

```typescript { .api }

90

const chai: typeof Chai;

91

const expect: typeof Chai.expect;

92

const assert: typeof Chai.assert;

93

const should: typeof Chai.should;

94

```

95

96

**Usage Examples:**

97

98

```javascript

99

import { expect, assert, should } from '@open-wc/testing/pure';

100

101

// Expect style

102

expect(value).to.equal(42);

103

expect(element).to.have.class('active');

104

105

// Assert style

106

assert.equal(value, 42);

107

assert.isAccessible(element);

108

109

// Should style

110

value.should.equal(42);

111

element.should.have.class('active');

112

```

113

114

### Fixture Management

115

116

Create and manage DOM fixtures for testing components in isolation.

117

118

```typescript { .api }

119

/**

120

* Asynchronously renders template and puts it in DOM, awaiting element updates

121

*/

122

function fixture<T extends Element>(

123

template: LitHTMLRenderable,

124

options?: FixtureOptions

125

): Promise<T>;

126

127

/**

128

* Synchronously renders template and puts it in DOM

129

*/

130

function fixtureSync<T extends Element>(

131

template: LitHTMLRenderable,

132

options?: FixtureOptions

133

): T;

134

135

/**

136

* Specifically handles lit-html templates for async fixture setup

137

*/

138

function litFixture<T extends Element>(

139

template: LitHTMLRenderable,

140

options?: FixtureOptions

141

): Promise<T>;

142

143

/**

144

* Synchronous version of litFixture

145

*/

146

function litFixtureSync<T extends Element>(

147

template: LitHTMLRenderable,

148

options?: FixtureOptions

149

): T;

150

151

/**

152

* Cleans up all defined fixtures by removing wrapper nodes from DOM

153

*/

154

function fixtureCleanup(): void;

155

156

interface FixtureOptions {

157

render?: Function;

158

parentNode?: Element;

159

scopedElements?: ScopedElementsMap;

160

}

161

162

type LitHTMLRenderable =

163

| TemplateResult

164

| TemplateResult[]

165

| Node | Node[]

166

| string | string[]

167

| number | number[]

168

| boolean | boolean[];

169

```

170

171

**Usage Examples:**

172

173

```javascript

174

import { fixture, fixtureSync, fixtureCleanup, html } from '@open-wc/testing/pure';

175

176

// Async fixture creation

177

const el = await fixture(html`<div>Hello World</div>`);

178

const el2 = await fixture('<span>Text content</span>');

179

180

// Sync fixture creation

181

const syncEl = fixtureSync(html`<p>Immediate render</p>`);

182

183

// Custom parent node

184

const customEl = await fixture(html`<div>Child</div>`, {

185

parentNode: document.getElementById('container')

186

});

187

188

// Clean up all fixtures (usually in afterEach)

189

afterEach(() => {

190

fixtureCleanup();

191

});

192

```

193

194

### Template Creation

195

196

Create lit-html templates for fixture rendering and testing.

197

198

```typescript { .api }

199

/**

200

* Creates lit-html templates for testing (tagged template literal)

201

*/

202

const html: (strings: TemplateStringsArray, ...values: any[]) => TemplateResult;

203

204

/**

205

* Allows dynamic tag names in lit-html templates (for testing only)

206

*/

207

function unsafeStatic(value: string): StaticValue;

208

```

209

210

**Usage Examples:**

211

212

```javascript

213

import { fixture, html, unsafeStatic } from '@open-wc/testing/pure';

214

215

// Basic template

216

const el = await fixture(html`<div class="test">Content</div>`);

217

218

// Template with expressions

219

const name = 'Alice';

220

const el2 = await fixture(html`<span>Hello ${name}</span>`);

221

222

// Dynamic tag names (use carefully)

223

const tagName = unsafeStatic('my-element');

224

const el3 = await fixture(html`<${tagName} prop="value"></${tagName}>`);

225

```

226

227

### Element Lifecycle Management

228

229

Manage element updates, custom element registration, and lifecycle events.

230

231

```typescript { .api }

232

/**

233

* Awaits element update completion (supports Lit, Stencil, generic elements)

234

*/

235

function elementUpdated<T extends Element>(el: T): Promise<T>;

236

237

/**

238

* Registers new custom element with auto-generated unique name

239

*/

240

function defineCE<T extends HTMLElement>(klass: Constructor<T>): string;

241

242

type Constructor<T = {}> = new (...args: any[]) => T;

243

```

244

245

**Usage Examples:**

246

247

```javascript

248

import { fixture, elementUpdated, defineCE, html } from '@open-wc/testing/pure';

249

250

// Wait for element updates

251

const el = await fixture(html`<my-element></my-element>`);

252

el.someProperty = 'new value';

253

await elementUpdated(el); // Waits for Lit/Stencil updates

254

255

// Register custom element for testing

256

class TestElement extends HTMLElement {

257

connectedCallback() {

258

this.textContent = 'Test Element';

259

}

260

}

261

const tagName = defineCE(TestElement); // Returns 'test-0', 'test-1', etc.

262

const el2 = await fixture(`<${tagName}></${tagName}>`);

263

```

264

265

### Event Handling

266

267

Utilities for testing event-driven behavior and user interactions.

268

269

```typescript { .api }

270

/**

271

* Listens for one event and resolves with the event object

272

*/

273

function oneEvent(

274

eventTarget: EventTarget,

275

eventName: string

276

): Promise<Event>;

277

278

/**

279

* Like oneEvent but automatically calls preventDefault() on the event

280

*/

281

function oneDefaultPreventedEvent(

282

eventTarget: EventTarget,

283

eventName: string

284

): Promise<Event>;

285

286

/**

287

* Focuses element with IE11 compatibility workarounds

288

*/

289

function triggerFocusFor(element: HTMLElement): Promise<void>;

290

291

/**

292

* Blurs element with IE11 compatibility workarounds

293

*/

294

function triggerBlurFor(element: HTMLElement): Promise<void>;

295

```

296

297

**Usage Examples:**

298

299

```javascript

300

import { fixture, oneEvent, oneDefaultPreventedEvent, triggerFocusFor } from '@open-wc/testing/pure';

301

302

// Listen for custom events

303

const el = await fixture(html`<my-button></my-button>`);

304

const eventPromise = oneEvent(el, 'my-custom-event');

305

el.dispatchEvent(new CustomEvent('my-custom-event', { detail: 'data' }));

306

const event = await eventPromise;

307

console.log(event.detail); // 'data'

308

309

// Prevent default behavior

310

const link = await fixture(html`<a href="/test">Link</a>`);

311

const clickPromise = oneDefaultPreventedEvent(link, 'click');

312

link.click();

313

await clickPromise; // Event was prevented

314

315

// Focus management

316

const input = await fixture(html`<input type="text">`);

317

await triggerFocusFor(input);

318

expect(document.activeElement).to.equal(input);

319

```

320

321

### Timing and Async Utilities

322

323

Control timing and wait for conditions during tests.

324

325

```typescript { .api }

326

/**

327

* Resolves after specified milliseconds using setTimeout

328

*/

329

function aTimeout(ms: number): Promise<void>;

330

331

/**

332

* Resolves after requestAnimationFrame

333

*/

334

function nextFrame(): Promise<void>;

335

336

/**

337

* Waits until predicate returns truthy value, polling at intervals

338

*/

339

function waitUntil(

340

predicate: () => unknown | Promise<unknown>,

341

message?: string,

342

options?: { interval?: number; timeout?: number }

343

): Promise<void>;

344

```

345

346

**Usage Examples:**

347

348

```javascript

349

import { aTimeout, nextFrame, waitUntil } from '@open-wc/testing/pure';

350

351

// Simple timeout

352

await aTimeout(100); // Wait 100ms

353

354

// Wait for animation frame

355

await nextFrame(); // Wait for next render cycle

356

357

// Wait for condition with polling

358

const el = await fixture(html`<div></div>`);

359

setTimeout(() => el.classList.add('loaded'), 50);

360

361

await waitUntil(

362

() => el.classList.contains('loaded'),

363

'Element should be loaded',

364

{ interval: 10, timeout: 1000 }

365

);

366

```

367

368

### Browser Compatibility

369

370

Utilities for handling browser-specific behavior and compatibility.

371

372

```typescript { .api }

373

/**

374

* Detects Internet Explorer browser

375

*/

376

function isIE(): boolean;

377

```

378

379

**Usage Examples:**

380

381

```javascript

382

import { isIE } from '@open-wc/testing/pure';

383

384

if (isIE()) {

385

// Skip test or use IE-specific behavior

386

console.log('Running in Internet Explorer');

387

}

388

```

389

390

### Semantic DOM Comparison

391

392

Advanced DOM comparison that ignores whitespace, comments, and other non-semantic differences.

393

394

**New Chai Assertions:**

395

396

```typescript { .api }

397

// Available on any DOM element via expect()

398

interface Assertion {

399

dom: Assertion; // Compare full DOM trees semantically

400

lightDom: Assertion; // Compare light DOM only

401

}

402

403

// Additional methods on dom/lightDom assertions

404

interface Assertion {

405

equal(expected: string): Assertion; // Semantic equality

406

equalSnapshot(name?: string): Assertion; // Snapshot testing

407

}

408

```

409

410

**Usage Examples:**

411

412

```javascript

413

import { expect, fixture } from '@open-wc/testing/pure';

414

415

// Semantic DOM comparison ignores formatting

416

const el = await fixture(`<div><!-- comment --><h1> Hello </h1> </div>`);

417

expect(el).dom.to.equal('<div><h1>Hello</h1></div>');

418

419

// Light DOM comparison (excludes shadow DOM)

420

expect(el).lightDom.to.equal('<h1>Hello</h1>');

421

422

// Snapshot testing (when supported by test runner)

423

expect(el).dom.to.equalSnapshot();

424

expect(el).dom.to.equalSnapshot('custom-name');

425

```

426

427

### Accessibility Testing

428

429

Automated accessibility testing using axe-core integration.

430

431

**New Chai Assertions:**

432

433

```typescript { .api }

434

interface Assertion {

435

accessible(options?: A11yOptions): Promise<Assertion>;

436

}

437

438

interface A11yOptions {

439

ignoredRules?: string[]; // Skip specific axe rules

440

done?: Function; // Callback for async testing patterns

441

}

442

443

// Also available on assert interface

444

declare namespace assert {

445

function isAccessible(

446

element: Element,

447

options?: A11yOptions

448

): Promise<void>;

449

}

450

```

451

452

**Usage Examples:**

453

454

```javascript

455

import { expect, assert, fixture, html } from '@open-wc/testing/pure';

456

457

// Basic accessibility testing

458

const button = await fixture(html`<button>Click me</button>`);

459

await expect(button).to.be.accessible();

460

461

// Ignore specific rules

462

const problematicEl = await fixture(html`<div aria-labelledby="missing-id"></div>`);

463

await expect(problematicEl).to.be.accessible({

464

ignoredRules: ['aria-allowed-attr']

465

});

466

467

// Assert style

468

const form = await fixture(html`<form><label>Name <input></label></form>`);

469

await assert.isAccessible(form);

470

471

// Negation for expected failures

472

const badEl = await fixture(html`<div aria-labelledby="missing"></div>`);

473

await expect(badEl).not.to.be.accessible();

474

```

475

476

### Built-in Chai Plugins Status

477

478

**Available Plugins (functional with main import):**

479

- `@open-wc/semantic-dom-diff` - Provides `.dom.to.equal()` and `.lightDom.to.equal()` assertions

480

- `chai-a11y-axe` - Provides `.accessible()` accessibility testing assertions

481

482

**Currently Non-functional Plugins:**

483

The package references `chai-dom` and `sinon-chai` plugins but they require a build step that may not be available. If these plugins are working in your environment, they would provide:

484

485

- `chai-dom` - DOM assertions like `.to.have.class()`, `.to.have.attribute()`

486

- `sinon-chai` - Spy/stub assertions like `.to.have.callCount()`

487

488

**Recommendation:** Use the pure import (`@open-wc/testing/pure`) and manually configure additional chai plugins as needed to avoid runtime import errors.

489

490

## Types

491

492

```typescript { .api }

493

// Re-exported from @esm-bundle/chai

494

type Chai = typeof import('chai');

495

496

// Fixture configuration

497

interface FixtureOptions {

498

render?: Function;

499

parentNode?: Element;

500

scopedElements?: ScopedElementsMap;

501

}

502

503

// Template types for fixture creation

504

type LitHTMLRenderable =

505

| TemplateResult

506

| TemplateResult[]

507

| Node | Node[]

508

| string | string[]

509

| number | number[]

510

| boolean | boolean[];

511

512

// Event handling types

513

type OneEventFn = <TEvent extends Event = CustomEvent>(

514

eventTarget: EventTarget,

515

eventName: string

516

) => Promise<TEvent>;

517

518

// Element constructor type

519

type Constructor<T = {}> = new (...args: any[]) => T;

520

521

// Accessibility testing options

522

interface A11yOptions {

523

ignoredRules?: string[];

524

done?: Function;

525

}

526

527

// Timing utility options

528

interface WaitUntilOptions {

529

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

530

timeout?: number; // Total timeout in ms (default: 1000)

531

}

532

```

533

534

## Usage Recommendations

535

536

**For maximum reliability and compatibility:**

537

538

1. **Use the pure import**: `@open-wc/testing/pure` to avoid potential runtime issues

539

2. **Manual plugin setup**: If you need DOM or accessibility assertions, configure chai plugins manually

540

3. **Standard testing**: The package works perfectly for fixture creation, element testing, and basic chai assertions

541

4. **Version compatibility**: This documentation reflects @open-wc/testing@4.0.0 behavior

542

543

**Example minimal setup:**

544

545

```javascript

546

import { expect, fixture, html, elementUpdated } from '@open-wc/testing/pure';

547

548

describe('Component Tests', () => {

549

it('creates fixtures and tests elements', async () => {

550

const el = await fixture(html`<div class="test">Hello</div>`);

551

expect(el.textContent).to.equal('Hello');

552

expect(el.className).to.equal('test');

553

});

554

});

555

```