|
| 1 | +--- |
| 2 | +id: api |
| 3 | +title: API |
| 4 | +--- |
| 5 | + |
| 6 | +`Marko Testing Library` re-exports everything from `DOM Testing Library` as well |
| 7 | +as these methods: |
| 8 | + |
| 9 | +- [`render`](#render) |
| 10 | +- [`cleanup`](#cleanup) |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## `render` |
| 15 | + |
| 16 | +```javascript |
| 17 | +function render( |
| 18 | + template, // A Marko template to render |
| 19 | + input, // Input for the above template |
| 20 | + options // You won't often use this, expand below for docs on options |
| 21 | +) |
| 22 | +``` |
| 23 | + |
| 24 | +Render into a container which is appended to `document.body`. |
| 25 | + |
| 26 | +```javascript |
| 27 | +import { render } from '@marko/testing-library' |
| 28 | +import MyTemplate from './my-template.marko' |
| 29 | + |
| 30 | +render(MyTemplate) |
| 31 | +``` |
| 32 | + |
| 33 | +```javascript |
| 34 | +import { render, cleanup } from '@marko/testing-library' |
| 35 | +import 'jest-dom/extend-expect' |
| 36 | +import Greeting from './greeting.marko' |
| 37 | + |
| 38 | +afterEach(cleanup) |
| 39 | + |
| 40 | +test('renders a message', async () => { |
| 41 | + const { container, getByText } = await render(Greeting, { name: 'Marko' }) |
| 42 | + expect(getByText(/Marko/)).toBeInTheDocument() |
| 43 | + expect(container.firstChild).toMatchInlineSnapshot(` |
| 44 | + <h1>Hello, Marko!</h1> |
| 45 | + `) |
| 46 | +}) |
| 47 | +``` |
| 48 | + |
| 49 | +> Note |
| 50 | +> |
| 51 | +> The [cleanup](#cleanup) function should be called between tests to remove the |
| 52 | +> created DOM nodes and keep the tests isolated. |
| 53 | +
|
| 54 | +### `render` Options |
| 55 | + |
| 56 | +You won't often need to specify options, but if you ever do, here are the |
| 57 | +available options which you could provide as the third argument to `render`. |
| 58 | + |
| 59 | +#### `container` |
| 60 | + |
| 61 | +By default for client side tests, `Marko Testing Library` will create a `div` |
| 62 | +and append that `div` to the `document.body` and this is where your component |
| 63 | +will be rendered. If you provide your own HTMLElement `container` via this |
| 64 | +option, it will not be appended to the `document.body` automatically. |
| 65 | + |
| 66 | +For example: If you are unit testing a `tablebody` element, it cannot be a child |
| 67 | +of a `div`. In this case, you can specify a `table` as the render `container`. |
| 68 | + |
| 69 | +```javascript |
| 70 | +const table = document.createElement('table') |
| 71 | + |
| 72 | +const { container } = await render(MyTableBody, null, { |
| 73 | + container: document.body.appendChild(table), |
| 74 | +}) |
| 75 | +``` |
| 76 | + |
| 77 | +## `render` Result |
| 78 | + |
| 79 | +The `render` method returns a promise which resolves with an object that has a |
| 80 | +few properties: |
| 81 | + |
| 82 | +### `...queries` |
| 83 | + |
| 84 | +The most important feature of `render` is that the queries from |
| 85 | +[DOM Testing Library](dom-testing-library/api-queries.md) are automatically |
| 86 | +returned with their first argument bound to the results of rendering your |
| 87 | +component. |
| 88 | + |
| 89 | +See [Queries](dom-testing-library/api-queries.md) for a complete list. |
| 90 | + |
| 91 | +**Example** |
| 92 | + |
| 93 | +```javascript |
| 94 | +const { getByLabelText, queryAllByTestId } = await render(MyTemplate) |
| 95 | +``` |
| 96 | + |
| 97 | +### `debug` |
| 98 | + |
| 99 | +This method is a shortcut for logging the `prettyDOM` for all children inside of |
| 100 | +the `container`. |
| 101 | + |
| 102 | +```javascript |
| 103 | +import { render } from '@marko/testing-library' |
| 104 | +import Greeting from './greeting.marko' |
| 105 | + |
| 106 | +const { debug } = await render(Greeting, { name: 'World' }) |
| 107 | +debug() |
| 108 | + |
| 109 | +// <h1>Hello World</h1> |
| 110 | +// you can also pass an element: debug(getByTestId('messages')) |
| 111 | +``` |
| 112 | + |
| 113 | +This is a simple wrapper around `prettyDOM` which is also exposed and comes from |
| 114 | +[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library/blob/master/README.md#prettydom). |
| 115 | + |
| 116 | +### `rerender` |
| 117 | + |
| 118 | +A Marko components `input` can change at any time from a parent component. |
| 119 | +Although often this input is passed through your component declaratively, |
| 120 | +sometimes it is necessary to ensure that your components reacts appropriately to |
| 121 | +new data. You can simulate your component receiving new `input` by passing new |
| 122 | +data to the `rerender` helper. |
| 123 | + |
| 124 | +```javascript |
| 125 | +import { render } from '@marko/testing-library' |
| 126 | +import Greeting from './greeting.marko' |
| 127 | + |
| 128 | +const { rerender, debug } = await render(Greeting, { name: 'World' }) |
| 129 | + |
| 130 | +// re-render the same component with different props |
| 131 | +await rerender({ name: 'Marko' }) |
| 132 | + |
| 133 | +debug() |
| 134 | +// <h1>Hello Marko</h1> |
| 135 | +``` |
| 136 | + |
| 137 | +### `emitted` |
| 138 | + |
| 139 | +Marko components also communicate with their parents through events. It is |
| 140 | +recommended to also test that your components emit the right events at the right |
| 141 | +time. |
| 142 | + |
| 143 | +The `emitted` helper does just that. Calling the helper will return all emitted |
| 144 | +events since the last call to the helper. You can also pass in an event type to |
| 145 | +filter the results. |
| 146 | + |
| 147 | +```javascript |
| 148 | +import { render, fireEvent } from '@marko/testing-library' |
| 149 | +import Counter from './counter.marko' |
| 150 | + |
| 151 | +const { getByText, emitted } = await render(Counter) |
| 152 | + |
| 153 | +const button = getByText('Increment') |
| 154 | + |
| 155 | +fireEvent.click(button) |
| 156 | +fireEvent.click(button) |
| 157 | + |
| 158 | +// Assuming the `Counter` component forwards these button clicks as `increment` events |
| 159 | +expect(emitted('increment')).toHaveProperty('length', 2) |
| 160 | + |
| 161 | +fireEvent.click(button) |
| 162 | + |
| 163 | +// Note: the tracked events are cleared every time you read them. |
| 164 | +// Below we are snapshoting the events after our last assertion, |
| 165 | +// the return value will include an array with all of the arguments for each increment event. |
| 166 | +expect(emitted('increment')).toMatchInlineSnapshot(` |
| 167 | + Array [ |
| 168 | + Array [ |
| 169 | + Object { |
| 170 | + "count": 3, |
| 171 | + }, |
| 172 | + ], |
| 173 | + ] |
| 174 | +`) |
| 175 | + |
| 176 | +// Without an event type will give you all events with their type and arguments. |
| 177 | +expect(emitted()).toMatchInlineSnapshot(` |
| 178 | + Array [ |
| 179 | + Object { |
| 180 | + "args": Array [ |
| 181 | + Object { |
| 182 | + "count": 0, |
| 183 | + }, |
| 184 | + ], |
| 185 | + "type": "increment", |
| 186 | + }, |
| 187 | + Object { |
| 188 | + "args": Array [ |
| 189 | + Object { |
| 190 | + "count": 1, |
| 191 | + }, |
| 192 | + ], |
| 193 | + "type": "increment", |
| 194 | + }, |
| 195 | + Object { |
| 196 | + "args": Array [ |
| 197 | + Object { |
| 198 | + "count": 3, |
| 199 | + }, |
| 200 | + ], |
| 201 | + "type": "increment", |
| 202 | + } |
| 203 | + ] |
| 204 | + `) |
| 205 | +``` |
| 206 | + |
| 207 | +### `container` |
| 208 | + |
| 209 | +The containing DOM node of your rendered Marko Component. For server side tests |
| 210 | +this is a [JSDOM.fragment](), and for client side tests this will be whatever is |
| 211 | +passed as the `container` render option. |
| 212 | + |
| 213 | +> Tip: To get the root element of your rendered element, use |
| 214 | +> `container.firstChild`. |
| 215 | +
|
| 216 | +> 🚨 If you find yourself using `container` to query for rendered elements then |
| 217 | +> you should reconsider! The other queries are designed to be more resilient to |
| 218 | +> changes that will be made to the component you're testing. Avoid using |
| 219 | +> `container` to query for elements! |
| 220 | +
|
| 221 | +## `cleanup` |
| 222 | + |
| 223 | +With client side tests your components are rendered into a placeholder |
| 224 | +HTMLElement. To ensure that your components are properly removed, and destroyed, |
| 225 | +you can call `cleanup` at any time which will remove any attached components. |
| 226 | + |
| 227 | +```javascript |
| 228 | +import { cleanup } from '@marko/testing-library' |
| 229 | +// automatically unmount and cleanup DOM after the test is finished. |
| 230 | +afterEach(cleanup) |
| 231 | +``` |
| 232 | + |
| 233 | +To save some typing, you could also import a file with the above. |
| 234 | + |
| 235 | +```javascript |
| 236 | +import '@marko/testing-library/cleanup-after-each' |
| 237 | +``` |
| 238 | + |
| 239 | +If you are using Jest, you can simply include the following to your Jest config |
| 240 | +to avoid doing this in each file. |
| 241 | + |
| 242 | +```javascript |
| 243 | +module.exports = { |
| 244 | + ..., |
| 245 | + setupFilesAfterEnv: ['@marko/testing-library/cleanup-after-each'] |
| 246 | +} |
| 247 | +``` |
0 commit comments