Skip to content

Commit c16abe7

Browse files
authored
fix(wdio): wait for the driver to be properly closed (#8305)
1 parent d45f964 commit c16abe7

File tree

7 files changed

+44
-27
lines changed

7 files changed

+44
-27
lines changed

docs/guide/browser/commands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ Vitest exposes some `webdriverio` specific properties on the context object.
162162

163163
- `browser` is the `WebdriverIO.Browser` API.
164164

165-
Vitest automatically switches the `webdriver` context to the test iframe by calling `browser.switchToFrame` before the command is called, so `$` and `$$` methods refer to the elements inside the iframe, not in the orchestrator, but non-webdriver APIs will still refer to the parent frame context.
165+
Vitest automatically switches the `webdriver` context to the test iframe by calling `browser.switchFrame` before the command is called, so `$` and `$$` methods refer to the elements inside the iframe, not in the orchestrator, but non-webdriver APIs will still refer to the parent frame context.
166166

167167
::: tip
168168
If you are using TypeScript, don't forget to reference `@vitest/browser/providers/webdriverio` in your [setup file](/config/#setupfile) or a [config file](/config/) to get autocompletion:

packages/browser/src/node/plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ export default (parentServer: ParentBrowserProject, base = '/'): Plugin[] => {
385385
viteConfig.esbuild.legalComments = 'inline'
386386
}
387387

388-
const defaultPort = parentServer.vitest._browserLastPort++
388+
const defaultPort = parentServer.vitest.state._data.browserLastPort++
389389

390390
const api = resolveApiServerConfig(
391391
viteConfig.test?.browser || {},

packages/browser/src/node/pool.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,17 @@ export function createBrowserPool(vitest: Vitest): ProcessPool {
101101
const parallelPools: (() => Promise<void>)[] = []
102102
const nonParallelPools: (() => Promise<void>)[] = []
103103

104-
for (const result of initialisedPools) {
105-
if (!result) {
104+
for (const pool of initialisedPools) {
105+
if (!pool) {
106+
// this means it was cancelled
106107
return
107108
}
108109

109-
if (result.provider.mocker && result.provider.supportsParallelism) {
110-
parallelPools.push(result.runTests)
110+
if (pool.provider.mocker && pool.provider.supportsParallelism) {
111+
parallelPools.push(pool.runTests)
111112
}
112113
else {
113-
nonParallelPools.push(result.runTests)
114+
nonParallelPools.push(pool.runTests)
114115
}
115116
}
116117

packages/browser/src/node/providers/webdriver.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ export class WebdriverBrowserProvider implements BrowserProvider {
3636
}
3737

3838
async initialize(
39-
ctx: TestProject,
39+
project: TestProject,
4040
{ browser, options }: WebdriverProviderOptions,
4141
): Promise<void> {
42+
// increase shutdown timeout because WDIO takes some extra time to kill the driver
43+
if (!project.vitest.state._data.timeoutIncreased) {
44+
project.vitest.state._data.timeoutIncreased = true
45+
project.vitest.config.teardownTimeout += 10_000
46+
}
47+
4248
this.closing = false
43-
this.project = ctx
49+
this.project = project
4450
this.browserName = browser
4551
this.options = options as Capabilities.WebdriverIOConfig
4652
}
@@ -50,17 +56,17 @@ export class WebdriverBrowserProvider implements BrowserProvider {
5056
}
5157

5258
async switchToTestFrame(): Promise<void> {
53-
const page = this.browser!
59+
const browser = this.browser!
5460
// support wdio@9
55-
if (page.switchFrame) {
56-
await page.switchFrame(page.$('iframe[data-vitest]'))
61+
if (browser.switchFrame) {
62+
await browser.switchFrame(browser.$('iframe[data-vitest]'))
5763
}
5864
else {
59-
const iframe = await page.findElement(
65+
const iframe = await browser.findElement(
6066
'css selector',
6167
'iframe[data-vitest]',
6268
)
63-
await page.switchToFrame(iframe)
69+
await browser.switchToFrame(iframe)
6470
}
6571
this.iframeSwitched = true
6672
}
@@ -190,11 +196,15 @@ export class WebdriverBrowserProvider implements BrowserProvider {
190196
async close(): Promise<void> {
191197
debug?.('[%s] closing provider', this.browserName)
192198
this.closing = true
193-
await Promise.all([
194-
this.browser?.sessionId ? this.browser?.deleteSession?.() : null,
195-
])
196-
// TODO: right now process can only exit with timeout, if we use browser
197-
// needs investigating
198-
process.exit()
199+
const browser = this.browser
200+
const sessionId = browser?.sessionId
201+
if (!browser || !sessionId) {
202+
return
203+
}
204+
205+
// https://github.com/webdriverio/webdriverio/blob/ab1a2e82b13a9c7d0e275ae87e7357e1b047d8d3/packages/wdio-runner/src/index.ts#L486
206+
await browser.deleteSession()
207+
browser.sessionId = undefined as unknown as string
208+
this.browser = null
199209
}
200210
}

packages/browser/src/node/rpc.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,17 @@ export function setupBrowserRpc(globalServer: ParentBrowserProject, defaultMocke
5858
)
5959
}
6060

61-
if (!vitest._browserSessions.sessionIds.has(sessionId)) {
62-
const ids = [...vitest._browserSessions.sessionIds].join(', ')
61+
const sessions = vitest._browserSessions
62+
63+
if (!sessions.sessionIds.has(sessionId)) {
64+
const ids = [...sessions.sessionIds].join(', ')
6365
return error(
6466
new Error(`[vitest] Unknown session id "${sessionId}". Expected one of ${ids}.`),
6567
)
6668
}
6769

6870
if (type === 'orchestrator') {
69-
const session = vitest._browserSessions.getSession(sessionId)
71+
const session = sessions.getSession(sessionId)
7072
// it's possible the session was already resolved by the preview provider
7173
session?.connected()
7274
}
@@ -94,7 +96,7 @@ export function setupBrowserRpc(globalServer: ParentBrowserProject, defaultMocke
9496
clients.delete(rpcId)
9597
globalServer.removeCDPHandler(rpcId)
9698
if (type === 'orchestrator') {
97-
vitest._browserSessions.destroySession(sessionId)
99+
sessions.destroySession(sessionId)
98100
}
99101
// this will reject any hanging methods if there are any
100102
rpc.$close(

packages/vitest/src/node/core.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { ViteNodeRunner } from 'vite-node/client'
1919
import { ViteNodeServer } from 'vite-node/server'
2020
import { version } from '../../package.json' with { type: 'json' }
2121
import { WebSocketReporter } from '../api/setup'
22-
import { defaultBrowserPort } from '../constants'
2322
import { distDir } from '../paths'
2423
import { wildcardPatternToRegExp } from '../utils/base'
2524
import { convertTasksToEvents } from '../utils/tasks'
@@ -89,7 +88,6 @@ export class Vitest {
8988
/** @internal */ closingPromise?: Promise<void>
9089
/** @internal */ isCancelling = false
9190
/** @internal */ coreWorkspaceProject: TestProject | undefined
92-
/** @internal */ _browserLastPort = defaultBrowserPort
9391
/** @internal */ _browserSessions = new BrowserSessions()
9492
/** @internal */ _cliOptions: CliOptions = {}
9593
/** @internal */ reporters: Reporter[] = []
@@ -196,7 +194,6 @@ export class Vitest {
196194
this.watcher.unregisterWatcher()
197195
clearTimeout(this._rerunTimer)
198196
this.restartsCount += 1
199-
this._browserLastPort = defaultBrowserPort
200197
this.pool?.close?.()
201198
this.pool = undefined
202199
this.closingPromise = undefined

packages/vitest/src/node/state.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { TestProject } from './project'
44
import type { MergedBlobs } from './reporters/blob'
55
import type { OnUnhandledErrorCallback } from './types/config'
66
import { createFileTask } from '@vitest/runner/utils'
7+
import { defaultBrowserPort } from '../constants'
78
import { TestCase, TestModule, TestSuite } from './reporters/reported-tasks'
89

910
function isAggregateError(err: unknown): err is AggregateError {
@@ -26,6 +27,12 @@ export class StateManager {
2627

2728
onUnhandledError?: OnUnhandledErrorCallback
2829

30+
/** @internal */
31+
_data = {
32+
browserLastPort: defaultBrowserPort,
33+
timeoutIncreased: false,
34+
}
35+
2936
constructor(
3037
options: {
3138
onUnhandledError?: OnUnhandledErrorCallback

0 commit comments

Comments
 (0)