Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(browser): move page.config to server.config, add more docs #6252

Merged
merged 2 commits into from
Jul 31, 2024
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
15 changes: 5 additions & 10 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,6 @@ export default ({ mode }: { mode: string }) => {
link: '/guide/browser/',
collapsed: false,
items: [
{
text: 'Assertion API',
link: '/guide/browser/assertion-api',
docFooterText: 'Assertion API | Browser Mode',
},
{
text: 'Retry-ability',
link: '/guide/browser/retry-ability',
docFooterText: 'Retry-ability | Browser Mode',
},
{
text: 'Context',
link: '/guide/browser/context',
Expand All @@ -277,6 +267,11 @@ export default ({ mode }: { mode: string }) => {
link: '/guide/browser/interactivity-api',
docFooterText: 'Interactivity API | Browser Mode',
},
{
text: 'Assertion API',
link: '/guide/browser/assertion-api',
docFooterText: 'Assertion API | Browser Mode',
},
{
text: 'Commands',
link: '/guide/browser/commands',
Expand Down
32 changes: 32 additions & 0 deletions docs/guide/browser/assertion-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,35 @@ If you are using TypeScript or want to have correct type hints in `expect`, make
}
```
:::

Tests in the browser might fail inconsistently due to their asynchronous nature. Because of this, it is important to have a way to guarantee that assertions succeed even if the condition is delayed (by a timeout, network request, or animation, for example). For this purpose, Vitest provides retriable assertions out of the box via the [`expect.poll`](/api/expect#poll) and `expect.element` APIs:

```ts
import { expect, test } from 'vitest'
import { screen } from '@testing-library/dom'

test('error banner is rendered', async () => {
triggerError()

// @testing-library provides queries with built-in retry-ability
// It will try to find the banner until it's rendered
const banner = await screen.findByRole('alert', {
name: /error/i,
})

// Vitest provides `expect.element` with built-in retry-ability
// It will check `element.textContent` until it's equal to "Error!"
await expect.element(banner).toHaveTextContent('Error!')
})
```

::: tip
`expect.element` is a shorthand for `expect.poll(() => element)` and works in exactly the same way.

`toHaveTextContent` and all other [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom) assertions are still available on a regular `expect` without a built-in retry-ability mechanism:

```ts
// will fail immediately if .textContent is not `'Error!'`
expect(banner).toHaveTextContent('Error!')
```
:::
98 changes: 69 additions & 29 deletions docs/guide/browser/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,13 @@ title: Context | Browser Mode

Vitest exposes a context module via `@vitest/browser/context` entry point. As of 2.0, it exposes a small set of utilities that might be useful to you in tests.

```ts
export const server: {
/**
* Platform the Vitest server is running on.
* The same as calling `process.platform` on the server.
*/
platform: Platform
/**
* Runtime version of the Vitest server.
* The same as calling `process.version` on the server.
*/
version: string
/**
* Name of the browser provider.
*/
provider: string
/**
* Name of the current browser.
*/
browser: string
/**
* Available commands for the browser.
*/
commands: BrowserCommands
}
## `userEvent`

::: tip
The `userEvent` API is explained in detail at [Interactivity API](/guide/browser/interactivity-api).
:::

```ts
/**
* Handler for user interactions. The support is implemented by the browser provider (`playwright` or `webdriverio`).
* If used with `preview` provider, fallbacks to simulated events via `@testing-library/user-event`.
Expand All @@ -56,18 +37,32 @@ export const userEvent: {
fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise<void>
dragAndDrop: (source: Element, target: Element, options?: UserEventDragAndDropOptions) => Promise<void>
}
```

## `commands`

::: tip
Commands API is explained in detail at [Commands](/guide/browser/commands).
:::

```ts
/**
* Available commands for the browser.
* A shortcut to `server.commands`.
*/
export const commands: BrowserCommands
```

## `page`

The `page` export provides utilities to interact with the current `page`.

::: warning
While it exposes some utilities from Playwright's `page`, it is not the same object. Since the browser context is evaluated in the browser, your tests don't have access to Playwright's `page` because it runs on the server.
:::

```ts
export const page: {
/**
* Serialized test config.
*/
config: SerializedConfig
/**
* Change the size of iframe's viewport.
*/
Expand All @@ -82,6 +77,51 @@ export const page: {
}>
screenshot(options?: ScreenshotOptions): Promise<string>
}
```

## `cdp`

The `cdp` export returns the current Chrome DevTools Protocol session. It is mostly useful to library authors to build tools on top of it.

::: warning
CDP session works only with `playwright` provider and only when using `chromium` browser. You can read more about it in playwright's [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) documentation.
:::

```ts
export const cdp: () => CDPSession
```

## `server`

The `server` export represents the Node.js environment where the Vitest server is running. It is mostly useful for debugging.

```ts
export const server: {
/**
* Platform the Vitest server is running on.
* The same as calling `process.platform` on the server.
*/
platform: Platform
/**
* Runtime version of the Vitest server.
* The same as calling `process.version` on the server.
*/
version: string
/**
* Name of the browser provider.
*/
provider: string
/**
* Name of the current browser.
*/
browser: string
/**
* Available commands for the browser.
*/
commands: BrowserCommands
/**
* Serialized test config.
*/
config: SerializedConfig
}
```
6 changes: 6 additions & 0 deletions docs/guide/browser/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ bun add -D vitest @vitest/browser

::: warning
However, to run tests in CI you need to install either [`playwright`](https://npmjs.com/package/playwright) or [`webdriverio`](https://www.npmjs.com/package/webdriverio). We also recommend switching to either one of them for testing locally instead of using the default `preview` provider since it relies on simulating events instead of using Chrome DevTools Protocol.

If you don't already use one of these tools, we recommend starting with Playwright because it supports parallel execution, which makes your tests run faster. Additionally, the Chrome DevTools Protocol that Playwright uses is generally faster than WebDriver.
:::

### Using Playwright

[Playwright](https://npmjs.com/package/playwright) is a framework for Web Testing and Automation.

::: code-group
```bash [npm]
npm install -D vitest @vitest/browser playwright
Expand All @@ -68,6 +72,8 @@ bun add -D vitest @vitest/browser playwright

### Using Webdriverio

[WebdriverIO](https://www.npmjs.com/package/webdriverio) allows you to run tests locally using the WebDriver protocol.

::: code-group
```bash [npm]
npm install -D vitest @vitest/browser webdriverio
Expand Down
37 changes: 0 additions & 37 deletions docs/guide/browser/retry-ability.md

This file was deleted.

8 changes: 4 additions & 4 deletions packages/browser/context.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ export const server: {
* @see {@link https://vitest.dev/guide/browser/commands}
*/
commands: BrowserCommands
/**
* Serialized test config.
*/
config: SerializedConfig
}

/**
Expand All @@ -250,10 +254,6 @@ export const userEvent: UserEvent
export const commands: BrowserCommands

export interface BrowserPage {
/**
* Serialized test config.
*/
config: SerializedConfig
/**
* Change the size of iframe's viewport.
*/
Expand Down
3 changes: 0 additions & 3 deletions packages/browser/src/client/tester/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,6 @@ export function cdp() {

const screenshotIds: Record<string, Record<string, string>> = {}
export const page: BrowserPage = {
get config() {
return runner().config
},
viewport(width, height) {
const id = runner().iframeId
channel.postMessage({ type: 'viewport', width, height, id })
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/node/plugins/pluginContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ export const server = {
browser: ${JSON.stringify(server.project.config.browser.name)},
commands: {
${commandsCode}
}
},
config: __vitest_browser_runner__.config,
}
export const commands = server.commands
export const userEvent = ${getUserEvent(provider)}
Expand Down
8 changes: 4 additions & 4 deletions test/browser/test/viewport.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { page, server } from '@vitest/browser/context'
import { server } from '@vitest/browser/context'
import { describe, expect, it } from 'vitest'

describe.skipIf(server.provider === 'preview')('viewport window has been properly initialized', () => {
it.skipIf(!page.config.browser.headless)('viewport has proper size', () => {
const { width, height } = page.config.browser.viewport
it.skipIf(!server.config.browser.headless)('viewport has proper size', () => {
const { width, height } = server.config.browser.viewport
const { width: actualWidth, height: actualHeight } = window.document.documentElement.getBoundingClientRect()

expect(actualWidth).toBe(width)
expect(actualHeight).toBe(height)
})

it.skipIf(page.config.browser.headless)('window has been maximized', () => {
it.skipIf(server.config.browser.headless)('window has been maximized', () => {
let topWindow = window
while (topWindow.parent && topWindow !== topWindow.parent) {
topWindow = topWindow.parent as unknown as any
Expand Down