diff --git a/packages/vitest/package.json b/packages/vitest/package.json index f0c7710779c0..ec8873cec1f0 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -180,6 +180,7 @@ "@types/micromatch": "^4.0.2", "@types/prompts": "^2.4.4", "@types/sinonjs__fake-timers": "^8.1.2", + "@wdio/types": "^8.16.3", "birpc": "0.2.14", "chai-subset": "^1.6.0", "cli-truncate": "^3.1.0", diff --git a/packages/vitest/src/node/browser/webdriver.ts b/packages/vitest/src/node/browser/webdriver.ts index 29aa8a2f959c..f4ddccd5c3e0 100644 --- a/packages/vitest/src/node/browser/webdriver.ts +++ b/packages/vitest/src/node/browser/webdriver.ts @@ -1,4 +1,5 @@ import type { Awaitable } from '@vitest/utils' +import type { Capabilities } from '@wdio/types' import type { BrowserProvider, BrowserProviderOptions } from '../../types/browser' import { ensurePackageInstalled } from '../pkg' import type { WorkspaceProject } from '../workspace' @@ -10,6 +11,14 @@ export interface WebdriverProviderOptions extends BrowserProviderOptions { browser: WebdriverBrowser } +type BrowserOptionsKey = 'goog:chromeOptions' | 'moz:firefoxOptions' | 'ms:edgeOptions' +interface WebdriverBrowserExtensionMapValue { + key: BrowserOptionsKey + value: { + args: string[] + } +} + export class WebdriverBrowserProvider implements BrowserProvider { public name = 'webdriverio' @@ -42,6 +51,9 @@ export class WebdriverBrowserProvider implements BrowserProvider { const options = this.ctx.config.browser if (this.browser === 'safari') { + if (options.headless) + throw new Error('You\'ve enabled headless mode for Safari but it doesn\'t currently support it') + const safaridriver = await import('safaridriver') safaridriver.start({ diagnose: true }) this.stopSafari = () => safaridriver.stop() @@ -53,13 +65,57 @@ export class WebdriverBrowserProvider implements BrowserProvider { const { remote } = await import('webdriverio') + const webdriverBrowserExtensionMap: { + chrome: WebdriverBrowserExtensionMapValue + firefox: WebdriverBrowserExtensionMapValue + edge: WebdriverBrowserExtensionMapValue + } = { + chrome: { + key: 'goog:chromeOptions', + value: { + args: [], + }, + }, + firefox: { + key: 'moz:firefoxOptions', + value: { + args: [], + }, + }, + edge: { + key: 'ms:edgeOptions', + value: { + args: [], + }, + }, + } + + const capabilities: Capabilities.VendorExtensions & { + browserName: WebdriverProviderOptions['browser'] + } = { + browserName: this.browser, + } + + if (this.browser !== 'safari') { + if (options.headless) { + switch (this.browser) { + case 'chrome': + webdriverBrowserExtensionMap[this.browser].value.args = webdriverBrowserExtensionMap[this.browser].value.args.concat(['headless', 'disable-gpu']) + break + case 'firefox': + webdriverBrowserExtensionMap[this.browser].value.args.push('-headless') + break + case 'edge': + webdriverBrowserExtensionMap[this.browser].value.args.push('--headless') + } + } + capabilities[webdriverBrowserExtensionMap[this.browser].key] = webdriverBrowserExtensionMap[this.browser].value + } + // TODO: close everything, if browser is closed from the outside this.cachedBrowser = await remote({ logLevel: 'error', - capabilities: { - 'browserName': this.browser, - 'wdio:devtoolsOptions': { headless: options.headless }, - }, + capabilities, }) return this.cachedBrowser diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c47174572df..fad90432bc87 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1393,6 +1393,9 @@ importers: '@types/sinonjs__fake-timers': specifier: ^8.1.2 version: 8.1.2 + '@wdio/types': + specifier: ^8.16.3 + version: 8.16.3 birpc: specifier: 0.2.14 version: 0.2.14 diff --git a/test/browser/specs/runner.test.mjs b/test/browser/specs/runner.test.mjs index 3c9619d31652..b4a21fd3d685 100644 --- a/test/browser/specs/runner.test.mjs +++ b/test/browser/specs/runner.test.mjs @@ -5,7 +5,12 @@ import { execa } from 'execa' const browser = process.env.BROWSER || (process.env.PROVIDER === 'playwright' ? 'chromium' : 'chrome') -const { stderr, stdout } = await execa('npx', ['vitest', '--run', `--browser.name=${browser}`, '--browser.headless'], { +const testArgs = ['vitest', '--run', `--browser.name=${browser}`] +// Safari doesn't support headless mode +if (browser !== 'safari') + testArgs.push('--browser.headless') + +const { stderr, stdout } = await execa('npx', testArgs, { env: { ...process.env, CI: 'true', @@ -24,8 +29,8 @@ const passedTests = getPassed(browserResultJson.testResults) const failedTests = getFailed(browserResultJson.testResults) await test('tests are actually running', async () => { - assert.ok(browserResultJson.testResults.length === 8, 'Not all the tests have been run') - assert.ok(passedTests.length === 7, 'Some tests failed') + assert.ok(browserResultJson.testResults.length === 9, 'Not all the tests have been run') + assert.ok(passedTests.length === 8, 'Some tests failed') assert.ok(failedTests.length === 1, 'Some tests have passed but should fail') assert.doesNotMatch(stderr, /Unhandled Error/, 'doesn\'t have any unhandled errors') diff --git a/test/browser/test/headless.test.ts b/test/browser/test/headless.test.ts new file mode 100644 index 000000000000..8e0e7d9cf6df --- /dev/null +++ b/test/browser/test/headless.test.ts @@ -0,0 +1,5 @@ +import { expect, it } from 'vitest' + +it('is running in headless mode', () => { + expect(navigator.userAgent.search(/headless/i)).not.toBe(-1) +})