Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 86 additions & 40 deletions docs/marko-testing-library/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ as these methods:
- [`debug`](#debug)
- [`rerender`](#rerender)
- [`emitted`](#emitted)
- [`cleanup`](#cleanup)
- [`container`](#container-1)
- [`cleanup`](#cleanup)
- [`cleanup`](#cleanup-1)

---

Expand All @@ -39,34 +40,26 @@ render(MyTemplate)
```

```javascript
import { render, cleanup } from '@marko/testing-library'
import '@testing-library/jest-dom/extend-expect'
import { render, screen } from '@marko/testing-library'
import Greeting from './greeting.marko'

afterEach(cleanup)

test('renders a message', async () => {
const { container, getByText } = await render(Greeting, { name: 'Marko' })
expect(getByText(/Marko/)).toBeInTheDocument()
const { container } = await render(Greeting, { name: 'Marko' })
expect(screen.getByText(/Marko/)).toBeInTheDocument()
expect(container.firstChild).toMatchInlineSnapshot(`
<h1>Hello, Marko!</h1>
`)
})
```

> Note
>
> The [cleanup](#cleanup) function should be called between tests to remove the
> created DOM nodes and keep the tests isolated.

### `render` Options

You won't often need to specify options, but if you ever do, here are the
available options which you could provide as the third argument to `render`.
available options which you can provide as the third argument to `render`.

#### `container`

By default for client side tests, `Marko Testing Library` will create a `div`
By default for client-side tests, `Marko Testing Library` will create a `div`
and append that `div` to the `document.body` and this is where your component
will be rendered. If you provide your own HTMLElement `container` via this
option, it will not be appended to the `document.body` automatically.
Expand All @@ -90,18 +83,28 @@ few properties:
### `...queries`

The most important feature of `render` is that the queries from
[DOM Testing Library](dom-testing-library/api-queries.mdx) are automatically
returned with their first argument bound to the results of rendering your
component.
[DOM Testing Library](queries/about.mdx) are automatically returned with their
first argument bound to the results of rendering your component.

See [Queries](dom-testing-library/api-queries.mdx) for a complete list.
See [Queries](queries/about.mdx) for a complete list.

**Example**

```javascript
const { getByLabelText, queryAllByTestId } = await render(MyTemplate)
```

Alternatively, you can use the
[top-level `screen` method](queries/about.mdx#screen) to query into all
currently rendered components in the `document.body`, eg:

```javascript
import { render, screen } from "@marko/testing-library"

await render(MyTemplate)
const el = screen.getByText(...)
```

### `debug`

This method is a shortcut for logging the `prettyDOM` for all children inside of
Expand All @@ -119,13 +122,13 @@ debug()
```

This is a simple wrapper around `prettyDOM` which is also exposed and comes from
[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library/blob/master/README.md#prettydom).
[`DOM Testing Library`](dom-testing-library/api-debugging.mdx#prettydom).

### `rerender`

A Marko components `input` can change at any time from a parent component.
Although often this input is passed through your component declaratively,
sometimes it is necessary to ensure that your components reacts appropriately to
sometimes it is necessary to ensure that your components react appropriately to
new data. You can simulate your component receiving new `input` by passing new
data to the `rerender` helper.

Expand Down Expand Up @@ -160,13 +163,13 @@ const { getByText, emitted } = await render(Counter)

const button = getByText('Increment')

fireEvent.click(button)
fireEvent.click(button)
await fireEvent.click(button)
await fireEvent.click(button)

// Assuming the `Counter` component forwards these button clicks as `increment` events
expect(emitted('increment')).toHaveProperty('length', 2)

fireEvent.click(button)
await fireEvent.click(button)

// Note: the tracked events are cleared every time you read them.
// Below we are snapshoting the events after our last assertion,
Expand Down Expand Up @@ -212,11 +215,37 @@ expect(emitted()).toMatchInlineSnapshot(`
`)
```

### `cleanup`

Like the [top-level cleanup method](#cleanup-1), this allows you to remove and
destroy the currently rendered component before the test has been completed.

This can be useful to validate that a component properly cleans up any DOM
mutations once it has been destroyed.

```javascript
import { render, screen, getRoles } from '@marko/testing-library'
import Main from './main.marko'
import Dialog from './dialog.marko'

await render(Main)

const main = screen.getByRole('main')
expect(main).not.toHaveAttribute('aria-hidden')

const { cleanup } = await render(Dialog)
expect(main).toHaveAttribute('aria-hidden') // assert added attribute

cleanup() // destroy the dialog

expect(main).not.toHaveAttribute('aria-hidden') // assert attribute removed
```

### `container`

The containing DOM node of your rendered Marko Component. For server side tests
The containing DOM node of your rendered Marko Component. For server-side tests
this is a [JSDOM.fragment](https://github.com/jsdom/jsdom#fragment), and for
client side tests this will be whatever is passed as the `container` render
client-side tests this will be whatever is passed as the `container` render
option.

> Tip: To get the root element of your rendered element, use
Expand All @@ -227,30 +256,47 @@ option.
> changes that will be made to the component you're testing. Avoid using
> `container` to query for elements!

## `fireEvent`

Because Marko batches DOM updates to avoid unnecessary re-renders, the
[fireEvent](dom-testing-library/api-events.mdx) helpers are re-exported as
`async` functions. Awaiting this ensures that the DOM has properly updated in
response to the event triggered in the test.

```js
await fireEvent.click(getByText('Click me'))
```

## `cleanup`

With client side tests your components are rendered into a placeholder
With client-side tests your components are rendered into a placeholder
HTMLElement. To ensure that your components are properly removed, and destroyed,
you can call `cleanup` at any time which will remove any attached components.
after each test the `cleanup` method is called for you automatically by hooking
into `afterEach` in supported test frameworks. You can also manually call
`cleanup` at any time which will remove all attached components.

```javascript
import { cleanup } from '@marko/testing-library'
// automatically unmount and cleanup DOM after the test is finished.
afterEach(cleanup)
```
import { render, cleanup, screen } from '@marko/testing-library'
import Greeting from './greeting.marko'

To save some typing, you could also import a file with the above.
await render(Greeting, { name: 'Marko' })

```javascript
import '@marko/testing-library/cleanup-after-each'
expect(screen.getByText(/Marko/)).toBeInTheDocument()

// manually cleanup the component before the test is finished
cleanup()
expect(screen.queryByText(/Marko/)).toBeNull()
```

If you are using Jest, you can simply include the following to your Jest config
to avoid doing this in each file.
You can turn off the automatic test cleanup by importing the following module:

```javascript
module.exports = {
...,
setupFilesAfterEnv: ['@marko/testing-library/cleanup-after-each']
}
import '@marko/testing-library/dont-cleanup-after-each'
```

With mocha you can use `mocha -r @marko/testing-library/dont-cleanup-after-each`
as a shorthand.

If you are using Jest, you can include
`setupFilesAfterEnv: ["@marko/testing-library/dont-cleanup-after-each"]` in your
Jest config to avoid doing this in each file.
17 changes: 7 additions & 10 deletions docs/marko-testing-library/intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ title: Marko Testing Library
sidebar_label: Introduction
---

The [`Marko Testing Library`](https://github.com/marko-js/testing-library) is a
very lightweight solution for testing Marko components. It provides light
utility functions on top of
[`@testing-library/dom`](https://github.com/testing-library/dom-testing-library)
in a way that encourages better testing practices.
[`Marko Testing Library`](https://github.com/marko-js/testing-library) builds on
top of [`DOM Testing Library`](dom-testing-library/intro.mdx) by adding APIs for
working with Marko components.

```
npm install --save-dev @marko/testing-library
Expand All @@ -19,7 +17,7 @@ npm install --save-dev @marko/testing-library
You want to write maintainable tests for your Marko components. As a part of
this goal, you want your tests to avoid including implementation details of your
components and rather focus on making your tests give you the confidence for
which they are intended. As part of this, you want your testbase to be
which they are intended. As part of this, you want your test suite to be
maintainable in the long run so refactors of your components (changes to
implementation but not functionality) don't break your tests and slow you and
your team down.
Expand All @@ -28,9 +26,8 @@ your team down.

The `@marko/testing-library` is a very lightweight solution for testing Marko
components. It provides light utility functions on top of
[`@testing-library/dom`](https://github.com/testing-library/dom-testing-library)
in a way that encourages better testing practices. Its primary guiding principle
is:
[`@testing-library/dom`](dom-testing-library/intro.mdx) in a way that encourages
better testing practices. Its primary guiding principle is:

> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.mdx)

Expand All @@ -39,7 +36,7 @@ will work with actual DOM nodes. The utilities this library provides facilitate
querying the DOM in the same way the user would. Finding for elements by their
label text (just like a user would), finding links and buttons from their text
(like a user would). It contains a small targeted API and can get out of your
way if absolutely needed with some built in escape hatches.
way if needed with some built-in escape hatches.

This library encourages your applications to be more accessible and allows you
to get your tests closer to using your components the way a user will, which
Expand Down
38 changes: 16 additions & 22 deletions docs/marko-testing-library/setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ title: Setup
sidebar_label: Setup
---

`Marko Testing Library` is not dependent on any test runner, however it is
`Marko Testing Library` is not dependent on any test runner. However, it is
dependent on the test environment. This package works for testing both server
side, and client side Marko templates and provide a slightly different
implementation for each. This is done using a
side, and client-side Marko templates and provides a slightly different
implementation optimized for each. This is done using a
[browser shim](https://github.com/defunctzombie/package-browser-field-spec),
just like in Marko.

Expand All @@ -29,36 +29,32 @@ will tell Jest to resolve the
[browser shim](https://github.com/defunctzombie/package-browser-field-spec)
version of all modules as mentioned above.

To test components rendered in the client side, be sure to enable both the
To test components rendered on the client-side, be sure to enable both the
`browser` option and the preset and you are good to go!

**jest.config.js**

```javascript
module.exports = {
preset: '@marko/jest',
browser: true, // Tells Jest to resolve browser shims.
preset: '@marko/jest/preset/browser',
}
```

For testing components rendered server side we can omit the `browser` option,
however ideally you should also set the
[`testEnvironment option`](https://jestjs.io/docs/en/configuration#testenvironment-string)
to `node` which will disable loading JSDOM globally.
For testing components rendered on the server-side we can instead use
`@marko/jest/preset/node` as our jest preset.

**jest.config.js**

```javascript
module.exports = {
preset: '@marko/jest',
testEnvironment: 'node', // Tells Jest not to load a global JSDOM for server side.
preset: '@marko/jest/preset/node',
}
```

A Jest configuration can also have multiple
[projects](https://jestjs.io/docs/en/configuration#projects-array-string-projectconfig)
which we can use to create a combined configuration for server side tests, and
browser side tests, like so:
which we can use to create a combined configuration for server-side tests, and
client-side tests, like so:

**jest.config.js**

Expand All @@ -67,14 +63,12 @@ module.exports = {
projects: [
{
displayName: 'server',
testEnvironment: 'node',
preset: '@marko/jest',
preset: '@marko/jest/preset/node',
testRegex: '/__tests__/[^.]+\\.server\\.js$',
},
{
displayName: 'browser',
preset: '@marko/jest',
browser: true,
preset: '@marko/jest/preset/browser',
testRegex: '/__tests__/[^.]+\\.browser\\.js$',
},
],
Expand All @@ -86,9 +80,9 @@ module.exports = {
Mocha also works great for testing Marko components. Mocha, however, has no
understanding of
[browser shims](https://github.com/defunctzombie/package-browser-field-spec)
which means out of the box it can only work with server side Marko components.
which means out of the box it can only work with server-side Marko components.

To run server side Marko tests with `mocha` you can simply run the following
To run server-side Marko tests with `mocha` you can simply run the following
command:

```console
Expand All @@ -97,9 +91,9 @@ mocha -r marko/node-require

This enables the
[Marko require hook](https://markojs.com/docs/installing/#require-marko-views)
and allows you to require server side Marko templates directly in your tests.
and allows you to require server-side Marko templates directly in your tests.

For client side testing of your components with Mocha often you will use a
For client-side testing of your components with Mocha often you will use a
bundler to build your tests (this will properly resolve the browser shims
mentioned above) and then you can load these tests in some kind of browser
context.