跳到主要内容

API

Marko 测试库 重新导出 DOM 测试库 中的所有内容,以及以下方法


render

function render(
template, // A Marko template to render
input, // Input for the above template
options // You won't often use this, expand below for docs on options
)

渲染到附加到 document.body 的容器中。

import {render} from '@marko/testing-library'
import MyTemplate from './my-template.marko'

render(MyTemplate)
import {render, screen} from '@marko/testing-library'
import Greeting from './greeting.marko'

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

render 选项

你通常不需要指定选项,但如果你需要,以下是你可以作为 render 的第三个参数提供的可用选项。

container

对于客户端测试,Marko 测试库 默认情况下会创建一个 div 并将其附加到 document.body,你的组件将在此处渲染。如果你通过此选项提供自己的 HTMLElement container,它不会自动附加到 document.body

例如:如果你正在对 tablebody 元素进行单元测试,它不能是 div 的子元素。在这种情况下,你可以将 table 指定为渲染 container

const table = document.createElement('table')

const {container} = await render(MyTableBody, null, {
container: document.body.appendChild(table),
})

render 结果

render 方法返回一个 promise,它解析为包含几个属性的对象

...queries

render 的最重要的功能是,来自 核心 API 的查询会自动返回,其第一个参数绑定到渲染组件的结果。

查看 查询 以获取完整的列表。

示例

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

或者,你可以使用 顶级 screen 方法 查询 document.body 中当前渲染的所有组件,例如

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

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

debug

此方法是为 container 内的所有子元素记录 prettyDOM 的快捷方式。

import {render} from '@marko/testing-library'
import Greeting from './greeting.marko'

const {debug} = await render(Greeting, {name: 'World'})
debug()

// <h1>Hello World</h1>
// you can also pass an element: debug(getByTestId('messages'))

这是一个简单的包装器,它围绕着也暴露的 prettyDOM,它来自 DOM 测试库

rerender

Marko 组件的 input 可以随时从父组件更改。虽然通常情况下,此输入是通过你的组件声明性地传递的,但有时需要确保你的组件对新数据做出适当的反应。你可以通过将新数据传递给 rerender 帮助器来模拟你的组件接收新 input

import {render} from '@marko/testing-library'
import Greeting from './greeting.marko'

const {rerender, debug} = await render(Greeting, {name: 'World'})

// re-render the same component with different props
await rerender({name: 'Marko'})

debug()
// <h1>Hello Marko</h1>

emitted

Marko 组件还通过事件与它们的父组件进行通信。建议你还要测试你的组件是否在正确的时间发出了正确的事件。

emitted 帮助器就是这样做的。调用帮助器将返回自上次调用帮助器以来的所有已发出的事件。你也可以传递一个事件类型来过滤结果。

import {render, fireEvent} from '@marko/testing-library'
import Counter from './counter.marko'

const {getByText, emitted} = await render(Counter)

const button = getByText('Increment')

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)

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,
// the return value will include an array with all of the arguments for each increment event.
expect(emitted('increment')).toMatchInlineSnapshot(`
Array [
Array [
Object {
"count": 3,
},
],
]
`)

// Without an event type will give you all events with their type and arguments.
expect(emitted()).toMatchInlineSnapshot(`
Array [
Object {
"args": Array [
Object {
"count": 0,
},
],
"type": "increment",
},
Object {
"args": Array [
Object {
"count": 1,
},
],
"type": "increment",
},
Object {
"args": Array [
Object {
"count": 3,
},
],
"type": "increment",
}
]
`)

cleanup

顶级 cleanup 方法 一样,这允许你在测试完成之前删除和销毁当前渲染的组件。

这可以用来验证组件在被销毁后是否正确清理了所有 DOM 突变。

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

已渲染的 Marko 组件的包含 DOM 节点。对于服务器端测试,这是一个 JSDOM.fragment,对于客户端测试,这将是作为 container 渲染选项传递的任何内容。

提示:要获取已渲染元素的根元素,请使用 container.firstChild

🚨 如果你发现自己正在使用 container 来查询渲染的元素,那么你应该重新考虑!其他查询旨在对将要对正在测试的组件进行的更改更具弹性。避免使用 container 来查询元素!

fireEvent

因为 Marko 会批处理 DOM 更新以避免不必要的重新渲染,所以 fireEvent 帮助器被重新导出为 async 函数。等待它可以确保 DOM 已针对测试中触发的事件正确更新。

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

cleanup

对于客户端测试,你的组件被渲染到一个占位符 HTMLElement 中。为了确保你的组件在每个测试后都被正确删除和销毁,cleanup 方法会通过钩入支持的测试框架中的 afterEach 自动为你调用。你也可以随时手动调用 cleanup,它将删除所有附加的组件。

import {render, cleanup, screen} from '@marko/testing-library'
import Greeting from './greeting.marko'

await render(Greeting, {name: 'Marko'})

expect(screen.getByText(/Marko/)).toBeInTheDocument()

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

你可以通过导入以下模块来关闭自动测试清理

import '@marko/testing-library/dont-cleanup-after-each'

使用 mocha 时,你可以使用 mocha --require @marko/testing-library/dont-cleanup-after-each 作为简写。

如果你正在使用 Jest,你可以在你的 Jest 配置中包含 setupFilesAfterEnv: ["@marko/testing-library/dont-cleanup-after-each"] 以避免在每个文件中执行此操作。