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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/api/browser/react.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export function render(
): Promise<RenderResult>
```

The `render` function records a `react.render` trace mark, visible in the [Trace View](/guide/browser/trace-view).

:::warning
Note that `render` is asynchronous, unlike in other packages. This is to support [`Suspense`](https://react.dev/reference/react/Suspense) correctly.

Expand Down Expand Up @@ -154,6 +156,8 @@ This method is a shortcut for `console.log(prettyDOM(baseElement))`. It will pri
function rerender(ui: React.ReactNode): Promise<void>
```

Also records a `react.rerender` trace mark in the [Trace View](/guide/browser/trace-view).

It is better if you test the component that's doing the prop updating to ensure that the props are being updated correctly to avoid relying on implementation details in your tests. That said, if you'd prefer to update the props of a rendered component in your test, this function can be used to update props of the rendered component.

```jsx
Expand All @@ -171,6 +175,8 @@ await rerender(<NumberDisplay number={2} />)
function unmount(): Promise<void>
```

Also records a `react.unmount` trace mark in the [Trace View](/guide/browser/trace-view).

This will cause the rendered component to be unmounted. This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks).

```jsx
Expand Down
49 changes: 32 additions & 17 deletions docs/api/browser/svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { expect, test } from 'vitest'
import Component from './Component.svelte'

test('counter button increments the count', async () => {
const screen = render(Component, {
const screen = await render(Component, {
initialCount: 1,
})

Expand All @@ -39,15 +39,26 @@ export function render<C extends Component>(
Component: ComponentImport<C>,
options?: ComponentOptions<C>,
renderOptions?: SetupOptions
): RenderResult<C>
): RenderResult<C> & PromiseLike<RenderResult<C>>
```

The `render` function records a `svelte.render` trace mark, visible in the [Trace View](/guide/browser/trace-view).

::: warning
Synchronous usage of `render` is deprecated and will be removed in the next major version. Please always `await` the result:

```ts
const screen = render(Component) // [!code --]
const screen = await render(Component) // [!code ++]
```
:::

### Options

The `render` function supports either options that you can pass down to [`mount`](https://svelte.dev/docs/svelte/imperative-component-api#mount) or props directly:

```ts
const screen = render(Component, {
const screen = await render(Component, {
props: { // [!code --]
initialCount: 1, // [!code --]
}, // [!code --]
Expand All @@ -68,7 +79,7 @@ For example, if you are unit testing a `tbody` element, it cannot be a child of
```ts
const table = document.createElement('table')

const screen = render(TableBody, {
const screen = await render(TableBody, {
props,
// ⚠️ appending the element to `body` manually before rendering
target: document.body.appendChild(table),
Expand All @@ -86,7 +97,7 @@ If the `target` is specified, then this defaults to that, otherwise this default
In addition to documented return value, the `render` function also returns all available [locators](/api/browser/locators) relative to the [`baseElement`](#baseelement), including [custom ones](/api/browser/locators#custom-locators).

```ts
const screen = render(TableBody, props)
const screen = await render(TableBody, props)

await screen.getByRole('link', { name: 'Expand' }).click()
```
Expand All @@ -104,7 +115,7 @@ If you find yourself using `container` to query for rendered elements then you s
The mounted Svelte component instance. You can use this to access component methods and properties if needed.

```ts
const { component } = render(Counter, {
const { component } = await render(Counter, {
initialCount: 0,
})

Expand All @@ -118,7 +129,7 @@ The [locator](/api/browser/locators) of your `container`. It is useful to use qu
```ts
import { render } from 'vitest-browser-svelte'

const { locator } = render(NumberDisplay, {
const { locator } = await render(NumberDisplay, {
number: 2,
})

Expand All @@ -139,15 +150,15 @@ This method is a shortcut for `console.log(prettyDOM(baseElement))`. It will pri
#### rerender

```ts
function rerender(props: Partial<ComponentProps<T>>): void
function rerender(props: Partial<ComponentProps<T>>): Promise<void>
```

Updates the component's props and waits for Svelte to apply the changes. Use this to test how your component responds to prop changes.
Updates the component's props and waits for Svelte to apply the changes. Use this to test how your component responds to prop changes. Also records a `svelte.rerender` trace mark in the [Trace View](/guide/browser/trace-view).

```ts
import { render } from 'vitest-browser-svelte'

const { rerender } = render(NumberDisplay, {
const { rerender } = await render(NumberDisplay, {
number: 1,
})

Expand All @@ -158,16 +169,20 @@ await rerender({ number: 2 })
#### unmount

```ts
function unmount(): void
function unmount(): Promise<void>
```

Unmount and destroy the Svelte component. This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks).
Unmount and destroy the Svelte component. Also records a `svelte.unmount` trace mark in the [Trace View](/guide/browser/trace-view). This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks).

::: warning
Synchronous usage of `unmount` is deprecated and will be removed in the next major version. Please always `await` the result.
:::

```ts
import { render } from 'vitest-browser-svelte'

const { container, unmount } = render(Component)
unmount()
const { container, unmount } = await render(Component)
await unmount()
// your component has been unmounted and now: container.innerHTML === ''
```

Expand All @@ -193,7 +208,7 @@ locators.extend({
},
})

const screen = render(Component)
const screen = await render(Component)
await expect.element(
screen.getByArticleTitle('Hello World')
).toBeVisible()
Expand All @@ -211,7 +226,7 @@ import { expect, test } from 'vitest'
import SubjectTest from './basic-snippet.test.svelte'

test('basic snippet', async () => {
const screen = render(SubjectTest)
const screen = await render(SubjectTest)

const heading = screen.getByRole('heading')
const child = heading.getByTestId('child')
Expand Down Expand Up @@ -250,7 +265,7 @@ import { expect, test } from 'vitest'
import Subject from './complex-snippet.svelte'

test('renders greeting in message snippet', async () => {
const screen = render(Subject, {
const screen = await render(Subject, {
name: 'Alice',
message: createRawSnippet(greeting => ({
render: () => `<span data-testid="message">${greeting()}</span>`,
Expand Down
43 changes: 32 additions & 11 deletions docs/api/browser/vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { expect, test } from 'vitest'
import Component from './Component.vue'

test('counter button increments the count', async () => {
const screen = render(Component, {
const screen = await render(Component, {
props: {
initialCount: 1,
}
Expand Down Expand Up @@ -40,9 +40,20 @@ The package exposes two entry points: `vitest-browser-vue` and `vitest-browser-v
export function render(
component: Component,
options?: ComponentRenderOptions,
): RenderResult
): RenderResult & PromiseLike<RenderResult>
```

The `render` function records a `vue.render` trace mark, visible in the [Trace View](/guide/browser/trace-view).

::: warning
Synchronous usage of `render` is deprecated and will be removed in the next major version. Please always `await` the result:

```ts
const screen = render(Component) // [!code --]
const screen = await render(Component) // [!code ++]
```
:::

### Options

The `render` function supports all [`mount` options](https://test-utils.vuejs.org/api/#mount) from `@vue/test-utils` (except `attachTo` - use `container` instead). In addition to them, there are also `container` and `baseElement`.
Expand All @@ -56,7 +67,7 @@ For example, if you are unit testing a `tbody` element, it cannot be a child of
```js
const table = document.createElement('table')

const { container } = render(TableBody, {
const { container } = await render(TableBody, {
props,
// ⚠️ appending the element to `body` manually before rendering
container: document.body.appendChild(table),
Expand All @@ -72,7 +83,7 @@ If the `container` is specified, then this defaults to that, otherwise this defa
In addition to documented return value, the `render` function also returns all available [locators](/api/browser/locators) relative to the [`baseElement`](#baseelement), including [custom ones](/api/browser/locators#custom-locators).

```ts
const screen = render(TableBody, { props })
const screen = await render(TableBody, { props })

await screen.getByRole('link', { name: 'Expand' }).click()
```
Expand Down Expand Up @@ -102,7 +113,7 @@ The [locator](/api/browser/locators) of your `container`. It is useful to use qu
```js
import { render } from 'vitest-browser-vue'

const { locator } = render(NumberDisplay, {
const { locator } = await render(NumberDisplay, {
props: { number: 2 }
})

Expand All @@ -125,27 +136,37 @@ This method is a shortcut for `console.log(prettyDOM(baseElement))`. It will pri
#### rerender

```ts
function rerender(props: Partial<Props>): void
function rerender(props: Partial<Props>): void & PromiseLike<void>
```

Also records a `vue.rerender` trace mark in the [Trace View](/guide/browser/trace-view).

It is better if you test the component that's doing the prop updating to ensure that the props are being updated correctly to avoid relying on implementation details in your tests. That said, if you'd prefer to update the props of a rendered component in your test, this function can be used to update props of the rendered component.

::: warning
Synchronous usage of `rerender` is deprecated and will be removed in the next major version. Please always `await` the result.
:::

```js
import { render } from 'vitest-browser-vue'

const { rerender } = render(NumberDisplay, { props: { number: 1 } })
const { rerender } = await render(NumberDisplay, { props: { number: 1 } })

// re-render the same component with different props
rerender({ number: 2 })
await rerender({ number: 2 })
```

#### unmount

```ts
function unmount(): void
function unmount(): void & PromiseLike<void>
```

This will cause the rendered component to be unmounted. This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks).
This will cause the rendered component to be unmounted. Also records a `vue.unmount` trace mark in the [Trace View](/guide/browser/trace-view). This is useful for testing what happens when your component is removed from the page (like testing that you don't leave event handlers hanging around causing memory leaks).

::: warning
Synchronous usage of `unmount` is deprecated and will be removed in the next major version. Please always `await` the result.
:::

#### emitted

Expand Down Expand Up @@ -182,7 +203,7 @@ locators.extend({
},
})

const screen = render(Component)
const screen = await render(Component)
await expect.element(
screen.getByArticleTitle('Hello World')
).toBeVisible()
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export function withCancel<T extends (...args: any[]) => any>(

const abortControllers = new WeakMap<TestContext, AbortController>()

export function abortIfTimeout([context]: [TestContext?], error: Error): void {
export function abortIfTimeout([context]: [TestContext?, unknown?], error: Error): void {
if (context) {
abortContextSignal(context, error)
}
Expand Down
7 changes: 6 additions & 1 deletion packages/runner/src/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,18 @@ export interface WithFixturesOptions {
* Current fixtures from the context.
*/
fixtures?: TestFixtures
/**
* The suite to use for fixture lookups.
* Used by beforeEach/afterEach/aroundEach hooks to pick up fixture overrides from the test's describe block.
*/
suite?: Suite
}

const contextHasFixturesCache = new WeakMap<TestContext, WeakSet<TestFixtureItem>>()

export function withFixtures(fn: Function, options?: WithFixturesOptions) {
const collector = getCurrentSuite()
const suite = collector.suite || collector.file
const suite = options?.suite || collector.suite || collector.file
return async (hookContext?: TestContext): Promise<any> => {
const context: (TestContext & { [key: string]: any }) | undefined = hookContext || options?.context as TestContext

Expand Down
17 changes: 14 additions & 3 deletions packages/runner/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,17 @@ export function beforeEach<ExtraContext = object>(
): void {
assertTypes(fn, '"beforeEach" callback', ['function'])
const stackTraceError = new Error('STACK_TRACE_ERROR')

const wrapper: BeforeEachListener<ExtraContext> = (context, suite) => {
const fixtureResolver = withFixtures(fn, { suite })
return fixtureResolver(context)
}

return getCurrentSuite<ExtraContext>().on(
'beforeEach',
Object.assign(
withTimeout(
withFixtures(fn),
wrapper,
timeout ?? getDefaultHookTimeout(),
true,
stackTraceError,
Expand Down Expand Up @@ -197,10 +203,15 @@ export function afterEach<ExtraContext = object>(
timeout?: number,
): void {
assertTypes(fn, '"afterEach" callback', ['function'])
const wrapper: AfterEachListener<ExtraContext> = (context, suite) => {
const fixtureResolver = withFixtures(fn, { suite })
return fixtureResolver(context)
}

return getCurrentSuite<ExtraContext>().on(
'afterEach',
withTimeout(
withFixtures(fn),
wrapper,
timeout ?? getDefaultHookTimeout(),
true,
new Error('STACK_TRACE_ERROR'),
Expand Down Expand Up @@ -375,7 +386,7 @@ export function aroundEach<ExtraContext = object>(
const innerFn = (ctx: any) => fn(runTest, ctx, suite)
configureProps(innerFn, { index: 1, original: fn })

const fixtureResolver = withFixtures(innerFn)
const fixtureResolver = withFixtures(innerFn, { suite })
return fixtureResolver(context)
}

Expand Down
Loading
Loading