|
| 1 | +--- |
| 2 | +chapter: 13 |
| 3 | +subtitle: Testing |
| 4 | +--- |
| 5 | + |
| 6 | +Catalyst controllers are based on Web Components, and as such need the Web Platform environment to run in, including in tests. It's possible to run these tests in "browser like" environments such as NodeJS or Deno with libraries like jsdom, but it's best to run tests directly in the browser. |
| 7 | + |
| 8 | +### Recommended Libraries |
| 9 | + |
| 10 | +We recommend using [`@web/test-runner`](https://modern-web.dev/docs/test-runner/overview/), which provides the `web-test-runner` command line tool that can run [mocha](https://mochajs.org/) test files in a headless Chromium instance. We also recommend using [`@open-wc/testing`](https://open-wc.org/docs/testing/testing-package/) which provides a set of testing functions, including `expect` from [Chai](https://www.chaijs.com/api/bdd/). If you're using TypeScript, it may be worth also installing [`@web/dev-server-esbuild`](https://modern-web.dev/docs/dev-server/overview/) which can transpile TypeScript to JavaScript, allowing the use of TypeScript within test files themselves. |
| 11 | + |
| 12 | +With these installed and configured your `package.json` might look something like: |
| 13 | + |
| 14 | +```json |
| 15 | +{ |
| 16 | + "name": "my-catalyst-component", |
| 17 | + "scripts": { |
| 18 | + "test": "web-test-server" |
| 19 | + }, |
| 20 | + "devDependencies": { |
| 21 | + "@web/dev-server-esbuild": "^0.3.0", |
| 22 | + "@web/test-runner": "^0.13.27", |
| 23 | + "@open-wc/testing": "^3.1.2" |
| 24 | + } |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +You can configure the `web-test-server` by writing a `web-test-runner.config.js` file, which sets up the esbuild plugin to transpile TypeScript, and configure the directory containing your test files: |
| 29 | + |
| 30 | +```typescript |
| 31 | +import {esbuildPlugin} from '@web/dev-server-esbuild' |
| 32 | + |
| 33 | +export default { |
| 34 | + files: ['test/*'], |
| 35 | + nodeResolve: true, |
| 36 | + plugins: [esbuildPlugin({ts: true})] |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +#### Example Test File |
| 41 | + |
| 42 | +With this set-up, the boilerplate for an Element test suite might look something like this: |
| 43 | + |
| 44 | +```typescript |
| 45 | +// test/my-controller.ts |
| 46 | +import {expect, fixture, html} from '@open-wc/testing' |
| 47 | +import {MyController} from '../src/my-controller' |
| 48 | + |
| 49 | +describe('MyController', () => { |
| 50 | + let instance |
| 51 | + beforeEach(async () => { |
| 52 | + instance = await fixture(html`<my-controller> |
| 53 | + <div class="expected-children"></div> |
| 54 | + </my-controller>`) |
| 55 | + }) |
| 56 | + |
| 57 | + it('is a Catalyst controller', () => { |
| 58 | + expect(instance).to.have.attribute('data-catalyst') |
| 59 | + }) |
| 60 | + |
| 61 | + it('matches snapshot', () => { |
| 62 | + expect(instance).dom.to.equalSnapshot() |
| 63 | + }) |
| 64 | + |
| 65 | + it('passes Axe tests', () => |
| 66 | + expect(instance).to.be.accessible() |
| 67 | + }) |
| 68 | + |
| 69 | + it('...') // Fill out the rest |
| 70 | +}) |
| 71 | +``` |
| 72 | + |
| 73 | +##### Useful Assertions |
| 74 | + |
| 75 | +The `@open-wc/testing` package exports the `expect` function from Chai, but also automatically registers a set of plugins useful for writing web components, including [chai-a11y-axe](https://www.npmjs.com/package/chai-a11y-axe) and [chai-dom](https://www.npmjs.com/package/chai-dom). Here are some handy example assertions which may be commonly written: |
| 76 | + |
| 77 | + |
| 78 | +- `expect(instance).to.be.accessible()` - Runs a suite of [Axe](https://www.npmjs.com/package/axe) accessibility tests on the element. |
| 79 | +- `expect(instance).dom.to.equalSnapshot()` - Stores a snaphsot test of the existing DOM, which can be tested against later, for regressions. |
| 80 | +- `expect(instance).shadowDom.to.equalSnapshot()` - Stores a snaphsot test of the existing ShadowDOM, which can be tested against later, for regressions. |
| 81 | +- `expect(instance).to.have.class('foo')` - Checks the element has the `foo` class (like `el.classList.contains('foo')`). |
| 82 | +- `expect(instance).to.have.attribute('foo')` - Checks the element has the `foo` attribute (like `el.hasAttribute('foo')`). |
| 83 | +- `expect(instance).to.have.attribute('foo')` - Checks the element has the `foo` attribute (like `el.hasAttribute('foo')`). |
| 84 | +- `expect(instance).to.have.descendants('.foo')` - Checks the element has elements matching the selector `.foo` attribute (like `el.querySelectorAll('foo')`). |
| 85 | + |
0 commit comments