mirror of
https://github.com/hanzoai/vitest.git
synced 2026-09-24 17:04:25 +00:00
refactor!: remove webdriverio package (#10675)
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
name: Setup WebDriverIO Browsers
|
||||
description: Setup Chrome, ChromeDriver, and optional Firefox for WebDriverIO tests
|
||||
|
||||
inputs:
|
||||
chrome-version:
|
||||
description: Chrome for Testing version to install, or false to skip
|
||||
# Pin to Chrome 148 while Chrome for Testing 149 Linux downloads/install are broken.
|
||||
default: '148'
|
||||
firefox-version:
|
||||
description: Firefox version to install, or false to skip
|
||||
default: latest
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
|
||||
if: inputs.chrome-version != 'false'
|
||||
id: setup-chrome
|
||||
with:
|
||||
chrome-version: ${{ inputs.chrome-version }}
|
||||
install-chromedriver: true
|
||||
|
||||
- name: Export Chrome binaries
|
||||
if: inputs.chrome-version != 'false'
|
||||
shell: bash
|
||||
env:
|
||||
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
CHROMEDRIVER_PATH_VALUE: ${{ steps.setup-chrome.outputs.chromedriver-path }}
|
||||
run: | # zizmor: ignore[github-env] values come from pinned browser-actions/setup-chrome outputs.
|
||||
echo "CHROME_BIN=$CHROME_PATH" >> "$GITHUB_ENV"
|
||||
echo "CHROMEDRIVER_PATH=$CHROMEDRIVER_PATH_VALUE" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Allow setup-chrome sandbox
|
||||
if: inputs.chrome-version != 'false' && runner.os == 'Linux'
|
||||
shell: bash
|
||||
env:
|
||||
CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||
run: |
|
||||
# Chrome for Testing is not covered by Ubuntu's AppArmor sandbox policy on CI.
|
||||
# https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu
|
||||
# https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md
|
||||
# https://github.com/browser-actions/setup-chrome/issues/639
|
||||
cat <<EOF | sudo tee /etc/apparmor.d/chrome-for-testing
|
||||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
profile chrome-for-testing "$CHROME_PATH" flags=(unconfined) {
|
||||
userns,
|
||||
include if exists <local/chrome>
|
||||
}
|
||||
EOF
|
||||
sudo apparmor_parser -r /etc/apparmor.d/chrome-for-testing
|
||||
|
||||
- uses: browser-actions/setup-firefox@fcf821c621167805dd63a29662bd7cb5676c81a8 # v1.7.1
|
||||
if: inputs.firefox-version != 'false'
|
||||
id: setup-firefox
|
||||
with:
|
||||
firefox-version: ${{ inputs.firefox-version }}
|
||||
|
||||
- name: Export Firefox binary
|
||||
if: inputs.firefox-version != 'false'
|
||||
shell: bash
|
||||
env:
|
||||
FIREFOX_PATH: ${{ steps.setup-firefox.outputs.firefox-path }}
|
||||
run: echo "FIREFOX_BIN=$FIREFOX_PATH" >> "$GITHUB_ENV" # zizmor: ignore[github-env] value comes from pinned browser-actions/setup-firefox output.
|
||||
@@ -109,11 +109,6 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
|
||||
- uses: ./.github/actions/setup-webdriverio
|
||||
id: setup-webdriverio
|
||||
with:
|
||||
firefox-version: false
|
||||
|
||||
- name: Install
|
||||
run: pnpm i
|
||||
|
||||
@@ -179,11 +174,6 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
|
||||
- uses: ./.github/actions/setup-webdriverio
|
||||
id: setup-webdriverio
|
||||
with:
|
||||
firefox-version: false
|
||||
|
||||
- name: Install
|
||||
run: pnpm i
|
||||
|
||||
@@ -220,9 +210,6 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
|
||||
- uses: ./.github/actions/setup-webdriverio
|
||||
id: setup-webdriverio
|
||||
|
||||
- name: Install
|
||||
run: pnpm i
|
||||
|
||||
@@ -234,9 +221,6 @@ jobs:
|
||||
- name: Test Browser (playwright)
|
||||
run: pnpm run test:browser:playwright
|
||||
|
||||
- name: Test Browser (webdriverio)
|
||||
run: pnpm run test:browser:webdriverio
|
||||
|
||||
test-vite7:
|
||||
needs: changed
|
||||
# macos-latest is the fastest one
|
||||
@@ -255,11 +239,6 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- uses: ./.github/actions/setup-webdriverio
|
||||
id: setup-webdriverio
|
||||
with:
|
||||
firefox-version: false
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
pnpm override-vite7
|
||||
|
||||
@@ -33,7 +33,7 @@ Vitest is a next-generation testing framework powered by Vite. This is a monorep
|
||||
- **Examples**: `CI=true pnpm test:examples`
|
||||
- **Specific test suite**: `CI=true cd test/<test-folder> && pnpm test <test-file>`
|
||||
- **Unit directory test**: `CI=true pnpm test <test-file>` (for `test/unit`)
|
||||
- **Browser tests**: `CI=true pnpm test:browser:playwright` or `CI=true pnpm test:browser:webdriverio`
|
||||
- **Browser tests**: `CI=true pnpm test:browser:playwright`
|
||||
|
||||
**IMPORTANT: Do NOT use `--` when passing test filters to pnpm.**
|
||||
Using `--` causes pnpm to drop the filter, resulting in a full test run instead of a filtered one.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Configuring WebdriverIO
|
||||
|
||||
::: info Playwright vs WebdriverIO
|
||||
If you do not already use WebdriverIO in your project, we recommend starting with [Playwright](/config/browser/playwright) as it is easier to configure and has more flexible API.
|
||||
::: info Community maintained
|
||||
The WebdriverIO provider ([`@vitest/browser-webdriverio`](https://github.com/vitest-community/vitest-webdriverio)) is maintained by the Vitest community in the [`vitest-community`](https://github.com/vitest-community) organization, separately from the core Vitest packages. Please report provider-specific issues to its repository.
|
||||
:::
|
||||
|
||||
To run tests using WebdriverIO, you need to install the [`@vitest/browser-webdriverio`](https://npmx.dev/package/@vitest/browser-webdriverio) npm package and specify its `webdriverio` export in the `test.browser.provider` property of your config:
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"ui:dev": "npm -C packages/ui run dev:client",
|
||||
"ui:test": "npm -C packages/ui run test:run",
|
||||
"override-vite7": "yq -i '.overrides.vite = \"npm:vite@7\"' pnpm-workspace.yaml",
|
||||
"test:browser:webdriverio": "pnpm -C test/browser run test:webdriverio",
|
||||
"test:browser:playwright": "pnpm -C test/browser run test:playwright"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# @vitest/browser-webdriverio
|
||||
|
||||
[](https://npmx.dev/package/@vitest/browser-webdriverio)
|
||||
|
||||
Run your Vitest [browser tests](https://vitest.dev/guide/browser/) using [webdriverio](https://webdriver.io/docs/api/browser) API. Note that Vitest does not use webdriverio as a test runner, but only as a browser provider.
|
||||
|
||||
We recommend using this package if you are already using webdriverio in your project.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package with your favorite package manager:
|
||||
|
||||
```sh
|
||||
npm install -D @vitest/browser-webdriverio
|
||||
# or
|
||||
yarn add -D @vitest/browser-webdriverio
|
||||
# or
|
||||
pnpm add -D @vitest/browser-webdriverio
|
||||
```
|
||||
|
||||
Then specify it in the `browser.provider` field of your Vitest configuration:
|
||||
|
||||
```ts
|
||||
// vitest.config.ts
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import { webdriverio } from '@vitest/browser-webdriverio'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
browser: {
|
||||
provider: webdriverio({
|
||||
// ...custom webdriverio options
|
||||
}),
|
||||
instances: [
|
||||
{ browser: 'chrome' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Then run Vitest in the browser mode:
|
||||
|
||||
```sh
|
||||
npx vitest --browser
|
||||
```
|
||||
|
||||
[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/browser-webdriverio) | [Documentation](https://vitest.dev/config/browser/webdriverio)
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from '@vitest/browser/context'
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"name": "@vitest/browser-webdriverio",
|
||||
"type": "module",
|
||||
"version": "5.0.0-beta.5",
|
||||
"description": "Browser running for Vitest using webdriverio",
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/vitest",
|
||||
"homepage": "https://vitest.dev/config/browser/webdriverio",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vitest-dev/vitest.git",
|
||||
"directory": "packages/browser-webdriverio"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vitest-dev/vitest/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"browser",
|
||||
"webdriverio",
|
||||
"wdio",
|
||||
"component",
|
||||
"mocks",
|
||||
"test",
|
||||
"testing"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./context": {
|
||||
"types": "./context.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"context.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "premove dist && pnpm rollup -c",
|
||||
"dev": "rollup -c --watch --watch.include 'src/**'"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": "workspace:*",
|
||||
"webdriverio": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"webdriverio": {
|
||||
"optional": false
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitest/browser": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wdio/types": "^9.24.0",
|
||||
"vitest": "workspace:*",
|
||||
"webdriverio": "^9.20.0"
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import json from '@rollup/plugin-json'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import { defineConfig } from 'rollup'
|
||||
import oxc from 'unplugin-oxc/rollup'
|
||||
import { createDtsUtils } from '../../scripts/build-utils.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const pkg = require('./package.json')
|
||||
|
||||
const external = [
|
||||
...Object.keys(pkg.dependencies),
|
||||
...Object.keys(pkg.peerDependencies || {}),
|
||||
/^@?vitest(\/|$)/,
|
||||
]
|
||||
|
||||
const dtsUtils = createDtsUtils()
|
||||
|
||||
const plugins = [
|
||||
resolve({
|
||||
preferBuiltins: true,
|
||||
}),
|
||||
json(),
|
||||
commonjs(),
|
||||
oxc({
|
||||
transform: { target: 'node20' },
|
||||
}),
|
||||
]
|
||||
|
||||
export default () =>
|
||||
defineConfig([
|
||||
{
|
||||
input: {
|
||||
index: './src/index.ts',
|
||||
locators: './src/locators.ts',
|
||||
},
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'esm',
|
||||
},
|
||||
external,
|
||||
context: 'null',
|
||||
plugins: [
|
||||
...dtsUtils.isolatedDecl(),
|
||||
...plugins,
|
||||
],
|
||||
},
|
||||
{
|
||||
input: dtsUtils.dtsInput('src/index.ts'),
|
||||
output: {
|
||||
dir: 'dist',
|
||||
entryFileNames: '[name].d.ts',
|
||||
format: 'esm',
|
||||
},
|
||||
watch: false,
|
||||
external,
|
||||
plugins: dtsUtils.dts(),
|
||||
},
|
||||
])
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const clear: UserEventCommand<UserEvent['clear']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.$(selector).clearValue()
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const click: UserEventCommand<UserEvent['click']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
options,
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.$(selector).click(options)
|
||||
}
|
||||
|
||||
export const dblClick: UserEventCommand<UserEvent['dblClick']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
_options,
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.$(selector).doubleClick()
|
||||
}
|
||||
|
||||
export const tripleClick: UserEventCommand<UserEvent['tripleClick']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
_options,
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser
|
||||
.action('pointer', { parameters: { pointerType: 'mouse' } })
|
||||
// move the pointer over the button
|
||||
.move({ origin: browser.$(selector) })
|
||||
// simulate 3 clicks
|
||||
.down()
|
||||
.up()
|
||||
.pause(50)
|
||||
.down()
|
||||
.up()
|
||||
.pause(50)
|
||||
.down()
|
||||
.up()
|
||||
.pause(50)
|
||||
// run the sequence
|
||||
.perform()
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const dragAndDrop: UserEventCommand<UserEvent['dragAndDrop']> = async (
|
||||
context,
|
||||
{ selector: source },
|
||||
{ selector: target },
|
||||
options_,
|
||||
) => {
|
||||
const $source = context.browser.$(source)
|
||||
const $target = context.browser.$(target)
|
||||
const options = (options_ || {}) as any
|
||||
const duration = options.duration ?? 10
|
||||
|
||||
// https://github.com/webdriverio/webdriverio/issues/8022#issuecomment-1700919670
|
||||
await context.browser
|
||||
.action('pointer')
|
||||
.move({ duration: 0, origin: $source, x: options.sourceX ?? 0, y: options.sourceY ?? 0 })
|
||||
.down({ button: 0 })
|
||||
.move({ duration: 0, origin: 'pointer', x: 0, y: 0 })
|
||||
.pause(duration)
|
||||
.move({ duration: 0, origin: $target, x: options.targetX ?? 0, y: options.targetY ?? 0 })
|
||||
.move({ duration: 0, origin: 'pointer', x: 1, y: 0 })
|
||||
.move({ duration: 0, origin: 'pointer', x: -1, y: 0 })
|
||||
.up({ button: 0 })
|
||||
.perform()
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const fill: UserEventCommand<UserEvent['fill']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
text,
|
||||
_options = {},
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.$(selector).setValue(text)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const hover: UserEventCommand<UserEvent['hover']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
options = {},
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.$(selector).moveTo(options as any)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { clear } from './clear'
|
||||
import { click, dblClick, tripleClick } from './click'
|
||||
import { dragAndDrop } from './dragAndDrop'
|
||||
import { fill } from './fill'
|
||||
import { hover } from './hover'
|
||||
import { keyboard, keyboardCleanup } from './keyboard'
|
||||
import { takeScreenshot } from './screenshot'
|
||||
import { selectOptions } from './select'
|
||||
import { tab } from './tab'
|
||||
import { type } from './type'
|
||||
import { upload } from './upload'
|
||||
import { viewport } from './viewport'
|
||||
import { wheel } from './wheel'
|
||||
|
||||
export default {
|
||||
__vitest_upload: upload as typeof upload,
|
||||
__vitest_click: click as typeof click,
|
||||
__vitest_dblClick: dblClick as typeof dblClick,
|
||||
__vitest_tripleClick: tripleClick as typeof tripleClick,
|
||||
__vitest_wheel: wheel as typeof wheel,
|
||||
__vitest_takeScreenshot: takeScreenshot as typeof takeScreenshot,
|
||||
__vitest_type: type as typeof type,
|
||||
__vitest_clear: clear as typeof clear,
|
||||
__vitest_fill: fill as typeof fill,
|
||||
__vitest_tab: tab as typeof tab,
|
||||
__vitest_keyboard: keyboard as typeof keyboard,
|
||||
__vitest_selectOptions: selectOptions as typeof selectOptions,
|
||||
__vitest_dragAndDrop: dragAndDrop as typeof dragAndDrop,
|
||||
__vitest_hover: hover as typeof hover,
|
||||
__vitest_cleanup: keyboardCleanup as typeof keyboardCleanup,
|
||||
__vitest_viewport: viewport as typeof viewport,
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import type { BrowserProvider } from 'vitest/node'
|
||||
import type { WebdriverBrowserProvider } from '../webdriverio'
|
||||
import type { UserEventCommand } from './utils'
|
||||
import { parseKeyDef } from '@vitest/browser'
|
||||
import { Key } from 'webdriverio'
|
||||
|
||||
export interface KeyboardState {
|
||||
unreleased: string[]
|
||||
}
|
||||
|
||||
export const keyboard: UserEventCommand<(
|
||||
text: string,
|
||||
state: KeyboardState,
|
||||
) => Promise<{ unreleased: string[] }>> = async (
|
||||
context,
|
||||
text,
|
||||
state,
|
||||
) => {
|
||||
await context.browser.execute(focusIframe)
|
||||
|
||||
const pressed = new Set<string>(state.unreleased)
|
||||
|
||||
await keyboardImplementation(
|
||||
pressed,
|
||||
context.provider,
|
||||
context.sessionId,
|
||||
text,
|
||||
async () => {
|
||||
await context.browser.execute(selectAll)
|
||||
},
|
||||
true,
|
||||
)
|
||||
|
||||
return {
|
||||
unreleased: Array.from(pressed),
|
||||
}
|
||||
}
|
||||
|
||||
export const keyboardCleanup: UserEventCommand<(state: KeyboardState) => Promise<void>> = async (
|
||||
context,
|
||||
state,
|
||||
) => {
|
||||
if (!state.unreleased) {
|
||||
return
|
||||
}
|
||||
const keyboard = context.browser.action('key')
|
||||
for (const key of state.unreleased) {
|
||||
keyboard.up(key)
|
||||
}
|
||||
await keyboard.perform()
|
||||
}
|
||||
|
||||
export async function keyboardImplementation(
|
||||
pressed: Set<string>,
|
||||
provider: BrowserProvider,
|
||||
_sessionId: string,
|
||||
text: string,
|
||||
selectAll: () => Promise<void>,
|
||||
skipRelease: boolean,
|
||||
): Promise<{ pressed: Set<string> }> {
|
||||
const browser = (provider as WebdriverBrowserProvider).browser!
|
||||
const actions = parseKeyDef(text)
|
||||
|
||||
let keyboard = browser.action('key')
|
||||
|
||||
for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
|
||||
let key = keyDef.key!
|
||||
const special = Key[key as 'Shift']
|
||||
|
||||
if (special) {
|
||||
key = special
|
||||
}
|
||||
|
||||
if (pressed.has(key)) {
|
||||
keyboard.up(key)
|
||||
pressed.delete(key)
|
||||
}
|
||||
|
||||
if (!releasePrevious) {
|
||||
if (key === 'selectall') {
|
||||
await keyboard.perform()
|
||||
keyboard = browser.action('key')
|
||||
await selectAll()
|
||||
continue
|
||||
}
|
||||
|
||||
for (let i = 1; i <= repeat; i++) {
|
||||
keyboard.down(key)
|
||||
}
|
||||
|
||||
if (releaseSelf) {
|
||||
keyboard.up(key)
|
||||
}
|
||||
else {
|
||||
pressed.add(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// seems like webdriverio doesn't release keys automatically if skipRelease is true and all events are keyUp
|
||||
const allRelease = keyboard.toJSON().actions.every(action => action.type === 'keyUp')
|
||||
|
||||
await keyboard.perform(allRelease ? false : skipRelease)
|
||||
|
||||
return {
|
||||
pressed,
|
||||
}
|
||||
}
|
||||
|
||||
function focusIframe() {
|
||||
if (
|
||||
!document.activeElement
|
||||
|| document.activeElement.ownerDocument !== document
|
||||
|| document.activeElement === document.body
|
||||
) {
|
||||
window.focus()
|
||||
}
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
const element = document.activeElement as HTMLInputElement
|
||||
if (element && typeof element.select === 'function') {
|
||||
element.select()
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import type { SerializedLocator } from '@vitest/browser'
|
||||
import type { ScreenshotOptions } from 'vitest/browser'
|
||||
import type { BrowserCommandContext } from 'vitest/node'
|
||||
import crypto from 'node:crypto'
|
||||
import { mkdir, rm } from 'node:fs/promises'
|
||||
import { normalize as platformNormalize } from 'node:path'
|
||||
import { resolveScreenshotPath } from '@vitest/browser'
|
||||
import { dirname, normalize, resolve } from 'pathe'
|
||||
|
||||
interface ScreenshotCommandOptions extends Omit<ScreenshotOptions, 'element' | 'mask'> {
|
||||
element?: SerializedLocator
|
||||
mask?: readonly SerializedLocator[]
|
||||
target?: 'element' | 'page'
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a screenshot using the provided browser context and returns a buffer and the expected screenshot path.
|
||||
*
|
||||
* **Note**: the returned `path` indicates where the screenshot *might* be found.
|
||||
* It is not guaranteed to exist, especially if `options.save` is `false`.
|
||||
*
|
||||
* @throws {Error} If the function is not called within a test or if the browser provider does not support screenshots.
|
||||
*/
|
||||
export async function takeScreenshot(
|
||||
context: BrowserCommandContext,
|
||||
name: string,
|
||||
options: Omit<ScreenshotCommandOptions, 'base64'>,
|
||||
): Promise<{ buffer: Buffer<ArrayBufferLike>; path: string }> {
|
||||
if (!context.testPath) {
|
||||
throw new Error(`Cannot take a screenshot without a test path`)
|
||||
}
|
||||
|
||||
const path = resolveScreenshotPath(
|
||||
context.testPath,
|
||||
name,
|
||||
context.project.config,
|
||||
options.path,
|
||||
)
|
||||
|
||||
// playwright does not need a screenshot path if we don't intend to save it
|
||||
let savePath: string | undefined
|
||||
|
||||
if (options.save) {
|
||||
savePath = normalize(path)
|
||||
|
||||
await mkdir(dirname(savePath), { recursive: true })
|
||||
}
|
||||
|
||||
// webdriverio needs a path, so if one is not already set we create a temporary one
|
||||
if (savePath === undefined) {
|
||||
savePath = resolve(context.project.tmpDir, crypto.randomUUID())
|
||||
|
||||
await mkdir(context.project.tmpDir, { recursive: true })
|
||||
}
|
||||
|
||||
// webdriverio expects the path to contain the extension and only works with PNG files
|
||||
const savePathWithExtension = savePath.endsWith('.png') ? savePath : `${savePath}.png`
|
||||
|
||||
// there seems to be a bug in webdriverio, `X:/` gets appended to cwd, so we convert to `X:\`
|
||||
const normalizedSavePath = platformNormalize(savePathWithExtension)
|
||||
// `browser.saveScreenshot` captures the top-level page for `expect(page)`;
|
||||
// element and plain `page.screenshot()` calls keep the existing body fallback.
|
||||
const buffer = options.target === 'page'
|
||||
? await context.browser.saveScreenshot(normalizedSavePath)
|
||||
: await context.browser.$(options.element?.selector ?? 'body').saveScreenshot(
|
||||
normalizedSavePath,
|
||||
)
|
||||
|
||||
if (!options.save) {
|
||||
await rm(savePathWithExtension, { force: true })
|
||||
}
|
||||
|
||||
return { buffer, path }
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const selectOptions: UserEventCommand<UserEvent['selectOptions']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
userValues,
|
||||
_options = {},
|
||||
) => {
|
||||
const values = userValues as any as [({ index: number })]
|
||||
|
||||
if (!values.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const browser = context.browser
|
||||
|
||||
if (values.length === 1 && 'index' in values[0]) {
|
||||
const selectElement = browser.$(selector)
|
||||
await selectElement.selectByIndex(values[0].index)
|
||||
}
|
||||
else {
|
||||
throw new Error('Provider "webdriverio" doesn\'t support selecting multiple values at once')
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
import { Key } from 'webdriverio'
|
||||
|
||||
export const tab: UserEventCommand<UserEvent['tab']> = async (
|
||||
context,
|
||||
options = {},
|
||||
) => {
|
||||
const browser = context.browser
|
||||
await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab])
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { UserEvent } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
import { keyboardImplementation } from './keyboard'
|
||||
|
||||
export const type: UserEventCommand<UserEvent['type']> = async (
|
||||
context,
|
||||
{ selector },
|
||||
text,
|
||||
options = {},
|
||||
) => {
|
||||
const { skipClick = false, skipAutoClose = false } = options
|
||||
const unreleased = new Set(Reflect.get(options, 'unreleased') as string[] ?? [])
|
||||
|
||||
const browser = context.browser
|
||||
const element = browser.$(selector)
|
||||
|
||||
if (!skipClick && !await element.isFocused()) {
|
||||
await element.click()
|
||||
}
|
||||
|
||||
await keyboardImplementation(
|
||||
unreleased,
|
||||
context.provider,
|
||||
context.sessionId,
|
||||
text,
|
||||
() => browser.execute(() => {
|
||||
const element = document.activeElement as HTMLInputElement
|
||||
if (element && typeof element.select === 'function') {
|
||||
element.select()
|
||||
}
|
||||
}),
|
||||
skipAutoClose,
|
||||
)
|
||||
|
||||
return {
|
||||
unreleased: Array.from(unreleased),
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { SerializedLocator } from '@vitest/browser'
|
||||
import type { UserEventUploadOptions } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
import { resolve } from 'pathe'
|
||||
|
||||
export const upload: UserEventCommand<(element: SerializedLocator, files: Array<string | {
|
||||
name: string
|
||||
mimeType: string
|
||||
base64: string
|
||||
}>, options: UserEventUploadOptions) => void> = async (
|
||||
context,
|
||||
{ selector },
|
||||
files,
|
||||
_options,
|
||||
) => {
|
||||
const testPath = context.testPath
|
||||
if (!testPath) {
|
||||
throw new Error(`Cannot upload files outside of a test`)
|
||||
}
|
||||
const root = context.project.config.root
|
||||
|
||||
for (const file of files) {
|
||||
if (typeof file !== 'string') {
|
||||
throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`)
|
||||
}
|
||||
}
|
||||
|
||||
const element = context.browser.$(selector)
|
||||
|
||||
for (const file of files) {
|
||||
const filepath = resolve(root, file as string)
|
||||
const remoteFilePath = await context.browser.uploadFile(filepath)
|
||||
await element.addValue(remoteFilePath)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { SerializedLocator } from '@vitest/browser'
|
||||
import type { Locator } from 'vitest/browser'
|
||||
import type { BrowserCommand } from 'vitest/node'
|
||||
|
||||
export type UserEventCommand<T extends (...args: any) => any> = BrowserCommand<
|
||||
ConvertUserEventParameters<Parameters<T>>
|
||||
>
|
||||
|
||||
type ConvertElementToLocator<T> = T extends Element | Locator ? SerializedLocator : T
|
||||
type ConvertUserEventParameters<T extends unknown[]> = {
|
||||
[K in keyof T]: ConvertElementToLocator<T[K]>;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { WebdriverBrowserProvider } from '../webdriverio'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
export const viewport: UserEventCommand<(options: {
|
||||
width: number
|
||||
height: number
|
||||
}) => void> = async (context, options) => {
|
||||
await (context.provider as WebdriverBrowserProvider).setViewport(options)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Locator, UserEventWheelDeltaOptions } from 'vitest/browser'
|
||||
import type { UserEventCommand } from './utils'
|
||||
|
||||
type WheelCommand = (element: Locator | Element, options: UserEventWheelDeltaOptions) => Promise<void>
|
||||
|
||||
export const wheel: UserEventCommand<WheelCommand> = async (
|
||||
context,
|
||||
{ selector },
|
||||
options,
|
||||
) => {
|
||||
const browser = context.browser
|
||||
const times = options.times ?? 1
|
||||
const deltaX = options.delta.x ?? 0
|
||||
const deltaY = options.delta.y ?? 0
|
||||
|
||||
let action = browser.action('wheel')
|
||||
const wheelOptions: Parameters<typeof action['scroll']>[0] = {
|
||||
deltaX,
|
||||
deltaY,
|
||||
origin: browser.$(selector),
|
||||
}
|
||||
|
||||
for (let count = 0; count < times; count += 1) {
|
||||
action = action.scroll(wheelOptions)
|
||||
}
|
||||
|
||||
await action.perform()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { resolve } from 'pathe'
|
||||
|
||||
const pkgRoot = resolve(fileURLToPath(import.meta.url), '../..')
|
||||
export const distRoot: string = resolve(pkgRoot, 'dist')
|
||||
@@ -1,6 +0,0 @@
|
||||
export {
|
||||
WebdriverBrowserProvider,
|
||||
webdriverio,
|
||||
type WebdriverProviderOptions,
|
||||
} from './webdriverio'
|
||||
export { defineBrowserCommand } from '@vitest/browser'
|
||||
@@ -1,308 +0,0 @@
|
||||
import type {
|
||||
LocatorScreenshotOptions,
|
||||
UserEventClearOptions,
|
||||
UserEventClickOptions,
|
||||
UserEventDragAndDropOptions,
|
||||
UserEventFillOptions,
|
||||
UserEventHoverOptions,
|
||||
UserEventSelectOptions,
|
||||
UserEventWheelOptions,
|
||||
} from 'vitest/browser'
|
||||
import {
|
||||
convertElementToCssSelector,
|
||||
ensureAwaited,
|
||||
getByAltTextSelector,
|
||||
getByLabelSelector,
|
||||
getByPlaceholderSelector,
|
||||
getByRoleSelector,
|
||||
getByTestIdSelector,
|
||||
getByTextSelector,
|
||||
getByTitleSelector,
|
||||
getIframeScale,
|
||||
Locator,
|
||||
selectorEngine,
|
||||
triggerCommandWithTrace,
|
||||
} from '@vitest/browser/locators'
|
||||
import { page, server, utils } from 'vitest/browser'
|
||||
import { __INTERNAL } from 'vitest/internal/browser'
|
||||
|
||||
class WebdriverIOLocator extends Locator {
|
||||
constructor(protected _pwSelector: string, protected _container?: Element) {
|
||||
super()
|
||||
}
|
||||
|
||||
// This exists to avoid calling `this.elements` in `this.selector`'s getter in interactive actions
|
||||
private withElement(
|
||||
element: Element,
|
||||
error: Error | undefined,
|
||||
pwSelector: string,
|
||||
) {
|
||||
const cssSelector = convertElementToCssSelector(element)
|
||||
return new ElementWebdriverIOLocator(cssSelector, error, pwSelector, element)
|
||||
}
|
||||
|
||||
override get selector(): string {
|
||||
const selectors = this.elements().map(element => convertElementToCssSelector(element))
|
||||
if (!selectors.length) {
|
||||
throw utils.getElementError(this._pwSelector, this._container || document.body)
|
||||
}
|
||||
let hasShadowRoot = false
|
||||
const newSelectors = selectors.map((selector) => {
|
||||
if (selector.startsWith('>>>')) {
|
||||
hasShadowRoot = true
|
||||
return selector.slice(3)
|
||||
}
|
||||
return selector
|
||||
})
|
||||
return (hasShadowRoot ? '>>>' : '') + newSelectors.join(', ')
|
||||
}
|
||||
|
||||
public override click(options?: UserEventClickOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).click(processClickOptions(options))
|
||||
})
|
||||
}
|
||||
|
||||
public override dblClick(options?: UserEventClickOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).dblClick(processClickOptions(options))
|
||||
})
|
||||
}
|
||||
|
||||
public override tripleClick(options?: UserEventClickOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).tripleClick(processClickOptions(options))
|
||||
})
|
||||
}
|
||||
|
||||
public selectOptions(
|
||||
value: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
|
||||
options?: UserEventSelectOptions,
|
||||
): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
const values = getWebdriverioSelectOptions(element, value)
|
||||
const selector = convertElementToCssSelector(element)
|
||||
const locator = this.asLocator()
|
||||
return triggerCommandWithTrace<void>({
|
||||
name: '__vitest_selectOptions',
|
||||
arguments: [{ selector, locator }, values, options],
|
||||
errorSource: error,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public override hover(options?: UserEventHoverOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).hover(processHoverOptions(options))
|
||||
})
|
||||
}
|
||||
|
||||
public override dropTo(target: Locator, options?: UserEventDragAndDropOptions): Promise<void> {
|
||||
// playwright doesn't enforce a single element, it selects the first one,
|
||||
// so we just follow the behavior
|
||||
return super.dropTo(target, processDragAndDropOptions(options))
|
||||
}
|
||||
|
||||
public override wheel(options: UserEventWheelOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).wheel(options)
|
||||
})
|
||||
}
|
||||
|
||||
public override clear(options?: UserEventClearOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).clear(options)
|
||||
})
|
||||
}
|
||||
|
||||
public override fill(text: string, options?: UserEventFillOptions): Promise<void> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).fill(text, options)
|
||||
})
|
||||
}
|
||||
|
||||
public override screenshot(options?: LocatorScreenshotOptions): Promise<any> {
|
||||
return ensureAwaited(async (error) => {
|
||||
const element = await this.findElement(options)
|
||||
return this.withElement(element, error, this._pwSelector).screenshot(options)
|
||||
})
|
||||
}
|
||||
|
||||
// playwright doesn't enforce a single element in upload
|
||||
// public override async upload(): Promise<void>
|
||||
|
||||
protected locator(selector: string) {
|
||||
return new WebdriverIOLocator(`${this._pwSelector} >> ${selector}`, this._container)
|
||||
}
|
||||
|
||||
protected elementLocator(element: Element) {
|
||||
return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(element), element)
|
||||
}
|
||||
}
|
||||
|
||||
const kElementLocator = Symbol.for('$$vitest:locator-resolved')
|
||||
|
||||
class ElementWebdriverIOLocator extends Locator {
|
||||
public [kElementLocator] = true
|
||||
|
||||
constructor(
|
||||
private _cssSelector: string,
|
||||
protected _errorSource: Error | undefined,
|
||||
protected _pwSelector: string,
|
||||
protected _container: Element,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
override get selector() {
|
||||
return this._cssSelector
|
||||
}
|
||||
|
||||
protected locator(_selector: string): Locator {
|
||||
throw new Error(`should not be called`)
|
||||
}
|
||||
|
||||
protected elementLocator(_element: Element): Locator {
|
||||
throw new Error(`should not be called`)
|
||||
}
|
||||
}
|
||||
|
||||
page.extend({
|
||||
getByLabelText(text, options) {
|
||||
return new WebdriverIOLocator(getByLabelSelector(text, options))
|
||||
},
|
||||
getByRole(role, options) {
|
||||
return new WebdriverIOLocator(getByRoleSelector(role, options))
|
||||
},
|
||||
getByTestId(testId) {
|
||||
return new WebdriverIOLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute, testId))
|
||||
},
|
||||
getByAltText(text, options) {
|
||||
return new WebdriverIOLocator(getByAltTextSelector(text, options))
|
||||
},
|
||||
getByPlaceholder(text, options) {
|
||||
return new WebdriverIOLocator(getByPlaceholderSelector(text, options))
|
||||
},
|
||||
getByText(text, options) {
|
||||
return new WebdriverIOLocator(getByTextSelector(text, options))
|
||||
},
|
||||
getByTitle(title, options) {
|
||||
return new WebdriverIOLocator(getByTitleSelector(title, options))
|
||||
},
|
||||
|
||||
elementLocator(element: Element) {
|
||||
return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(element))
|
||||
},
|
||||
})
|
||||
|
||||
__INTERNAL._createLocator = selector => new WebdriverIOLocator(selector)
|
||||
|
||||
function getWebdriverioSelectOptions(element: Element, value: string | string[] | HTMLElement[] | HTMLElement | Locator | Locator[]) {
|
||||
const options = [...element.querySelectorAll('option')] as HTMLOptionElement[]
|
||||
|
||||
const arrayValues = Array.isArray(value) ? value : [value]
|
||||
|
||||
if (!arrayValues.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (arrayValues.length > 1) {
|
||||
throw new Error('Provider "webdriverio" doesn\'t support selecting multiple values at once')
|
||||
}
|
||||
|
||||
const optionValue = arrayValues[0]
|
||||
|
||||
if (typeof optionValue !== 'string') {
|
||||
const element = ('element' in optionValue ? optionValue.element() : optionValue) as HTMLOptionElement
|
||||
const index = options.indexOf(element)
|
||||
if (index === -1) {
|
||||
throw new Error(`The element ${selectorEngine.previewNode(element)} was not found in the "select" options.`)
|
||||
}
|
||||
|
||||
return [{ index }]
|
||||
}
|
||||
|
||||
const valueIndex = options.findIndex(option => option.value === optionValue)
|
||||
if (valueIndex !== -1) {
|
||||
return [{ index: valueIndex }]
|
||||
}
|
||||
|
||||
const labelIndex = options.findIndex(option =>
|
||||
option.textContent?.trim() === optionValue || option.ariaLabel === optionValue,
|
||||
)
|
||||
|
||||
if (labelIndex === -1) {
|
||||
throw new Error(`The option "${optionValue}" was not found in the "select" options.`)
|
||||
}
|
||||
|
||||
return [{ index: labelIndex }]
|
||||
}
|
||||
|
||||
function processClickOptions(options?: UserEventClickOptions) {
|
||||
// only ui scales the iframe, so we need to adjust the position
|
||||
if (!options || !server.config.browser.ui) {
|
||||
return options
|
||||
}
|
||||
if (options.x != null || options.y != null) {
|
||||
const cache = {}
|
||||
if (options.x != null) {
|
||||
options.x = scaleCoordinate(options.x, cache)
|
||||
}
|
||||
if (options.y != null) {
|
||||
options.y = scaleCoordinate(options.y, cache)
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function processHoverOptions(options?: UserEventHoverOptions) {
|
||||
// only ui scales the iframe, so we need to adjust the position
|
||||
if (!options || !server.config.browser.ui) {
|
||||
return options
|
||||
}
|
||||
const cache = {}
|
||||
if (options.xOffset != null) {
|
||||
options.xOffset = scaleCoordinate(options.xOffset, cache)
|
||||
}
|
||||
if (options.yOffset != null) {
|
||||
options.yOffset = scaleCoordinate(options.yOffset, cache)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function processDragAndDropOptions(options?: UserEventDragAndDropOptions) {
|
||||
// only ui scales the iframe, so we need to adjust the position
|
||||
if (!options || !server.config.browser.ui) {
|
||||
return options
|
||||
}
|
||||
const cache = {}
|
||||
if (options.sourceX != null) {
|
||||
options.sourceX = scaleCoordinate(options.sourceX, cache)
|
||||
}
|
||||
if (options.sourceY != null) {
|
||||
options.sourceY = scaleCoordinate(options.sourceY, cache)
|
||||
}
|
||||
if (options.targetX != null) {
|
||||
options.targetX = scaleCoordinate(options.targetX, cache)
|
||||
}
|
||||
if (options.targetY != null) {
|
||||
options.targetY = scaleCoordinate(options.targetY, cache)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function scaleCoordinate(coordinate: number, cache: any) {
|
||||
return Math.round(coordinate * getCachedScale(cache))
|
||||
}
|
||||
|
||||
function getCachedScale(cache: { scale: number | undefined }) {
|
||||
return cache.scale ??= getIframeScale()
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
import type { CustomComparatorsRegistry } from '@vitest/browser'
|
||||
import type { Capabilities } from '@wdio/types'
|
||||
import type {
|
||||
ScreenshotComparatorRegistry,
|
||||
ScreenshotMatcherOptions,
|
||||
SelectorOptions,
|
||||
} from 'vitest/browser'
|
||||
import type {
|
||||
BrowserCommand,
|
||||
BrowserProvider,
|
||||
BrowserProviderOption,
|
||||
CDPSession,
|
||||
TestProject,
|
||||
} from 'vitest/node'
|
||||
import type { ClickOptions, DragAndDropOptions, MoveToOptions, remote } from 'webdriverio'
|
||||
import { defineBrowserProvider } from '@vitest/browser'
|
||||
|
||||
import { resolve } from 'pathe'
|
||||
import { createDebugger } from 'vitest/node'
|
||||
import commands from './commands'
|
||||
import { distRoot } from './constants'
|
||||
|
||||
const debug = createDebugger('vitest:browser:wdio')
|
||||
|
||||
const webdriverBrowsers = ['firefox', 'chrome', 'edge', 'safari'] as const
|
||||
type WebdriverBrowser = (typeof webdriverBrowsers)[number]
|
||||
|
||||
export interface WebdriverProviderOptions extends Partial<
|
||||
Parameters<typeof remote>[0]
|
||||
> {}
|
||||
|
||||
export function webdriverio(options: WebdriverProviderOptions = {}): BrowserProviderOption<WebdriverProviderOptions> {
|
||||
return defineBrowserProvider({
|
||||
name: 'webdriverio',
|
||||
supportedBrowser: webdriverBrowsers,
|
||||
options,
|
||||
providerFactory(project) {
|
||||
return new WebdriverBrowserProvider(project, options)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export class WebdriverBrowserProvider implements BrowserProvider {
|
||||
public name = 'webdriverio' as const
|
||||
public supportsParallelism: boolean = false
|
||||
|
||||
public browser: WebdriverIO.Browser | null = null
|
||||
|
||||
private browserName!: WebdriverBrowser
|
||||
private project!: TestProject
|
||||
|
||||
private options?: WebdriverProviderOptions
|
||||
|
||||
private closing = false
|
||||
private iframeSwitched = false
|
||||
private topLevelContext: string | undefined
|
||||
|
||||
public initScripts: string[] = [
|
||||
resolve(distRoot, 'locators.js'),
|
||||
]
|
||||
|
||||
getSupportedBrowsers(): readonly string[] {
|
||||
return webdriverBrowsers
|
||||
}
|
||||
|
||||
constructor(
|
||||
project: TestProject,
|
||||
options: WebdriverProviderOptions,
|
||||
) {
|
||||
// increase shutdown timeout because WDIO takes some extra time to kill the driver
|
||||
if (!project.vitest.state._data.timeoutIncreased) {
|
||||
project.vitest.state._data.timeoutIncreased = true
|
||||
project.vitest.config.teardownTimeout += 10_000
|
||||
}
|
||||
|
||||
this.closing = false
|
||||
this.project = project
|
||||
this.browserName = project.config.browser.name as WebdriverBrowser
|
||||
this.options = options
|
||||
|
||||
for (const [name, command] of Object.entries(commands)) {
|
||||
project.browser!.registerCommand(name as any, command as BrowserCommand)
|
||||
}
|
||||
}
|
||||
|
||||
isIframeSwitched(): boolean {
|
||||
return this.iframeSwitched
|
||||
}
|
||||
|
||||
async switchToTestFrame(): Promise<void> {
|
||||
const browser = this.browser!
|
||||
// support wdio@9
|
||||
if (browser.switchFrame) {
|
||||
await browser.switchFrame(browser.$('iframe[data-vitest]'))
|
||||
}
|
||||
else {
|
||||
const iframe = await browser.findElement(
|
||||
'css selector',
|
||||
'iframe[data-vitest]',
|
||||
)
|
||||
await browser.switchToFrame(iframe)
|
||||
}
|
||||
this.iframeSwitched = true
|
||||
}
|
||||
|
||||
async switchToMainFrame(): Promise<void> {
|
||||
const page = this.browser!
|
||||
if (page.switchFrame) {
|
||||
await page.switchFrame(null)
|
||||
}
|
||||
else {
|
||||
await page.switchToParentFrame()
|
||||
}
|
||||
this.iframeSwitched = false
|
||||
}
|
||||
|
||||
async setViewport(options: { width: number; height: number }): Promise<void> {
|
||||
if (this.topLevelContext == null || !this.browser) {
|
||||
throw new Error(`The browser has no open pages.`)
|
||||
}
|
||||
if (this.browser.isBidi) {
|
||||
await this.browser.send({
|
||||
method: 'browsingContext.setViewport',
|
||||
params: {
|
||||
context: this.topLevelContext,
|
||||
devicePixelRatio: 1,
|
||||
viewport: options,
|
||||
},
|
||||
})
|
||||
}
|
||||
else {
|
||||
await this.browser.setWindowSize(options.width, options.height)
|
||||
}
|
||||
}
|
||||
|
||||
getCommandsContext(): {
|
||||
browser: WebdriverIO.Browser | null
|
||||
} {
|
||||
return {
|
||||
browser: this.browser,
|
||||
}
|
||||
}
|
||||
|
||||
async openBrowser(): Promise<WebdriverIO.Browser> {
|
||||
await this._throwIfClosing('opening the browser')
|
||||
|
||||
if (this.browser) {
|
||||
debug?.('[%s] the browser is already opened, reusing it', this.browserName)
|
||||
return this.browser
|
||||
}
|
||||
|
||||
const options = this.project.config.browser
|
||||
|
||||
if (this.browserName === 'safari') {
|
||||
if (options.headless) {
|
||||
throw new Error(
|
||||
'You\'ve enabled headless mode for Safari but it doesn\'t currently support it.',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const { remote } = await import('webdriverio')
|
||||
|
||||
const remoteOptions: Capabilities.WebdriverIOConfig = {
|
||||
logLevel: 'silent',
|
||||
...this.options,
|
||||
capabilities: this.buildCapabilities(),
|
||||
}
|
||||
|
||||
debug?.('[%s] opening the browser with options: %O', this.browserName, remoteOptions)
|
||||
// TODO: close everything, if browser is closed from the outside
|
||||
this.browser = await remote(remoteOptions)
|
||||
await this._throwIfClosing()
|
||||
|
||||
return this.browser
|
||||
}
|
||||
|
||||
private buildCapabilities() {
|
||||
const capabilities: Capabilities.WebdriverIOConfig['capabilities'] = {
|
||||
...this.options?.capabilities,
|
||||
browserName: this.browserName,
|
||||
}
|
||||
|
||||
const headlessMap = {
|
||||
chrome: ['goog:chromeOptions', ['headless', 'disable-gpu']],
|
||||
firefox: ['moz:firefoxOptions', ['-headless']],
|
||||
edge: ['ms:edgeOptions', ['--headless']],
|
||||
} as const
|
||||
|
||||
const options = this.project.config.browser
|
||||
const browser = this.browserName
|
||||
if (browser !== 'safari' && options.headless) {
|
||||
const [key, defaultArgs] = headlessMap[browser]
|
||||
const currentValues = (this.options?.capabilities as any)?.[key] || {}
|
||||
const currentArgs: string[] = currentValues.args || []
|
||||
|
||||
const hasEnableGpu = browser === 'chrome' && (currentArgs.includes('--enable-gpu') || currentArgs.includes('enable-gpu'))
|
||||
|
||||
const argsToAdd = hasEnableGpu
|
||||
? defaultArgs.filter(arg => arg !== 'disable-gpu')
|
||||
: defaultArgs
|
||||
|
||||
const newArgs = [...currentArgs, ...argsToAdd]
|
||||
|
||||
capabilities[key] = { ...currentValues, args: newArgs as any }
|
||||
}
|
||||
|
||||
// start Vitest UI maximized only on supported browsers
|
||||
if (options.ui && (browser === 'chrome' || browser === 'edge')) {
|
||||
const key = browser === 'chrome'
|
||||
? 'goog:chromeOptions'
|
||||
: 'ms:edgeOptions'
|
||||
const args = capabilities[key]?.args || []
|
||||
if (!args.includes('--start-maximized') && !args.includes('--start-fullscreen')) {
|
||||
args.push('--start-maximized')
|
||||
}
|
||||
capabilities[key] ??= {}
|
||||
capabilities[key]!.args = args
|
||||
}
|
||||
|
||||
const inspector = this.project.vitest.config.inspector
|
||||
if (inspector.enabled && (browser === 'chrome' || browser === 'edge')) {
|
||||
const key = browser === 'chrome'
|
||||
? 'goog:chromeOptions'
|
||||
: 'ms:edgeOptions'
|
||||
const args = capabilities[key]?.args || []
|
||||
|
||||
// NodeJS equivalent defaults: https://nodejs.org/en/learn/getting-started/debugging#enable-inspector
|
||||
const port = inspector.port || 9229
|
||||
const host = inspector.host || '127.0.0.1'
|
||||
|
||||
args.push(`--remote-debugging-port=${port}`)
|
||||
|
||||
if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1') {
|
||||
this.project.vitest.logger.warn(`Custom inspector host "${host}" will be ignored. Chrome only allows remote debugging on localhost.`)
|
||||
}
|
||||
this.project.vitest.logger.log(`Debugger listening on ws://127.0.0.1:${port}`)
|
||||
|
||||
capabilities[key] ??= {}
|
||||
capabilities[key]!.args = args
|
||||
}
|
||||
|
||||
return capabilities
|
||||
}
|
||||
|
||||
async openPage(sessionId: string, url: string): Promise<void> {
|
||||
await this._throwIfClosing('creating the browser')
|
||||
debug?.('[%s][%s] creating the browser page for %s', sessionId, this.browserName, url)
|
||||
const browserInstance = await this.openBrowser()
|
||||
debug?.('[%s][%s] browser page is created, opening %s', sessionId, this.browserName, url)
|
||||
await browserInstance.url(url)
|
||||
this.topLevelContext = await browserInstance.getWindowHandle()
|
||||
await this._throwIfClosing('opening the url')
|
||||
}
|
||||
|
||||
private async _throwIfClosing(action?: string) {
|
||||
if (this.closing) {
|
||||
debug?.(`[%s] provider was closed, cannot perform the action${action ? ` ${action}` : ''}`, this.browserName)
|
||||
await (this.browser?.sessionId ? this.browser?.deleteSession?.() : null)
|
||||
throw new Error(`[vitest] The provider was closed.`)
|
||||
}
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
debug?.('[%s] closing provider', this.browserName)
|
||||
this.closing = true
|
||||
const browser = this.browser
|
||||
const sessionId = browser?.sessionId
|
||||
if (!browser || !sessionId) {
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/webdriverio/webdriverio/blob/ab1a2e82b13a9c7d0e275ae87e7357e1b047d8d3/packages/wdio-runner/src/index.ts#L486
|
||||
await browser.deleteSession()
|
||||
browser.sessionId = undefined as unknown as string
|
||||
this.browser = null
|
||||
}
|
||||
|
||||
async getCDPSession(_sessionId: string): Promise<CDPSession> {
|
||||
return {
|
||||
send: (method, params) => {
|
||||
if (!this.browser) {
|
||||
throw new Error(`The environment was torn down.`)
|
||||
}
|
||||
return this.browser.sendCommandAndGetResult(method, params ?? {}).catch((error) => {
|
||||
return Promise.reject(new Error(`Failed to execute "${method}" command.`, { cause: error }))
|
||||
})
|
||||
},
|
||||
on: () => {
|
||||
throw new Error(`webdriverio provider doesn't support cdp.on()`)
|
||||
},
|
||||
once: () => {
|
||||
throw new Error(`webdriverio provider doesn't support cdp.once()`)
|
||||
},
|
||||
off: () => {
|
||||
throw new Error(`webdriverio provider doesn't support cdp.off()`)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'vitest/browser' {
|
||||
export interface UserEventClickOptions extends Partial<ClickOptions>, SelectorOptions {}
|
||||
export interface UserEventHoverOptions extends MoveToOptions, SelectorOptions {}
|
||||
export interface UserEventDragAndDropOptions extends DragAndDropOptions {
|
||||
sourceX?: number
|
||||
sourceY?: number
|
||||
targetX?: number
|
||||
targetY?: number
|
||||
}
|
||||
export interface UserEventFillOptions extends SelectorOptions {}
|
||||
export interface UserEventSelectOptions extends SelectorOptions {}
|
||||
export interface UserEventClearOptions extends SelectorOptions {}
|
||||
export interface UserEventDoubleClickOptions extends SelectorOptions {}
|
||||
export interface UserEventTripleClickOptions extends SelectorOptions {}
|
||||
export interface UserEventWheelBaseOptions extends SelectorOptions {}
|
||||
export interface LocatorScreenshotOptions extends SelectorOptions {}
|
||||
}
|
||||
|
||||
interface WebdriverCDPSession {
|
||||
send: (method: string, params?: Record<string, unknown>) => Promise<unknown>
|
||||
on: (event: string, listener: (...args: unknown[]) => void) => void
|
||||
once: (event: string, listener: (...args: unknown[]) => void) => void
|
||||
off: (event: string, listener: (...args: unknown[]) => void) => void
|
||||
}
|
||||
|
||||
declare module 'vitest/node' {
|
||||
export interface BrowserCommandContext {
|
||||
browser: WebdriverIO.Browser
|
||||
}
|
||||
|
||||
export interface _BrowserNames {
|
||||
webdriverio: WebdriverBrowser
|
||||
}
|
||||
|
||||
export interface ToMatchScreenshotOptions
|
||||
extends Omit<
|
||||
ScreenshotMatcherOptions,
|
||||
'comparatorName' | 'comparatorOptions'
|
||||
>, CustomComparatorsRegistry {}
|
||||
|
||||
export interface ToMatchScreenshotComparators
|
||||
extends ScreenshotComparatorRegistry {}
|
||||
|
||||
export interface CDPSession extends WebdriverCDPSession {}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "vite/client"],
|
||||
"isolatedDeclarations": true
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"**/vite.config.ts",
|
||||
"src/client/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -73,7 +73,6 @@
|
||||
"@vitejs/plugin-vue": "catalog:",
|
||||
"@vitest/browser-playwright": "workspace:*",
|
||||
"@vitest/browser-preview": "workspace:*",
|
||||
"@vitest/browser-webdriverio": "workspace:*",
|
||||
"@vueuse/core": "catalog:",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"birpc": "catalog:",
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { playwright } from '@vitest/browser-playwright'
|
||||
import { preview } from '@vitest/browser-preview'
|
||||
import { webdriverio } from '@vitest/browser-webdriverio'
|
||||
import { mergeConfig } from 'vite'
|
||||
import { configDefaults, defineConfig } from 'vitest/config'
|
||||
import viteConfig from './vite.config'
|
||||
|
||||
// known working set of tests for providers
|
||||
// pnpm -C packages/ui test:ui
|
||||
// PROVIDER=webdriverio pnpm -C packages/ui test:ui SmallTab
|
||||
// PROVIDER=preview pnpm -C packages/ui test:ui SmallTab -t access
|
||||
|
||||
const providerName = (process.env.PROVIDER || 'playwright') as
|
||||
| 'playwright'
|
||||
| 'webdriverio'
|
||||
| 'preview'
|
||||
|
||||
const testConfig = defineConfig({
|
||||
@@ -42,25 +39,10 @@ const testConfig = defineConfig({
|
||||
provider:
|
||||
providerName === 'preview'
|
||||
? preview()
|
||||
: providerName === 'webdriverio'
|
||||
? webdriverio({
|
||||
...(process.env.CHROMEDRIVER_PATH && process.env.CHROME_BIN
|
||||
? {
|
||||
'wdio:chromedriverOptions': {
|
||||
binary: process.env.CHROMEDRIVER_PATH,
|
||||
},
|
||||
'capabilities': {
|
||||
'goog:chromeOptions': {
|
||||
binary: process.env.CHROME_BIN,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
: playwright({
|
||||
actionTimeout: 5000,
|
||||
}),
|
||||
instances: [providerName === 'webdriverio' ? { browser: 'chrome' } : { browser: 'chromium' }],
|
||||
: playwright({
|
||||
actionTimeout: 5000,
|
||||
}),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
"@types/node": "^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "workspace:*",
|
||||
"@vitest/browser-preview": "workspace:*",
|
||||
"@vitest/browser-webdriverio": "workspace:*",
|
||||
"@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0",
|
||||
"@vitest/coverage-istanbul": "workspace:*",
|
||||
"@vitest/coverage-v8": "workspace:*",
|
||||
"@vitest/ui": "workspace:*",
|
||||
|
||||
Generated
+447
-390
File diff suppressed because it is too large
Load Diff
+7
-8
@@ -8,14 +8,14 @@ trustPolicy: no-downgrade
|
||||
trustPolicyExclude:
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
- [email protected]0.0
|
||||
- [email protected]0.0
|
||||
- '@wdio/[email protected]0.0'
|
||||
- [email protected]9.1
|
||||
- [email protected]9.1
|
||||
- '@wdio/[email protected]9.1'
|
||||
- '@wdio/[email protected]'
|
||||
- '@wdio/[email protected]0.0'
|
||||
- '@wdio/[email protected]0.0'
|
||||
- '@wdio/logger@9.18.0'
|
||||
- '@wdio/protocols@9.16.2'
|
||||
- '@wdio/[email protected]9.1'
|
||||
- '@wdio/[email protected]9.1'
|
||||
- '@wdio/logger@9.29.1'
|
||||
- '@wdio/protocols@9.29.1'
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
- [email protected]
|
||||
@@ -31,7 +31,6 @@ overrides:
|
||||
'@vitest/browser': workspace:*
|
||||
'@vitest/browser-playwright': workspace:*
|
||||
'@vitest/browser-preview': workspace:*
|
||||
'@vitest/browser-webdriverio': workspace:*
|
||||
'@vitest/ui': workspace:*
|
||||
acorn: 8.11.3
|
||||
mlly: ^1.8.0
|
||||
|
||||
@@ -14,7 +14,7 @@ TEST_BROWSER=chromium pnpm run test:playwright
|
||||
pnpm run test-fixtures --root ./fixtures/locators
|
||||
|
||||
# run specific fixture with selected provider and browser
|
||||
PROVIDER=webdriverio TEST_BROWSER=firefox pnpm run test-fixtures --root ./fixtures/locators
|
||||
PROVIDER=playwright TEST_BROWSER=firefox pnpm run test-fixtures --root ./fixtures/locators
|
||||
```
|
||||
|
||||
## Using docker playwright
|
||||
|
||||
@@ -16,17 +16,6 @@ const forceCrash: BrowserCommand<[]> = async (context) => {
|
||||
|
||||
throw new Error(`Browser crash not supported for ${browser}`)
|
||||
}
|
||||
if (context.provider.name === 'webdriverio') {
|
||||
const browser = context.browser
|
||||
const name = context.project.config.browser.name
|
||||
if (name === 'chrome') {
|
||||
await browser.url('chrome://crash')
|
||||
}
|
||||
if (name === 'firefox') {
|
||||
await browser.url('about:crashcontent')
|
||||
}
|
||||
throw new Error(`Browser crash not supported for ${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
|
||||
@@ -9,7 +9,7 @@ export default defineConfig({
|
||||
enabled: true,
|
||||
provider,
|
||||
instances: [
|
||||
{ browser: provider.name === 'webdriverio' ? "chrome" : "chromium" },
|
||||
{ browser: "chromium" },
|
||||
],
|
||||
headless: true,
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ export default defineConfig({
|
||||
enabled: true,
|
||||
headless: true,
|
||||
instances: [
|
||||
{ browser: provider.name === 'webdriverio' ? "chrome" : "chromium" },
|
||||
{ browser: "chromium" },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -9,11 +9,9 @@ import { instances, providers, provider } from '../../settings'
|
||||
|
||||
// ignore https errors due to self-signed certificate from plugin-basic-ssl
|
||||
// https://playwright.dev/docs/api/class-browser#browser-new-context-option-ignore-https-errors
|
||||
// https://webdriver.io/docs/configuration/#strictssl and acceptInsecureCerts in https://webdriver.io/docs/api/browser/#properties
|
||||
const configuredProvider = (function () {
|
||||
switch (provider.name) {
|
||||
case 'playwright': return providers.playwright()
|
||||
case 'webdriverio': return providers.webdriverio({ strictSSL: false, capabilities: { acceptInsecureCerts: true } })
|
||||
default: {
|
||||
throw new Error(`Invalid provider: ${provider.name}`)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ test('shadow dom is rebuilt and highlightable by mirror id', async () => {
|
||||
const shadow = host.attachShadow({ mode: 'open' })
|
||||
shadow.innerHTML = '<button>Shadow button</button>'
|
||||
document.body.append(host)
|
||||
// TODO: webdriverio currently records this trace-view selector after it is
|
||||
// lowered to a provider-specific shadow DOM selector.
|
||||
await page.getByRole('button', { name: 'Shadow button' }).mark('shadow button rendered')
|
||||
})
|
||||
|
||||
@@ -42,8 +40,6 @@ test('custom element dom shape is rebuilt without runtime behavior', async () =>
|
||||
}
|
||||
|
||||
document.body.innerHTML = '<trace-widget></trace-widget>'
|
||||
// TODO: webdriverio currently records this trace-view selector after it is
|
||||
// lowered to a provider-specific shadow DOM selector.
|
||||
await page.getByRole('button', { name: 'Custom element button' }).mark('custom element rendered')
|
||||
})
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@ test('click', async () => {
|
||||
test('click fail', async () => {
|
||||
document.body.innerHTML = '<button>Hello</button>'
|
||||
try {
|
||||
// TODO: webdriverio currently records only the lifecycle failure for this case,
|
||||
// not the failed action trace entry with selectorResolution: "missing".
|
||||
await page.getByRole('button', { name: 'Missing' }).click({ timeout: 100 })
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { expect, test } from 'vitest';
|
||||
import { page, userEvent } from 'vitest/browser';
|
||||
|
||||
test('clipboard', async () => {
|
||||
// make it smaller since webdriverio fails when scaled
|
||||
page.viewport(300, 300)
|
||||
|
||||
document.body.innerHTML = `
|
||||
|
||||
@@ -15,7 +15,6 @@ test('non US keys', async () => {
|
||||
await expect.element(page.getByPlaceholder("fill-#7396")).toHaveValue('éèù')
|
||||
|
||||
// playwright: garbled characters
|
||||
// webdriverio: error: invalid argument: missing command parameters
|
||||
// preview: ok
|
||||
if (server.provider === 'playwright') {
|
||||
await userEvent.type(page.getByPlaceholder("type-emoji"), '😊😍')
|
||||
@@ -24,31 +23,15 @@ test('non US keys', async () => {
|
||||
} else {
|
||||
await expect.element(page.getByPlaceholder("type-emoji")).toHaveValue('😊😍')
|
||||
}
|
||||
} else if (server.provider === 'webdriverio') {
|
||||
await expect(() =>
|
||||
userEvent.type(page.getByPlaceholder("type-emoji"), '😊😍')
|
||||
).rejects.toThrow()
|
||||
} else {
|
||||
await userEvent.type(page.getByPlaceholder("type-emoji"), '😊😍')
|
||||
await expect.element(page.getByPlaceholder("type-emoji")).toHaveValue('😊😍')
|
||||
}
|
||||
|
||||
// playwright: ok
|
||||
// webdriverio: error: ChromeDriver only supports characters in the BMP
|
||||
// preview: ok
|
||||
if (server.provider === 'webdriverio') {
|
||||
if (server.browser === 'firefox') {
|
||||
await userEvent.fill(page.getByPlaceholder("fill-emoji"), '😊😍')
|
||||
await expect.element(page.getByPlaceholder("fill-emoji")).toHaveValue('😊😍')
|
||||
} else {
|
||||
await expect(() =>
|
||||
userEvent.fill(page.getByPlaceholder("fill-emoji"), '😊😍')
|
||||
).rejects.toThrow()
|
||||
}
|
||||
} else {
|
||||
await userEvent.fill(page.getByPlaceholder("fill-emoji"), '😊😍')
|
||||
await expect.element(page.getByPlaceholder("fill-emoji")).toHaveValue('😊😍')
|
||||
}
|
||||
await userEvent.fill(page.getByPlaceholder("fill-emoji"), '😊😍')
|
||||
await expect.element(page.getByPlaceholder("fill-emoji")).toHaveValue('😊😍')
|
||||
})
|
||||
|
||||
test('click with modifier', async () => {
|
||||
@@ -63,7 +46,6 @@ test('click with modifier', async () => {
|
||||
});
|
||||
|
||||
await userEvent.keyboard('{Shift>}')
|
||||
// By using an empty object as the option, this opts in to using a chain of actions instead of an elementClick in webdriver.
|
||||
await userEvent.click(el, {})
|
||||
await userEvent.keyboard('{/Shift}')
|
||||
await expect.poll(() => el.textContent).toContain("[ok]")
|
||||
@@ -73,7 +55,6 @@ test('click with modifier', async () => {
|
||||
// https://testing-library.com/docs/user-event/keyboard
|
||||
// https://github.com/testing-library/user-event/blob/main/src/keyboard/keyMap.ts
|
||||
// https://playwright.dev/docs/api/class-keyboard
|
||||
// https://webdriver.io/docs/api/browser/keys/
|
||||
test('special keys', async () => {
|
||||
async function testKeyboard(text: string) {
|
||||
let data: any;
|
||||
@@ -99,14 +80,6 @@ test('special keys', async () => {
|
||||
expect(await testKeyboard('[ShiftLeft]')).toMatchInlineSnapshot(`"Shift|ShiftLeft|1"`);
|
||||
expect(await testKeyboard('[ShiftRight]')).toMatchInlineSnapshot(`"Shift|ShiftLeft|1"`);
|
||||
}
|
||||
if (server.provider === 'webdriverio') {
|
||||
expect(await testKeyboard('{Shift}')).toMatchInlineSnapshot(`"Shift|ShiftLeft|1"`);
|
||||
expect(await testKeyboard('{ShiftLeft}')).toMatchInlineSnapshot(`"ERROR"`);
|
||||
expect(await testKeyboard('{ShiftRight}')).toMatchInlineSnapshot(`"ERROR"`);
|
||||
expect(await testKeyboard('[Shift]')).toMatchInlineSnapshot(`"ERROR"`);
|
||||
expect(await testKeyboard('[ShiftLeft]')).toMatchInlineSnapshot(`"Shift|ShiftLeft|1"`);
|
||||
expect(await testKeyboard('[ShiftRight]')).toMatchInlineSnapshot(`"Shift|ShiftLeft|1"`);
|
||||
}
|
||||
if (server.provider === 'preview') {
|
||||
expect(await testKeyboard('{Shift}')).toMatchInlineSnapshot(`"Shift|ShiftLeft|0"`);
|
||||
expect(await testKeyboard('{ShiftLeft}')).toMatchInlineSnapshot(`"ShiftLeft|Unknown|0"`);
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "pnpm run test:webdriverio && pnpm run test:playwright",
|
||||
"test": "pnpm run test:playwright",
|
||||
"test:unit": "vitest --no-watch --config=vitest.config.unit.mts",
|
||||
"test:webdriverio": "PROVIDER=webdriverio pnpm run test:unit",
|
||||
"test:playwright": "PROVIDER=playwright pnpm run test:unit",
|
||||
"test:safaridriver": "PROVIDER=webdriverio BROWSER=safari pnpm run test:unit",
|
||||
"test-fixtures": "vitest",
|
||||
"bench-fixtures": "vitest bench",
|
||||
"test-expect-dom": "vitest --root ./fixtures/expect-dom",
|
||||
@@ -27,7 +25,6 @@
|
||||
"coverage": "vitest --coverage.enabled --coverage.provider=istanbul --browser.headless=yes",
|
||||
"test:browser:preview": "PROVIDER=preview vitest",
|
||||
"test:browser:playwright": "PROVIDER=playwright vitest",
|
||||
"test:browser:webdriverio": "PROVIDER=webdriverio vitest",
|
||||
"test:browser:playwright:html": "PROVIDER=playwright vitest --reporter=html",
|
||||
"docker": "docker compose"
|
||||
},
|
||||
@@ -38,7 +35,6 @@
|
||||
"@vitest/browser": "workspace:*",
|
||||
"@vitest/browser-playwright": "workspace:*",
|
||||
"@vitest/browser-preview": "workspace:*",
|
||||
"@vitest/browser-webdriverio": "workspace:*",
|
||||
"@vitest/bundled-lib": "link:./bundled-lib",
|
||||
"@vitest/cjs-lib": "link:./cjs-lib",
|
||||
"playwright": "catalog:",
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { BrowserInstanceOption } from 'vitest/node'
|
||||
import { playwright } from '@vitest/browser-playwright'
|
||||
import { preview } from '@vitest/browser-preview'
|
||||
import { webdriverio } from '@vitest/browser-webdriverio'
|
||||
|
||||
const providerName = (process.env.PROVIDER || 'playwright') as 'playwright' | 'webdriverio' | 'preview'
|
||||
const providerName = (process.env.PROVIDER || 'playwright') as 'playwright' | 'preview'
|
||||
|
||||
const wsEndpoint = process.env.BROWSER_WS_ENDPOINT === 'true' ? 'ws://127.0.0.1:6677/' : process.env.BROWSER_WS_ENDPOINT
|
||||
|
||||
@@ -18,41 +17,6 @@ export const providers = {
|
||||
}
|
||||
: options),
|
||||
preview,
|
||||
webdriverio: (options?: Parameters<typeof webdriverio>[0]) => {
|
||||
const capabilities = options?.capabilities || {}
|
||||
|
||||
return webdriverio({
|
||||
...options,
|
||||
capabilities: {
|
||||
...capabilities,
|
||||
// Explicit browser/driver binaries keep WebDriverIO from auto-downloading mismatched versions.
|
||||
// https://webdriver.io/docs/driverbinaries
|
||||
// https://webdriver.io/docs/capabilities#webdriverio-capabilities-to-manage-browser-driver-options
|
||||
...(process.env.CHROMEDRIVER_PATH && process.env.CHROME_BIN
|
||||
? {
|
||||
'wdio:chromedriverOptions': {
|
||||
...(capabilities as any)['wdio:chromedriverOptions'],
|
||||
binary: process.env.CHROMEDRIVER_PATH,
|
||||
},
|
||||
// https://developer.chrome.com/docs/chromedriver/capabilities#chromeoptions-object
|
||||
'goog:chromeOptions': {
|
||||
...(capabilities as any)['goog:chromeOptions'],
|
||||
binary: process.env.CHROME_BIN,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(process.env.FIREFOX_BIN
|
||||
? {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/WebDriver/Reference/Capabilities/firefoxOptions
|
||||
'moz:firefoxOptions': {
|
||||
...(capabilities as any)['moz:firefoxOptions'],
|
||||
binary: process.env.FIREFOX_BIN,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export const provider = providers[providerName]()
|
||||
@@ -65,7 +29,7 @@ const playwrightInstances: BrowserInstanceOption[] = [
|
||||
...(process.env.BROWSER_NO_WEBKIT ? [] : [{ browser: 'webkit' as const }]),
|
||||
]
|
||||
|
||||
const webdriverioInstances: BrowserInstanceOption[] = [
|
||||
const previewInstances: BrowserInstanceOption[] = [
|
||||
{ browser: 'chrome' },
|
||||
{ browser: 'firefox' },
|
||||
]
|
||||
@@ -85,4 +49,4 @@ export const instances: BrowserInstanceOption[] = testBrowser
|
||||
]
|
||||
: provider.name === 'playwright'
|
||||
? playwrightInstances
|
||||
: webdriverioInstances
|
||||
: previewInstances
|
||||
|
||||
@@ -12,14 +12,3 @@ test.runIf(provider.name === 'playwright')('[playwright] runs multiple different
|
||||
expect(stdout).toContain('[chromium] HTML_INJECTED_VAR is true')
|
||||
expect(stdout).toContain('[firefox] HTML_INJECTED_VAR is undefined')
|
||||
})
|
||||
|
||||
test.runIf(provider.name === 'webdriverio')('[webdriverio] runs multiple different configurations correctly', async () => {
|
||||
const { stdout, exitCode, stderr } = await runBrowserTests({
|
||||
root: './fixtures/multiple-different-configs',
|
||||
})
|
||||
|
||||
expect(stderr).toBe('')
|
||||
expect(exitCode).toBe(0)
|
||||
expect(stdout).toContain('[chromium] HTML_INJECTED_VAR is true')
|
||||
expect(stdout).toContain('[firefox] HTML_INJECTED_VAR is undefined')
|
||||
})
|
||||
|
||||
@@ -346,9 +346,7 @@ test('user-event', async () => {
|
||||
const { stdout, stderr } = await runBrowserTests({
|
||||
root: './fixtures/user-event',
|
||||
})
|
||||
if (provider.name !== 'webdriverio') {
|
||||
expect(stderr).toBe('')
|
||||
}
|
||||
expect(stderr).toBe('')
|
||||
onTestFailed(() => console.error(stderr))
|
||||
instances.forEach(({ browser }) => {
|
||||
expect(stdout).toReportPassedTest('cleanup-retry.test.ts', browser)
|
||||
@@ -368,9 +366,6 @@ test('timeout settings', async () => {
|
||||
expect(stderr).toContain('locator.click: Timeout 500ms exceeded.')
|
||||
expect(stderr).toContain('locator.click: Timeout 345ms exceeded.')
|
||||
}
|
||||
if (provider.name === 'webdriverio') {
|
||||
expect(stderr).toContain('Cannot find element with locator')
|
||||
}
|
||||
})
|
||||
|
||||
test('viewport', async () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import { createNode } from '#src/createNode'
|
||||
import { afterAll, beforeEach, describe, expect, test } from 'vitest'
|
||||
import { page, server } from 'vitest/browser'
|
||||
import { page } from 'vitest/browser'
|
||||
import '../src/button.css'
|
||||
|
||||
afterAll(() => {
|
||||
@@ -115,25 +115,6 @@ describe('dom related activity', () => {
|
||||
/__screenshots__\/dom.test.ts\/dom-related-activity-svg-screenshot-1.png/,
|
||||
)
|
||||
})
|
||||
|
||||
test.runIf(server.provider === 'webdriverio')('shadow dom works with multiple elements', async () => {
|
||||
const wrapper = createWrapper()
|
||||
const div = createNode()
|
||||
wrapper.appendChild(div)
|
||||
|
||||
const shadow = div.attachShadow({ mode: 'open' })
|
||||
const shadowDiv1 = createNode()
|
||||
shadowDiv1.role = 'tab'
|
||||
const shadowDiv2 = createNode()
|
||||
shadowDiv2.role = 'tab'
|
||||
shadow.appendChild(shadowDiv1)
|
||||
shadow.appendChild(shadowDiv2)
|
||||
|
||||
expect(
|
||||
page.getByRole('tab').selector,
|
||||
'there is only a single >>> in the selector',
|
||||
).toBe('>>>html > body > div > div > div, html > body > div > div > div')
|
||||
})
|
||||
})
|
||||
|
||||
function createWrapper() {
|
||||
|
||||
@@ -767,7 +767,6 @@ describe('userEvent.keyboard', async () => {
|
||||
])
|
||||
})
|
||||
|
||||
// looks like wdio doesn't support releasing Enter on its own
|
||||
test('should not auto release', async () => {
|
||||
const spyKeydown = vi.fn()
|
||||
const spyKeyup = vi.fn()
|
||||
@@ -928,8 +927,7 @@ describe.each([
|
||||
expect(select.value).toBe('2')
|
||||
})
|
||||
|
||||
// webdriverio doesn't support selecting multiple values
|
||||
describe.skipIf(server.provider === 'webdriverio')('multiple values', () => {
|
||||
describe('multiple values', () => {
|
||||
test('can select multiple values', async () => {
|
||||
const { select, options } = createSelect()
|
||||
select.multiple = true
|
||||
@@ -972,7 +970,7 @@ describe.each([
|
||||
})
|
||||
|
||||
describe('uploading files', async () => {
|
||||
test.skipIf(server.provider === 'webdriverio')('can upload an instance of File', async () => {
|
||||
test('can upload an instance of File', async () => {
|
||||
const file = new File(['hello'], 'hello.png', { type: 'image/png' })
|
||||
const input = document.createElement('input')
|
||||
input.id = 'file'
|
||||
@@ -987,7 +985,7 @@ describe('uploading files', async () => {
|
||||
expect(await uploadedFile.text()).toBe('hello')
|
||||
})
|
||||
|
||||
test.skipIf(server.provider === 'webdriverio')('can upload several instances of File', async () => {
|
||||
test('can upload several instances of File', async () => {
|
||||
const file1 = new File(['hello1'], 'hello1.png', { type: 'image/png' })
|
||||
const file2 = new File(['hello2'], 'hello2.png', { type: 'image/png' })
|
||||
const input = document.createElement('input')
|
||||
@@ -1007,9 +1005,7 @@ describe('uploading files', async () => {
|
||||
expect(uploadedFile2.type).toBe('image/png')
|
||||
})
|
||||
|
||||
test.skipIf(
|
||||
server.provider === 'webdriverio' && server.browser === 'firefox',
|
||||
)('can upload a file by filepath relative to test file', async () => {
|
||||
test('can upload a file by filepath relative to test file', async () => {
|
||||
const input = document.createElement('input')
|
||||
input.id = 'file'
|
||||
input.type = 'file'
|
||||
@@ -1022,9 +1018,7 @@ describe('uploading files', async () => {
|
||||
expect(uploadedFile.type).toBe('text/css')
|
||||
})
|
||||
|
||||
test.skipIf(
|
||||
server.provider === 'webdriverio' && server.browser === 'firefox',
|
||||
)('can upload several files by filepath relative to test file', async () => {
|
||||
test('can upload several files by filepath relative to test file', async () => {
|
||||
const input = document.createElement('input')
|
||||
input.id = 'file'
|
||||
input.type = 'file'
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"@vitejs/test-dep-virtual": "file:./deps/test-dep-virtual",
|
||||
"@vitest/browser-playwright": "workspace:*",
|
||||
"@vitest/browser-preview": "workspace:*",
|
||||
"@vitest/browser-webdriverio": "workspace:*",
|
||||
"@vitest/mocker": "workspace:*",
|
||||
"@vitest/test-dep-optimizer-external": "file:./deps/optimizer/external",
|
||||
"@vitest/test-dep-optimizer-optimized": "file:./deps/optimizer/optimized",
|
||||
|
||||
@@ -467,21 +467,6 @@ test('core provider has options if `provider` is playwright', async () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('core provider has options if `provider` is wdio', async () => {
|
||||
const v = await vitest({}, {
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright({ contextOptions: { hasTouch: true } }),
|
||||
instances: [
|
||||
{ browser: 'chrome' },
|
||||
],
|
||||
},
|
||||
})
|
||||
expect(v.config.browser.provider?.options).toEqual({
|
||||
contextOptions: { hasTouch: true },
|
||||
})
|
||||
})
|
||||
|
||||
test('core provider has options if `provider` is preview', async () => {
|
||||
const v = await vitest({}, {
|
||||
browser: {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { RunVitestConfig, VitestRunnerCLIOptions } from '../../test-utils'
|
||||
import { playwright } from '@vitest/browser-playwright'
|
||||
import { preview } from '@vitest/browser-preview'
|
||||
import { webdriverio } from '@vitest/browser-webdriverio'
|
||||
import { normalize, resolve } from 'pathe'
|
||||
import { beforeEach, expect, test } from 'vitest'
|
||||
import { version } from 'vitest/package.json'
|
||||
@@ -9,10 +8,9 @@ import * as testUtils from '../../test-utils'
|
||||
|
||||
const providers = [
|
||||
playwright(),
|
||||
webdriverio(),
|
||||
preview(),
|
||||
] as const
|
||||
const names = ['edge', 'chromium', 'webkit', 'chrome', 'firefox', 'safari'] as const
|
||||
const names = ['chromium', 'webkit', 'firefox'] as const
|
||||
const browsers = providers.map(provider => names.map(name => ({ name, provider }))).flat()
|
||||
|
||||
function runVitest(config: RunVitestConfig, runnerOptions?: VitestRunnerCLIOptions) {
|
||||
@@ -109,9 +107,6 @@ test('inspect and --inspect-brk cannot be used when not playwright + chromium',
|
||||
if (provider.name === 'playwright' && name === 'chromium') {
|
||||
continue
|
||||
}
|
||||
if (provider.name === 'webdriverio' && (name === 'chrome' || name === 'edge')) {
|
||||
continue
|
||||
}
|
||||
|
||||
const { stderr } = await runVitest({
|
||||
[option]: true,
|
||||
@@ -137,9 +132,9 @@ test('inspect and --inspect-brk cannot be used when not playwright + chromium',
|
||||
Use either:
|
||||
{
|
||||
browser: {
|
||||
provider: ${provider.name === 'preview' ? 'playwright' : provider.name}(),
|
||||
provider: playwright(),
|
||||
instances: [
|
||||
{ browser: '${(provider.name === 'preview' || provider.name === 'playwright') ? 'chromium' : 'chrome'}' }
|
||||
{ browser: 'chromium' }
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -156,9 +151,6 @@ test.each(
|
||||
if (provider.name === 'playwright') {
|
||||
return name !== 'chromium'
|
||||
}
|
||||
if (provider.name === 'webdriverio') {
|
||||
return name !== 'chrome' && name !== 'edge'
|
||||
}
|
||||
return true
|
||||
}),
|
||||
)('v8 coverage provider throws when $provider.name + $name', async ({ provider, name }) => {
|
||||
@@ -187,9 +179,9 @@ test.each(
|
||||
Use either:
|
||||
{
|
||||
browser: {
|
||||
provider: ${provider.name === 'preview' ? 'playwright' : provider.name}(),
|
||||
provider: playwright(),
|
||||
instances: [
|
||||
{ browser: '${(provider.name === 'preview' || provider.name === 'playwright') ? 'chromium' : 'chrome'}' }
|
||||
{ browser: 'chromium' }
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -262,7 +254,7 @@ test('v8 coverage provider cannot be used in workspace without chromium', async
|
||||
name: 'Browser project',
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: webdriverio(),
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'webkit' }],
|
||||
},
|
||||
},
|
||||
@@ -273,7 +265,7 @@ test('v8 coverage provider cannot be used in workspace without chromium', async
|
||||
`Error: @vitest/coverage-v8 does not work with
|
||||
{
|
||||
browser: {
|
||||
provider: webdriverio(),
|
||||
provider: playwright(),
|
||||
instances: [
|
||||
{ browser: 'webkit' }
|
||||
],
|
||||
|
||||
+17
-101
@@ -1,16 +1,10 @@
|
||||
import type { RunVitestConfig } from '../../test-utils'
|
||||
import { setDefaultResultOrder } from 'node:dns'
|
||||
import path from 'node:path'
|
||||
import { playwright } from '@vitest/browser-playwright'
|
||||
import { webdriverio } from '@vitest/browser-webdriverio'
|
||||
import { afterAll, expect, test } from 'vitest'
|
||||
import { expect, test } from 'vitest'
|
||||
import { rolldownVersion } from 'vitest/node'
|
||||
import { runInlineTests, runVitest } from '../../test-utils'
|
||||
|
||||
// webdriver@9 sets dns.setDefaultResultOrder("ipv4first") on import,
|
||||
// which makes Vite resolve localhost to 127.0.0.1 and breaks other tests asserting "localhost"
|
||||
afterAll(() => setDefaultResultOrder('verbatim'))
|
||||
|
||||
test('setting resetMocks works if restoreMocks is also set', async () => {
|
||||
const { stderr, testTree } = await runInlineTests({
|
||||
'vitest.config.js': {
|
||||
@@ -146,15 +140,6 @@ test('redirect mock works without loading broken original', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
function replaceRoot(tree: any, root: string): any {
|
||||
for (const child of Object.values(tree) as any[]) {
|
||||
if (child?.__module_errors__) {
|
||||
child.__module_errors__ = child.__module_errors__.map((e: string) => e.replace(root, '<root>'))
|
||||
}
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
function modeToConfig(mode: string): RunVitestConfig {
|
||||
if (mode === 'playwright') {
|
||||
return {
|
||||
@@ -166,34 +151,11 @@ function modeToConfig(mode: string): RunVitestConfig {
|
||||
},
|
||||
}
|
||||
}
|
||||
if (mode === 'webdriverio') {
|
||||
return {
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: webdriverio({
|
||||
...(process.env.CHROMEDRIVER_PATH && process.env.CHROME_BIN
|
||||
? {
|
||||
'wdio:chromedriverOptions': {
|
||||
binary: process.env.CHROMEDRIVER_PATH,
|
||||
},
|
||||
'capabilities': {
|
||||
'goog:chromeOptions': {
|
||||
binary: process.env.CHROME_BIN,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
instances: [{ browser: 'chrome' }],
|
||||
headless: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
test.for(['node', 'playwright', 'webdriverio'])('importOriginal for virtual modules (%s)', async (mode) => {
|
||||
const { stderr, errorTree, root } = await runInlineTests({
|
||||
test.for(['node', 'playwright'])('importOriginal for virtual modules (%s)', async (mode) => {
|
||||
const { stderr, errorTree } = await runInlineTests({
|
||||
'vitest.config.js': `
|
||||
import { defineConfig } from 'vitest/config'
|
||||
export default defineConfig({
|
||||
@@ -227,36 +189,17 @@ test('importOriginal returns original virtual module exports', () => {
|
||||
`,
|
||||
}, modeToConfig(mode))
|
||||
|
||||
// webdriverio uses a server-side interceptor plugin whose load hook
|
||||
// intercepts the clean id, so importActual returns the mock instead
|
||||
// of the original module. This is a known limitation.
|
||||
if (mode === 'webdriverio') {
|
||||
expect(replaceRoot(errorTree(), root)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"__unhandled_errors__": [
|
||||
"[vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. Read more: https://vitest.dev/api/vi.html#vi-mock",
|
||||
],
|
||||
"basic.test.js": {
|
||||
"__module_errors__": [
|
||||
"Failed to import test file <root>/basic.test.js",
|
||||
],
|
||||
},
|
||||
}
|
||||
`)
|
||||
}
|
||||
else {
|
||||
expect(stderr).toBe('')
|
||||
expect(errorTree()).toMatchInlineSnapshot(`
|
||||
{
|
||||
"basic.test.js": {
|
||||
"importOriginal returns original virtual module exports": "passed",
|
||||
},
|
||||
}
|
||||
`)
|
||||
}
|
||||
expect(stderr).toBe('')
|
||||
expect(errorTree()).toMatchInlineSnapshot(`
|
||||
{
|
||||
"basic.test.js": {
|
||||
"importOriginal returns original virtual module exports": "passed",
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test.for(['node', 'playwright', 'webdriverio'])('mocking virtual module without importOriginal skips loading original (%s)', async (mode) => {
|
||||
test.for(['node', 'playwright'])('mocking virtual module without importOriginal skips loading original (%s)', async (mode) => {
|
||||
const { stderr, testTree } = await runInlineTests({
|
||||
'vitest.config.js': `
|
||||
import { defineConfig } from 'vitest/config'
|
||||
@@ -300,8 +243,8 @@ test('mock works without loading original', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
test.for(['node', 'playwright', 'webdriverio'])('mocking actual module with factory skips loading original (%s)', async (mode) => {
|
||||
const { stderr, errorTree, root } = await runInlineTests({
|
||||
test.for(['node', 'playwright'])('mocking actual module with factory skips loading original (%s)', async (mode) => {
|
||||
const { stderr, errorTree } = await runInlineTests({
|
||||
'vitest.config.js': `
|
||||
import { defineConfig } from 'vitest/config'
|
||||
export default defineConfig({
|
||||
@@ -330,19 +273,6 @@ test('mock works without loading original', () => {
|
||||
`,
|
||||
}, modeToConfig(mode))
|
||||
|
||||
if (mode === 'webdriverio') {
|
||||
expect(replaceRoot(errorTree(), root)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"basic.test.js": {
|
||||
"__module_errors__": [
|
||||
"Failed to import test file <root>/basic.test.js",
|
||||
],
|
||||
},
|
||||
}
|
||||
`)
|
||||
return
|
||||
}
|
||||
|
||||
expect(stderr).toBe('')
|
||||
expect(errorTree()).toMatchInlineSnapshot(`
|
||||
{
|
||||
@@ -353,8 +283,8 @@ test('mock works without loading original', () => {
|
||||
`)
|
||||
})
|
||||
|
||||
test.for(['node', 'playwright', 'webdriverio'])('mocking actual module via __mocks__ skips loading original (%s)', async (mode) => {
|
||||
const { stderr, errorTree, root } = await runInlineTests({
|
||||
test.for(['node', 'playwright'])('mocking actual module via __mocks__ skips loading original (%s)', async (mode) => {
|
||||
const { stderr, errorTree } = await runInlineTests({
|
||||
'vitest.config.js': `
|
||||
import { defineConfig } from 'vitest/config'
|
||||
export default defineConfig({
|
||||
@@ -382,19 +312,6 @@ test('mock works without loading original', () => {
|
||||
`,
|
||||
}, modeToConfig(mode))
|
||||
|
||||
if (mode === 'webdriverio') {
|
||||
expect(replaceRoot(errorTree(), root)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"basic.test.js": {
|
||||
"__module_errors__": [
|
||||
"Failed to import test file <root>/basic.test.js",
|
||||
],
|
||||
},
|
||||
}
|
||||
`)
|
||||
return
|
||||
}
|
||||
|
||||
expect(stderr).toBe('')
|
||||
expect(errorTree()).toMatchInlineSnapshot(`
|
||||
{
|
||||
@@ -449,7 +366,6 @@ ${importChecks}
|
||||
test.for([
|
||||
'node',
|
||||
'playwright',
|
||||
'webdriverio',
|
||||
])('repeating mock, importActual, and resetModules (%s)', async (mode) => {
|
||||
const { stderr, errorTree } = await runInlineTests({
|
||||
// external
|
||||
@@ -551,7 +467,7 @@ test("local", async () => {
|
||||
'./local.js': `export const local = 'local'`,
|
||||
}, modeToConfig(mode))
|
||||
|
||||
if (mode === 'webdriverio' || mode === 'playwright') {
|
||||
if (mode === 'playwright') {
|
||||
// browser mode doesn't support resetModules nor node builtin
|
||||
expect(errorTree()).toMatchInlineSnapshot(`
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user