From af2b813c7526339d277eb473241fc6fa19b79d51 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 31 Jul 2024 20:06:12 +0200 Subject: [PATCH] feat(browser): move page.config to server.config, add more docs (#6252) --- docs/.vitepress/config.ts | 15 +-- docs/guide/browser/assertion-api.md | 32 ++++++ docs/guide/browser/context.md | 98 +++++++++++++------ docs/guide/browser/index.md | 6 ++ docs/guide/browser/retry-ability.md | 37 ------- packages/browser/context.d.ts | 8 +- packages/browser/src/client/tester/context.ts | 3 - .../browser/src/node/plugins/pluginContext.ts | 3 +- test/browser/test/viewport.test.ts | 8 +- 9 files changed, 122 insertions(+), 88 deletions(-) delete mode 100644 docs/guide/browser/retry-ability.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 112abf20131b..eadf771bad12 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -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', @@ -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', diff --git a/docs/guide/browser/assertion-api.md b/docs/guide/browser/assertion-api.md index ff84070269e6..726675490012 100644 --- a/docs/guide/browser/assertion-api.md +++ b/docs/guide/browser/assertion-api.md @@ -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!') +``` +::: diff --git a/docs/guide/browser/context.md b/docs/guide/browser/context.md index 097dd4f87d7d..90a65c65b237 100644 --- a/docs/guide/browser/context.md +++ b/docs/guide/browser/context.md @@ -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`. @@ -56,18 +37,32 @@ export const userEvent: { fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise dragAndDrop: (source: Element, target: Element, options?: UserEventDragAndDropOptions) => Promise } +``` + +## `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. */ @@ -82,6 +77,51 @@ export const page: { }> screenshot(options?: ScreenshotOptions): Promise } +``` + +## `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 +} +``` diff --git a/docs/guide/browser/index.md b/docs/guide/browser/index.md index 32eb10f92580..0449b08efe29 100644 --- a/docs/guide/browser/index.md +++ b/docs/guide/browser/index.md @@ -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 @@ -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 diff --git a/docs/guide/browser/retry-ability.md b/docs/guide/browser/retry-ability.md deleted file mode 100644 index 65e4180eba54..000000000000 --- a/docs/guide/browser/retry-ability.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Retry-ability | Browser Mode ---- - -# Retry-ability - -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!') -``` -::: diff --git a/packages/browser/context.d.ts b/packages/browser/context.d.ts index 6f755e3aa260..4fc68d37998a 100644 --- a/packages/browser/context.d.ts +++ b/packages/browser/context.d.ts @@ -233,6 +233,10 @@ export const server: { * @see {@link https://vitest.dev/guide/browser/commands} */ commands: BrowserCommands + /** + * Serialized test config. + */ + config: SerializedConfig } /** @@ -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. */ diff --git a/packages/browser/src/client/tester/context.ts b/packages/browser/src/client/tester/context.ts index 54bf7847044b..76e673edc672 100644 --- a/packages/browser/src/client/tester/context.ts +++ b/packages/browser/src/client/tester/context.ts @@ -246,9 +246,6 @@ export function cdp() { const screenshotIds: Record> = {} export const page: BrowserPage = { - get config() { - return runner().config - }, viewport(width, height) { const id = runner().iframeId channel.postMessage({ type: 'viewport', width, height, id }) diff --git a/packages/browser/src/node/plugins/pluginContext.ts b/packages/browser/src/node/plugins/pluginContext.ts index 6546d5b48a03..701e1abc0e65 100644 --- a/packages/browser/src/node/plugins/pluginContext.ts +++ b/packages/browser/src/node/plugins/pluginContext.ts @@ -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)} diff --git a/test/browser/test/viewport.test.ts b/test/browser/test/viewport.test.ts index 146faae1c66a..6e4f07a6f240 100644 --- a/test/browser/test/viewport.test.ts +++ b/test/browser/test/viewport.test.ts @@ -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