diff --git a/__mocks__/@wdio/utils.ts b/__mocks__/@wdio/utils.ts index 527ad0d0d..3301e3e87 100644 --- a/__mocks__/@wdio/utils.ts +++ b/__mocks__/@wdio/utils.ts @@ -1,23 +1,26 @@ +import { vi } from 'vitest' import { + sleep as sleepOrig, isFunctionAsync as isFnAsync, getArgumentType as getArgumentTypeOrig, isValidParameter as isValidParameterOrig, commandCallStructure as commandCallStructureOrig -} from '../../../src/utils' -import webdriverMonadOrig from '../../../src/monad' +} from '../../packages/wdio-utils/src/utils' +import webdriverMonadOrig from '../../packages/wdio-utils/src/monad' import { isW3C as isW3cOrig, sessionEnvironmentDetector as sessionEnvDetector, capabilitiesEnvironmentDetector as capabilitiesEnvDetector, devtoolsEnvironmentDetector as devtoolsEnvDetector -} from '../../../src/envDetector' +} from '../../packages/wdio-utils/src/envDetector' +import { UNICODE_CHARACTERS as UNICODE_CHARACTERS_ORIG } from '../../packages/wdio-utils/src/constants' class DotReporter { options: any emit: any constructor (options: any) { this.options = options - this.emit = jest.fn() + this.emit = vi.fn() } } @@ -62,46 +65,46 @@ const pluginMocks = { framework: frameworkMocks } -export const initialisePlugin = jest.fn().mockImplementation( +export const initialisePlugin = vi.fn().mockImplementation( (name: keyof typeof frameworkMocks, type: keyof typeof pluginMocks) => ( { default: (pluginMocks[type] as any)[name] } ) ) -export const initialiseWorkerService = jest.fn().mockReturnValue([]) -export const initialiseLauncherService = jest.fn().mockReturnValue({ +export const initialiseWorkerService = vi.fn().mockReturnValue([]) +export const initialiseLauncherService = vi.fn().mockReturnValue({ launcherServices: [], ignoredWorkerServices: [] }) export const isValidParameter = isValidParameterOrig export const commandCallStructure = commandCallStructureOrig export const isFunctionAsync = isFnAsync -export const safeRequire = jest.fn().mockReturnValue(() => {}) +export const safeRequire = vi.fn().mockReturnValue(() => {}) export const webdriverMonad = webdriverMonadOrig export const getArgumentType = getArgumentTypeOrig /** * shim */ -export const executeSync = jest.fn() -export const executeAsync = jest.fn() +export const executeSync = vi.fn() +export const executeAsync = vi.fn() export const wrapCommand = (_: any, origFn: any) => origFn -export const runTestInFiberContext = jest.fn().mockReturnValue(jest.fn()) -export const executeHooksWithArgs = jest.fn() -export const runFnInFiberContext = jest.fn().mockImplementation((fn) => { +export const runTestInFiberContext = vi.fn().mockReturnValue(vi.fn()) +export const executeHooksWithArgs = vi.fn() +export const runFnInFiberContext = vi.fn().mockImplementation((fn) => { return function (this: unknown, ...args: any[]) { return Promise.resolve(fn.apply(this, args)) } }) export const setWdioSyncSupport = (value: boolean) => { hasWdioSyncSupport = value } export let hasWdioSyncSupport = false -export const testFnWrapper = jest.fn() +export const testFnWrapper = vi.fn() export const isW3C = isW3cOrig -export const sessionEnvironmentDetector = jest.fn().mockImplementation( +export const sessionEnvironmentDetector = vi.fn().mockImplementation( // @ts-ignore (...args) => sessionEnvDetector(...args)) export const capabilitiesEnvironmentDetector = capabilitiesEnvDetector export const devtoolsEnvironmentDetector = devtoolsEnvDetector -export const transformCommandLogResult = jest.fn().mockImplementation((data) => data) -export const canAccess = jest.fn() -export const sleep = jest.fn().mockImplementation(jest.requireActual('../../../src/utils').sleep) -export const UNICODE_CHARACTERS = jest.requireActual('../../../src/constants').UNICODE_CHARACTERS +export const transformCommandLogResult = vi.fn().mockImplementation((data) => data) +export const canAccess = vi.fn() +export const sleep = vi.fn().mockImplementation(sleepOrig) +export const UNICODE_CHARACTERS = UNICODE_CHARACTERS_ORIG diff --git a/__mocks__/archiver.ts b/__mocks__/archiver.ts new file mode 100644 index 000000000..c58d96769 --- /dev/null +++ b/__mocks__/archiver.ts @@ -0,0 +1,11 @@ +import { vi } from 'vitest' + +const mock = {} as any +mock.on = vi.fn().mockReturnValue(mock) +mock.append = vi.fn().mockReturnValue(mock) +mock.finalize = vi.fn().mockReturnValue(mock) + +export default (...args: any[]) => { + mock.args = args + return mock +} diff --git a/packages/webdriverio/tests/__mocks__/foobartools.ts b/__mocks__/foobartools.ts similarity index 100% rename from packages/webdriverio/tests/__mocks__/foobartools.ts rename to __mocks__/foobartools.ts diff --git a/__mocks__/fs.ts b/__mocks__/fs.ts new file mode 100644 index 000000000..45c1d6a35 --- /dev/null +++ b/__mocks__/fs.ts @@ -0,0 +1,8 @@ +import { vi } from 'vitest' + +const fs = {} as any +fs.writeFileSync = vi.fn() +fs.existsSync = vi.fn(() => true) +fs.accessSync = vi.fn(() => true) + +export default fs diff --git a/__mocks__/get-port.ts b/__mocks__/get-port.ts new file mode 100644 index 000000000..8a2f5026e --- /dev/null +++ b/__mocks__/get-port.ts @@ -0,0 +1,2 @@ +import { vi } from 'vitest' +export default vi.fn().mockReturnValue(Promise.resolve(9222)) diff --git a/packages/webdriverio/tests/__mocks__/got.ts b/__mocks__/got.ts similarity index 97% rename from packages/webdriverio/tests/__mocks__/got.ts rename to __mocks__/got.ts index 72f4a5749..43cfab165 100644 --- a/packages/webdriverio/tests/__mocks__/got.ts +++ b/__mocks__/got.ts @@ -1,4 +1,5 @@ -import { ELEMENT_KEY } from '../../src/constants' +import { vi } from 'vitest' +import { ELEMENT_KEY } from '../packages/webdriverio/src/constants' let manualMockResponse: any @@ -9,7 +10,7 @@ let sessionId = defaultSessionId const genericElementId = 'some-elem-123' const genericSubElementId = 'some-sub-elem-321' const genericSubSubElementId = 'some-sub-sub-elem-231' -const requestMock: any = jest.fn().mockImplementation((uri, params) => { +const requestMock: any = vi.fn().mockImplementation((uri, params) => { let value: any = {} let jsonwpMode = false let sessionResponse: any = { @@ -405,8 +406,8 @@ const requestMock: any = jest.fn().mockImplementation((uri, params) => { }) }) -requestMock.extend = jest.fn().mockReturnValue(requestMock) -requestMock.put = jest.fn().mockReturnValue(Promise.resolve({})) +requestMock.extend = vi.fn().mockReturnValue(requestMock) +requestMock.put = vi.fn().mockReturnValue(Promise.resolve({})) requestMock.retryCnt = 0 requestMock.setMockResponse = (value: any) => { manualMockResponse = value diff --git a/__mocks__/ky.ts b/__mocks__/ky.ts index af3cac653..196722398 100644 --- a/__mocks__/ky.ts +++ b/__mocks__/ky.ts @@ -1,3 +1,5 @@ +import { vi } from 'vitest' + function res(status: number, value: any, fullValue: boolean = false) { return { status, json: () => fullValue ? value : { value } } } @@ -6,7 +8,7 @@ function err(status: number, name: string, message: string) { return { status, json: () => ({ value: { error: name, message } }) } } -const kyMock: any = jest.fn().mockImplementation((url, opts) => { +const kyMock: any = vi.fn().mockImplementation((url, opts) => { switch (url.pathname) { case '/sumoerror': throw new Error('ups') diff --git a/packages/webdriverio/tests/__mocks__/repl.ts b/__mocks__/repl.ts similarity index 55% rename from packages/webdriverio/tests/__mocks__/repl.ts rename to __mocks__/repl.ts index a7ab99aed..fca8a80ef 100644 --- a/packages/webdriverio/tests/__mocks__/repl.ts +++ b/__mocks__/repl.ts @@ -1,5 +1,7 @@ +import { vi } from 'vitest' + class ReplMock { - start = jest.fn().mockReturnValue({ on: this.on.bind(this) }) + start = vi.fn().mockReturnValue({ on: this.on.bind(this) }) on (event: string, cb: Function) { cb(event) diff --git a/packages/webdriverio/tests/__mocks__/webdriverio.ts b/__mocks__/webdriverio.ts similarity index 100% rename from packages/webdriverio/tests/__mocks__/webdriverio.ts rename to __mocks__/webdriverio.ts diff --git a/packages/wdio-utils/tests/shim-async.test.ts b/packages/wdio-utils/tests/shim-async.test.ts index e5d4130d8..3b34bd303 100644 --- a/packages/wdio-utils/tests/shim-async.test.ts +++ b/packages/wdio-utils/tests/shim-async.test.ts @@ -5,10 +5,6 @@ import { executeAsync, wrapCommand } from '../src/shim' -vi.mock('@wdio/sync', () => { - throw new Error('Does not exist') -}) - const globalAny: any = global beforeEach(() => { diff --git a/packages/wdio-utils/tests/utils.test.ts b/packages/wdio-utils/tests/utils.test.ts index 41b792030..40903a499 100644 --- a/packages/wdio-utils/tests/utils.test.ts +++ b/packages/wdio-utils/tests/utils.test.ts @@ -1,4 +1,5 @@ import { vi, describe, it, expect, MockedFunction } from 'vitest' +import path from 'path' import fs from 'node:fs' import { @@ -6,7 +7,7 @@ import { getArgumentType, isFunctionAsync, filterSpecArgs, isBase64, transformCommandLogResult } from '../src/utils' -vi.mock('fs') +vi.mock('fs', () => import(path.join(process.cwd(), '__mocks__', 'fs'))) describe('utils', () => { it('commandCallStructure', () => { diff --git a/packages/webdriver/src/command.ts b/packages/webdriver/src/command.ts index 13357407d..1555c3d64 100644 --- a/packages/webdriver/src/command.ts +++ b/packages/webdriver/src/command.ts @@ -16,7 +16,7 @@ export default function ( ) { const { command, ref, parameters, variables = [], isHubCommand = false } = commandInfo - return function protocolCommand (this: BaseClient, ...args: any[]): Promise { + return async function protocolCommand (this: BaseClient, ...args: any[]): Promise { let endpoint = endpointUri // clone endpointUri in case we change it const commandParams = [...variables.map((v) => Object.assign(v, { /** @@ -88,7 +88,7 @@ export default function ( body[commandParams[i].name] = arg } - const request = RequestFactory.getInstance(method, endpoint, body, isHubCommand) + const request = await RequestFactory.getInstance(method, endpoint, body, isHubCommand) request.on('performance', (...args) => this.emit('request.performance', ...args)) this.emit('command', { method, endpoint, body }) log.info('COMMAND', commandCallStructure(command, args)) @@ -96,7 +96,8 @@ export default function ( if (result.value != null) { log.info('RESULT', /screenshot|recording/i.test(command) && typeof result.value === 'string' && result.value.length > 64 - ? `${result.value.substr(0, 61)}...` : result.value) + ? `${result.value.slice(0, 61)}...` + : result.value) } this.emit('result', { method, endpoint, body, result }) diff --git a/packages/webdriver/src/request/factory.ts b/packages/webdriver/src/request/factory.ts index a1e05f3c0..26f4ec985 100644 --- a/packages/webdriver/src/request/factory.ts +++ b/packages/webdriver/src/request/factory.ts @@ -1,24 +1,28 @@ import type { URL as URLType } from 'node:url' +import type NodeJSRequest from './node' +import type BrowserRequest from './browser' import WebDriverRequest from './index.js' +interface Request { + new (method: string, endpoint: string, body?: Record, isHubCommand?: boolean): NodeJSRequest | BrowserRequest; +} + +let EnvRequestLib: Request export default class RequestFactory { - static getInstance ( + static async getInstance ( method: string, endpoint: string, body?: Record, isHubCommand: boolean = false - ): WebDriverRequest { - if (process?.versions?.node) { - const reqModule = require('./node') - // we either need to get the default export explicitly in the prod case, or implicitly - // in the case of jest mocking - const NodeJSRequest = reqModule.default || reqModule - return new NodeJSRequest(method, endpoint, body, isHubCommand) + ): Promise { + if (!EnvRequestLib) { + EnvRequestLib = process?.versions?.node + ? (await import('./node')).default + : (await import('./browser')).default } - const BrowserRequest = require('./browser').default - return new BrowserRequest(method, endpoint, body, isHubCommand) + return new EnvRequestLib(method, endpoint, body, isHubCommand) } } diff --git a/packages/webdriver/src/utils.ts b/packages/webdriver/src/utils.ts index 2b0322748..cb4542b6b 100644 --- a/packages/webdriver/src/utils.ts +++ b/packages/webdriver/src/utils.ts @@ -69,7 +69,7 @@ export async function startWebDriverSession (params: Options.WebDriver): Promise */ : [{ alwaysMatch: params.capabilities, firstMatch: [{}] }, params.capabilities] - const sessionRequest = RequestFactory.getInstance( + const sessionRequest = await RequestFactory.getInstance( 'POST', '/session', { diff --git a/packages/webdriver/tests/__snapshots__/index.test.ts.snap b/packages/webdriver/tests/__snapshots__/index.test.ts.snap index 726fa4aee..a4a90ad96 100644 --- a/packages/webdriver/tests/__snapshots__/index.test.ts.snap +++ b/packages/webdriver/tests/__snapshots__/index.test.ts.snap @@ -1,21 +1,21 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1 -exports[`WebDriver newSession should allow to create a new session using w3c compliant caps 1`] = ` -Array [ - Array [ - Object { - "capabilities": Object { +exports[`WebDriver > newSession > should allow to create a new session using w3c compliant caps 1`] = ` +[ + [ + { + "capabilities": { "browserName": "mockBrowser", "browserVersion": "1234", "platformName": "node", "setWindowRect": true, }, - "requestedCapabilities": Object { - "alwaysMatch": Object { + "requestedCapabilities": { + "alwaysMatch": { "browserName": "firefox", }, - "firstMatch": Array [ - Object {}, + "firstMatch": [ + {}, ], }, }, diff --git a/packages/webdriver/tests/command.test.ts b/packages/webdriver/tests/command.test.ts index a83e6f64c..601744e20 100644 --- a/packages/webdriver/tests/command.test.ts +++ b/packages/webdriver/tests/command.test.ts @@ -1,17 +1,20 @@ -import { EventEmitter } from 'events' +import path from 'node:path' +import { EventEmitter } from 'node:events' +import { describe, it, expect, vi } from 'vitest' import logger from '@wdio/logger' -import Protocols from '@wdio/protocols' +import type Protocols from '@wdio/protocols' import type { Options } from '@wdio/types' +// @ts-expect-error mock feature +import RequestMock, { thenMock } from '../src/request/node' import commandWrapper from '../src/command' import { BaseClient } from '../src/types' -/** - * workaround as typescript compiler uses expect-webdriverio as global - */ -const expect: jest.Expect = global.expect as unknown as jest.Expect +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('got') +const log = logger('webdriver') const commandPath = '/session/:sessionId/element/:elementId/element' const commandMethod = 'POST' const commandEndpoint: Protocols.CommandEndpoint = { @@ -45,15 +48,19 @@ const commandEndpoint: Protocols.CommandEndpoint = { }] } -jest.mock('../src/request/node', () => jest.fn().mockImplementation( - () => ({ - makeRequest: jest.fn().mockReturnValue({ - then: jest.fn().mockImplementation( - (then) => then) - }), - on: jest.fn(), - }) -)) +vi.mock('../src/request/node', () => { + const thenMock = vi.fn().mockResolvedValue({ value: 15 }) + return { + thenMock, + default: vi.fn().mockReturnValue({ + makeRequest: () => ({ + then: thenMock, + catch: vi.fn() + }), + on: vi.fn() + }) + } +}) class FakeClient extends EventEmitter { isW3C = false @@ -62,34 +69,39 @@ class FakeClient extends EventEmitter { isMobile = false isIOS = false isSauce = false + isFirefox = false + isDevTools =false isSeleniumStandalone = false sessionId = '123' capabilities = {} requestedCapabilities = {} options = { logLevel: 'warn' as Options.WebDriverLogTypes - } + } as any } const scope: BaseClient = new FakeClient() type mockResponse = (...args: any[]) => any describe('command wrapper', () => { - it('should fail if wrong arguments are passed in', () => { + it('should fail if wrong arguments are passed in', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - expect(commandFn) + await expect(commandFn) + .rejects .toThrow(/Wrong parameters applied for findElementFromElement/) }) - it('should fail if arguments are malformed', () => { + it('should fail if arguments are malformed', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - expect(() => commandFn.call(scope, '123', 123, '123')) + await expect(() => commandFn.call(scope, '123', 123, '123')) + .rejects .toThrow(/Malformed type for "using" parameter of command/) }) - it('should fail if not required param has wrong type', () => { + it('should fail if not required param has wrong type', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - expect(() => commandFn.call(scope, '123', '123', '123', 'foobar')) + await expect(() => commandFn.call(scope, '123', '123', '123', 'foobar')) + .rejects .toThrow(/Malformed type for "customParam" parameter of command/) }) @@ -97,81 +109,87 @@ describe('command wrapper', () => { expect.assertions(1) const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - try { - commandFn.call(scope, '123', '123', '123', 234, () => {}) - } catch (err: any) { - expect(err.message).toContain('Actual: (function)[]') - } + await expect(commandFn.call(scope, '123', '123', '123', 234, () => {})) + .rejects + .toThrow(/Actual: \(function\)\[\]/) }) - it('should do a proper request', () => { + it('should do a proper request', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - const requestMock = require('../src/request/node') - const resultFunction = commandFn.call(scope, '123', 'css selector', '#body', undefined) as unknown as mockResponse - expect(requestMock.mock.calls).toHaveLength(1) - expect(resultFunction({ value: 14 })).toBe(14) + await commandFn.call(scope, '123', 'css selector', '#body', undefined) as unknown as mockResponse + const callback = thenMock.mock.calls[0][0] + expect(callback({ value: 14 })).toBe(14) + vi.mocked(thenMock).mockClear() - const [method, endpoint, { using, value }] = requestMock.mock.calls[0] - expect(method).toBe('POST') - expect(endpoint).toBe('/session/:sessionId/element/123/element') - expect(using).toBe('css selector') - expect(value).toBe('#body') - requestMock.mockClear() + expect(RequestMock).toHaveBeenCalledWith( + 'POST', + '/session/:sessionId/element/123/element', + { + using: 'css selector', + value: '#body' + }, + false + ) + vi.mocked(RequestMock).mockClear() }) - it('should do a proper request with non required params', () => { + it('should do a proper request with non required params', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - const requestMock = require('../src/request/node') - const resultFunction = commandFn.call(scope, '123', 'css selector', '#body', 123) as unknown as mockResponse - expect(requestMock.mock.calls).toHaveLength(1) - expect(resultFunction({ value: 'foobarboo' })).toBe('foobarboo') - - const [method, endpoint, { using, value, customParam }] = requestMock.mock.calls[0] - expect(method).toBe('POST') - expect(endpoint).toBe('/session/:sessionId/element/123/element') - expect(using).toBe('css selector') - expect(value).toBe('#body') - expect(customParam).toBe(123) - requestMock.mockClear() + await commandFn.call(scope, '123', 'css selector', '#body', 123) as unknown as mockResponse + expect(RequestMock).toHaveBeenCalledWith( + 'POST', + '/session/:sessionId/element/123/element', + { + using: 'css selector', + value: '#body', + customParam: 123 + }, + false + ) + vi.mocked(RequestMock).mockClear() }) - it('should encode uri parameters', () => { + it('should encode uri parameters', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint) - const requestMock = require('../src/request/node') - commandFn.call(scope, '/path', 'css selector', '#body', 123) + await commandFn.call(scope, '/path', 'css selector', '#body', 123) - const [, endpoint] = requestMock.mock.calls[0] - expect(endpoint).toBe('/session/:sessionId/element/%2Fpath/element') - requestMock.mockClear() + expect(RequestMock).toHaveBeenCalledWith( + 'POST', + '/session/:sessionId/element/%2Fpath/element', + expect.anything(), + false + ) + vi.mocked(RequestMock).mockClear() }) - it('should double encode uri parameters if using selenium', () => { + it('should double encode uri parameters if using selenium', async () => { const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint, true) - const requestMock = require('../src/request/node') - commandFn.call(scope, '/path', 'css selector', '#body', 123) + await commandFn.call(scope, '/path', 'css selector', '#body', 123) - const [, endpoint] = requestMock.mock.calls[0] - expect(endpoint).toBe('/session/:sessionId/element/%252Fpath/element') - requestMock.mockClear() + expect(RequestMock).toHaveBeenCalledWith( + 'POST', + '/session/:sessionId/element/%252Fpath/element', + expect.anything(), + false + ) + vi.mocked(RequestMock).mockClear() }) }) describe('command wrapper result log', () => { - const log = logger('webdriver') - jest.spyOn(log, 'info').mockImplementation((string) => string) - logger.clearLogger = jest.fn() - const clearLoggerSpy = jest.spyOn(logger, 'clearLogger') - - function getRequestCallback (method: string, path: string, endpoint: Protocols.CommandEndpoint) { + async function getRequestCallback (method: string, path: string, endpoint: Protocols.CommandEndpoint) { const commandFn = commandWrapper(method, path, endpoint) - const requestMock = require('../src/request/node') - const resultFunction = commandFn.call(scope) - expect(requestMock.mock.calls).toHaveLength(1) + await commandFn.call(scope) + expect(RequestMock).toHaveBeenCalledTimes(1) + expect(RequestMock).toHaveBeenCalledWith(method, path, expect.any(Object), false) - requestMock.mockClear() - ;(log.info as jest.Mock).mockClear() + const callback = thenMock.mock.calls[0][0] - return resultFunction + vi.mocked(RequestMock).mockClear() + vi.mocked(thenMock).mockClear() + vi.mocked(log.info).mockClear() + + return callback } const takeScreenshotCmd = { @@ -200,7 +218,7 @@ describe('command wrapper result log', () => { title: 'truncate long string value', command: { ...takeScreenshotCmd }, value: 'f'.repeat(65), - log: 'f'.repeat(61) + '...' + log: 'f'.repeat(65) }, { title: 'truncate long string value', command: { @@ -272,16 +290,20 @@ describe('command wrapper result log', () => { get log() { return this.value } }] + logger.clearLogger = jest.fn() + const clearLoggerSpy = jest.spyOn(logger, 'clearLogger') + beforeEach(() => { delete process.env.WDIO_WORKER_ID jest.clearAllMocks() }) for (const scenario of scenarios) { - it(`should ${scenario.title} for ${scenario.command.endpoint.command}`, () => { - const resultFunction = getRequestCallback(scenario.command.method, scenario.command.path, scenario.command.endpoint) as unknown as mockResponse + it(`should ${scenario.title} for ${scenario.command.endpoint.command}`, async () => { + const resultFunction = await getRequestCallback(scenario.command.method, scenario.command.path, scenario.command.endpoint) as unknown as mockResponse + resultFunction({ value: scenario.value }) - expect((log.info as jest.Mock).mock.calls[0][1]).toBe(scenario.log) + expect(vi.mocked(log.info).mock.calls[0][1]).toBe(scenario.log) expect(clearLoggerSpy).not.toHaveBeenCalled() }) } @@ -290,14 +312,14 @@ describe('command wrapper result log', () => { process.env.WDIO_WORKER_ID = '0-0' const resultFunction = getRequestCallback(deleteSessionCmd.method, takeScreenshotCmd.path, takeScreenshotCmd.endpoint) as unknown as mockResponse resultFunction({}) - expect((log.info as jest.Mock).mock.calls).toHaveLength(0) + expect(vi.mocked(log.info).mock.calls).toHaveLength(0) expect(clearLoggerSpy).not.toHaveBeenCalled() }) it('should call clearLogger on deleteSession cmd', () => { const resultFunction = getRequestCallback(deleteSessionCmd.method, deleteSessionCmd.path, deleteSessionCmd.endpoint) as unknown as mockResponse resultFunction({}) - expect((log.info as jest.Mock).mock.calls).toHaveLength(0) + expect(vi.mocked(log.info).mock.calls).toHaveLength(0) expect(clearLoggerSpy).toHaveBeenCalledTimes(1) }) }) diff --git a/packages/webdriver/tests/constants.test.ts b/packages/webdriver/tests/constants.test.ts index 2f5ee20d9..3aaf6211c 100644 --- a/packages/webdriver/tests/constants.test.ts +++ b/packages/webdriver/tests/constants.test.ts @@ -1,3 +1,4 @@ +import { test, expect } from 'vitest' import { DEFAULTS } from '../src/constants' test('should do correct type check for "path"', () => { diff --git a/packages/webdriver/tests/index.test.ts b/packages/webdriver/tests/index.test.ts index 04dabe7d2..478eff4d9 100644 --- a/packages/webdriver/tests/index.test.ts +++ b/packages/webdriver/tests/index.test.ts @@ -1,18 +1,18 @@ -jest.unmock('@wdio/config') - -import path from 'path' -import gotMock from 'got' +import path from 'node:path' +import got from 'got' +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' // @ts-ignore mock feature import logger, { logMock } from '@wdio/logger' -import * as wdioUtils from '@wdio/utils' -import { Capabilities } from '@wdio/types' +import { sessionEnvironmentDetector } from '@wdio/utils' +import type { Capabilities } from '@wdio/types' import WebDriver, { getPrototype, DEFAULTS, command } from '../src' import { Client } from '../src/types' -const expect = global.expect as unknown as jest.Expect -const got = gotMock as unknown as jest.Mock -const sessionEnvironmentDetector = wdioUtils.sessionEnvironmentDetector as jest.Mock +vi.mock('@wdio/utils', () => import(path.join(process.cwd(), '__mocks__', '@wdio/utils'))) +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('fs') +vi.mock('got') const sessionOptions = { protocol: 'http', @@ -43,6 +43,7 @@ const setUpLogCheck = (conditionFunction: () => boolean) => { logMock.debug.mockImplementation(logCheck) } +// @ts-expect-error interface TestClient extends Client { getUrl (): string getApplicationCacheStatus (): void @@ -51,11 +52,11 @@ interface TestClient extends Client { } beforeEach(() => { - sessionEnvironmentDetector.mockClear() + vi.mocked(sessionEnvironmentDetector).mockClear() }) describe('WebDriver', () => { - test('exports getPrototype, DEFAULTS', () => { + it('exports getPrototype, DEFAULTS', () => { expect(typeof getPrototype).toBe('function') expect(typeof DEFAULTS).toBe('object') expect(typeof command).toBe('function') @@ -79,16 +80,16 @@ describe('WebDriver', () => { capabilities: { browserName: 'firefox' } }) - const url = got.mock.calls[0][0] - const req = got.mock.calls[0][1] - expect(url.pathname).toBe('/session') - expect(req.json).toEqual({ - capabilities: { - alwaysMatch: { browserName: 'firefox' }, - firstMatch: [{}] - }, - desiredCapabilities: { browserName: 'firefox' } - }) + expect(got).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/session' }), + expect.objectContaining({ json: { + capabilities: { + alwaysMatch: { browserName: 'firefox' }, + firstMatch: [{}] + }, + desiredCapabilities: { browserName: 'firefox' } + } }) + ) expect(process.env.WDIO_LOG_PATH).toEqual(path.join(testDirPath, 'wdio.log')) }) @@ -101,18 +102,18 @@ describe('WebDriver', () => { } }) - const url = got.mock.calls[0][0] - const req = got.mock.calls[0][1] - expect(url.pathname).toBe('/session') - expect(req.json).toEqual({ - capabilities: { - alwaysMatch: { browserName: 'firefox' }, - firstMatch: [{}] - }, - desiredCapabilities: { browserName: 'firefox' } - }) + expect(got).toHaveBeenCalledWith( + expect.objectContaining({ pathname: '/session' }), + expect.objectContaining({ json: { + capabilities: { + alwaysMatch: { browserName: 'firefox' }, + firstMatch: [{}] + }, + desiredCapabilities: { browserName: 'firefox' } + } }) + ) - expect(sessionEnvironmentDetector.mock.calls) + expect(vi.mocked(sessionEnvironmentDetector).mock.calls) .toMatchSnapshot() }) @@ -184,23 +185,27 @@ describe('WebDriver', () => { describe('attachToSession', () => { it('should allow to attach to existing session', async () => { - const client = WebDriver.attachToSession({ ...sessionOptions, logLevel: 'error' }) as TestClient + const client = WebDriver.attachToSession({ ...sessionOptions, logLevel: 'error' }) as any as TestClient await client.getUrl() - const url = got.mock.calls[0][0] - expect(url.href).toBe('http://localhost:4444/session/foobar/url') + expect(got).toHaveBeenCalledWith( + expect.objectContaining({ href: 'http://localhost:4444/session/foobar/url' }), + expect.anything() + ) expect(logger.setLevel).toBeCalled() }) it('should allow to attach to existing session2', async () => { - const client = WebDriver.attachToSession({ ...sessionOptions }) as TestClient + const client = WebDriver.attachToSession({ ...sessionOptions }) as any as TestClient await client.getUrl() - const url = got.mock.calls[0][0] - expect(url.href).toBe('http://localhost:4444/session/foobar/url') + expect(got).toHaveBeenCalledWith( + expect.objectContaining({ href: 'http://localhost:4444/session/foobar/url' }), + expect.anything() + ) expect(logger.setLevel).not.toBeCalled() }) it('should allow to attach to existing session - W3C', async () => { - const client = WebDriver.attachToSession({ ...sessionOptions }) as TestClient + const client = WebDriver.attachToSession({ ...sessionOptions }) as any as TestClient await client.getUrl() expect(client.isChrome).toBeFalsy() @@ -215,7 +220,7 @@ describe('WebDriver', () => { const client = WebDriver.attachToSession({ ...sessionOptions, isW3C: false, isSauce: true, - }) as TestClient + }) as any as TestClient await client.getUrl() @@ -229,7 +234,7 @@ describe('WebDriver', () => { const client = WebDriver.attachToSession({ ...sessionOptions, isChrome: true, isMobile: true - }) as TestClient + }) as any as TestClient await client.getUrl() @@ -294,7 +299,7 @@ describe('WebDriver', () => { capabilities: { browserName: 'firefox' } }) await WebDriver.reloadSession(session) - expect(got.mock.calls).toHaveLength(2) + expect(got).toHaveBeenCalledTimes(2) }) }) @@ -303,8 +308,8 @@ describe('WebDriver', () => { }) afterEach(() => { - (logger.setLevel as jest.Mock).mockClear() - got.mockClear() - sessionEnvironmentDetector.mockClear() + vi.mocked(logger.setLevel).mockClear() + vi.mocked(got).mockClear() + vi.mocked(sessionEnvironmentDetector).mockClear() }) }) diff --git a/packages/webdriver/tests/request-browser.test.ts b/packages/webdriver/tests/request-browser.test.ts index 3a46f0173..fb2129bbc 100644 --- a/packages/webdriver/tests/request-browser.test.ts +++ b/packages/webdriver/tests/request-browser.test.ts @@ -1,23 +1,18 @@ -/** - * @jest-environment jsdom - */ - +// @vitest-environment jsdom +import path from 'node:path' +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import logger from '@wdio/logger' -import _ky from 'ky' +import ky from 'ky' -import { Options } from '@wdio/types' +import type { Options } from '@wdio/types' import BrowserRequest from '../src/request/browser' -type LogMock = { - warn: jest.Mock, - error: jest.Mock -} +vi.mock('ky') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) -const ky = >_ky +const { warn, error } = logger('test') -const { warn, error } = logger('test') as unknown as LogMock - -const path = '/session' +const webdriverPath = '/session' const defaultOptions = { protocol: 'http', hostname: 'localhost', @@ -31,24 +26,27 @@ describe('webdriver request', () => { expect(req.endpoint).toBe('/foo/bar') }) - it('should be able to make request', () => { + it('should be able to make request', async () => { const req = new BrowserRequest('POST', '/foo/bar', { foo: 'bar' }) - req['_createOptions'] = jest.fn().mockImplementation((_, sessionId) => ({ + req['_createOptions'] = vi.fn().mockImplementation((_, sessionId) => ({ foo: 'bar', sessionId })) - req.emit = jest.fn() - req['_request'] = jest.fn() + req.emit = vi.fn() + req['_request'] = vi.fn() - req.makeRequest({ connectionRetryCount: 43, logLevel: 'warn' }, 'some_id') - expect((req['_request'] as jest.Mock).mock.calls[0][0].foo).toBe('bar') - expect((req['_request'] as jest.Mock).mock.calls[0][0].sessionId).toBe('some_id') - expect((req['_request'] as jest.Mock).mock.calls[0][2]).toBe(43) + await req.makeRequest({ connectionRetryCount: 43, logLevel: 'warn' }, 'some_id') + expect(req['_request']).toHaveBeenCalledWith( + expect.objectContaining({ foo: 'bar', sessionId: 'some_id' }), + undefined, + 43, + 0 + ) }) it('should pick up the fullRequestOptions returned by transformRequest', async () => { const req = new BrowserRequest('POST', '/foo/bar', { foo: 'bar' }) - const transformRequest = jest.fn().mockImplementation((requestOptions) => ({ + const transformRequest = vi.fn().mockImplementation((requestOptions) => ({ ...requestOptions, json: { foo: 'baz' } })) @@ -62,18 +60,21 @@ describe('webdriver request', () => { logLevel: 'warn' }, 'some_id') - expect(ky.mock.calls[0][1].json).toEqual({ foo: 'baz' }) + expect(ky).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ json: { foo: 'baz' } }) + ) }) it('should resolve with the body returned by transformResponse', async () => { const req = new BrowserRequest('POST', 'session/:sessionId/element', { foo: 'requestBody' }) - const transformResponse = jest.fn().mockImplementation((response) => ({ + const transformResponse = vi.fn().mockImplementation((response) => ({ ...response, body: { value: { foo: 'transformedResponse' } }, })) - ky.mockClear() + vi.mocked(ky).mockClear() const responseBody = await req.makeRequest({ transformResponse, protocol: 'https', @@ -86,18 +87,19 @@ describe('webdriver request', () => { expect(transformResponse.mock.calls[0][0]).toHaveProperty('body') expect(transformResponse.mock.calls[0][1].json).toEqual({ foo: 'requestBody' }) await expect(responseBody).toEqual({ value: { foo: 'transformedResponse' } }) - ky.mockClear() + vi.mocked(ky).mockClear() }) describe('createOptions', () => { - it('fails if command requires sessionId but none given', () => { - const req = new BrowserRequest('POST', `${path}/:sessionId/element`, {}) - expect(() => req['_createOptions']({ logLevel: 'warn' })).toThrow() + it('fails if command requires sessionId but none given', async () => { + const req = new BrowserRequest('POST', `${webdriverPath}/:sessionId/element`, {}) + await expect(async () => await req['_createOptions']({ logLevel: 'warn' })) + .rejects.toThrow('A sessionId is required') }) - it('creates proper options set', () => { - const req = new BrowserRequest('POST', `${path}/:sessionId/element`, {}) - const options: Options.RequestLibOptions = req['_createOptions']({ + it('creates proper options set', async () => { + const req = new BrowserRequest('POST', `${webdriverPath}/:sessionId/element`, {}) + const options: Options.RequestLibOptions = await req['_createOptions']({ protocol: 'https', hostname: 'localhost', port: 4445, @@ -116,9 +118,9 @@ describe('webdriver request', () => { expect(options.timeout).toBe(10 * 1000) }) - it('ignores path when command is a hub command', () => { + it('ignores path when command is a hub command', async () => { const req = new BrowserRequest('POST', '/grid/api/hub', {}, true) - const options = req['_createOptions']({ + const options = await req['_createOptions']({ protocol: 'https', hostname: 'localhost', port: 4445, @@ -128,9 +130,9 @@ describe('webdriver request', () => { expect((options.url as URL).href).toBe('https://localhost:4445/grid/api/hub') }) - it('should add auth if user and key is given', () => { - const req = new BrowserRequest('POST', path, { some: 'body' }) - const options = req['_createOptions']({ + it('should add auth if user and key is given', async () => { + const req = new BrowserRequest('POST', webdriverPath, { some: 'body' }) + const options = await req['_createOptions']({ ...defaultOptions, user: 'foo', key: 'bar', @@ -142,9 +144,9 @@ describe('webdriver request', () => { expect(options.json).toEqual({ some: 'body' }) }) - it('sets request body to "undefined" when request object is empty and DELETE is used', () => { - const req = new BrowserRequest('DELETE', path, {}) - const options = req['_createOptions']({ + it('sets request body to "undefined" when request object is empty and DELETE is used', async () => { + const req = new BrowserRequest('DELETE', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -152,9 +154,9 @@ describe('webdriver request', () => { expect(Boolean(options.json)).toEqual(false) }) - it('sets request body to "undefined" when request object is empty and GET is used', () => { - const req = new BrowserRequest('GET', `${path}/title`, {}) - const options = req['_createOptions']({ + it('sets request body to "undefined" when request object is empty and GET is used', async () => { + const req = new BrowserRequest('GET', `${webdriverPath}/title`, {}) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -162,9 +164,9 @@ describe('webdriver request', () => { expect(Boolean(options.json)).toEqual(false) }) - it('should attach an empty object body when POST is used', () => { + it('should attach an empty object body when POST is used', async () => { const req = new BrowserRequest('POST', '/status', {}) - const options = req['_createOptions']({ + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -172,9 +174,9 @@ describe('webdriver request', () => { expect(options.json).toEqual({}) }) - it('should add the Content-Length header when a request object has a body', () => { - const req = new BrowserRequest('POST', path, { foo: 'bar' }) - const options = req['_createOptions']({ + it('should add the Content-Length header when a request object has a body', async () => { + const req = new BrowserRequest('POST', webdriverPath, { foo: 'bar' }) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -184,9 +186,9 @@ describe('webdriver request', () => { expect((options.headers as Record)['Content-Length']).toBe('13') }) - it('should add Content-Length as well any other header provided in the request options if there is body in the request object', () => { - const req = new BrowserRequest('POST', path, { foo: 'bar' }) - const options = req['_createOptions']({ + it('should add Content-Length as well any other header provided in the request options if there is body in the request object', async () => { + const req = new BrowserRequest('POST', webdriverPath, { foo: 'bar' }) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', headers: { foo: 'bar' }, logLevel: 'warn' @@ -197,9 +199,9 @@ describe('webdriver request', () => { expect(headers['Content-Length']).toBe('13') }) - it('should add only the headers provided if the request body is empty', () => { - const req = new BrowserRequest('POST', path) - const options = req['_createOptions']({ + it('should add only the headers provided if the request body is empty', async () => { + const req = new BrowserRequest('POST', webdriverPath) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', headers: { foo: 'bar' }, @@ -223,64 +225,64 @@ describe('webdriver request', () => { delete process.env.strict_ssl }) - it('should contain key "rejectUnauthorized" with value "false" when "strictSSL" argument is given with false', () => { - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions']({ ...defaults, strictSSL: false }) + it('should contain key "rejectUnauthorized" with value "false" when "strictSSL" argument is given with false', async () => { + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaults, strictSSL: false }) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "false" when environment variable "STRICT_SSL" is defined with value "false"', () => { + it('should contain key "rejectUnauthorized" with value "false" when environment variable "STRICT_SSL" is defined with value "false"', async () => { process.env['STRICT_SSL'] = 'false' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "false" when environment variable "strict_ssl" is defined with value "false"', () => { + it('should contain key "rejectUnauthorized" with value "false" when environment variable "strict_ssl" is defined with value "false"', async () => { process.env['strict_ssl'] = 'false' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "true" when "strictSSL" argument is given with true', () => { - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions']({ ...defaults, strictSSL: true }) + it('should contain key "rejectUnauthorized" with value "true" when "strictSSL" argument is given with true', async () => { + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaults, strictSSL: true }) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with value "true"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with value "true"', async () => { process.env['STRICT_SSL'] = 'true' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with value "true"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with value "true"', async () => { process.env['strict_ssl'] = 'true' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" / "strict_ssl" is not defined', () => { - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" / "strict_ssl" is not defined', async () => { + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with any other value than "false"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with any other value than "false"', async () => { process.env['STRICT_SSL'] = 'foo' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with any other value than "false"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with any other value than "false"', async () => { process.env['strict_ssl'] = 'foo' - const req = new BrowserRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new BrowserRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) }) @@ -289,8 +291,8 @@ describe('webdriver request', () => { describe('_request', () => { it('should make a request', async () => { const expectedResponse = { value: { 'element-6066-11e4-a52e-4f735466cecf': 'some-elem-123' } } - const req = new BrowserRequest('POST', path, {}) - req.emit = jest.fn() + const req = new BrowserRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign( req.defaultOptions, @@ -299,7 +301,7 @@ describe('webdriver request', () => { const res = await req['_request'](opts) expect(res).toEqual(expectedResponse) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(req.emit).toHaveBeenCalledTimes(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', { result: expectedResponse }) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ request: opts, @@ -311,7 +313,7 @@ describe('webdriver request', () => { it('should short circuit if request throws a stale element exception', async () => { const req = new BrowserRequest('POST', 'session/:sessionId/element', {}) - req.emit = jest.fn() + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/session/foobar-123/element/some-sub-sub-elem-231/click' }, @@ -319,51 +321,51 @@ describe('webdriver request', () => { }) const error = await req['_request'](opts).catch(err => err) - expect(error.message).toBe('element is not attached to the page document') - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(vi.mocked(error).message).toBe('element is not attached to the page document') + expect(req.emit).toHaveBeenCalledTimes(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(1) - expect(warn.mock.calls).toEqual([['Request encountered a stale element - terminating request']]) + expect(vi.mocked(warn).mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toEqual([['Request encountered a stale element - terminating request']]) }) it('should not fail code due to an empty server response', async () => { - const req = new BrowserRequest('POST', path, {}) - req.emit = jest.fn() + const req = new BrowserRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/empty' } }) await expect(req['_request'](opts)).rejects.toEqual(new Error('Response has empty body')) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(req.emit).toHaveBeenCalledTimes(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(0) - expect(error.mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toHaveLength(0) + expect(vi.mocked(error).mock.calls).toHaveLength(1) }) it('should retry requests but still fail', async () => { - const req = new BrowserRequest('POST', path, {}) - req.emit = jest.fn() + const req = new BrowserRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/failing' } }) await expect(req['_request'](opts, undefined, 2)).rejects.toEqual(new Error('unknown error')) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(6) + expect(req.emit).toHaveBeenCalledTimes(6) expect(req.emit).toHaveBeenNthCalledWith(1, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(3, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(4, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(5, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(6, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(2) - expect(error.mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toHaveLength(2) + expect(vi.mocked(error).mock.calls).toHaveLength(1) }) it('should retry and eventually respond', async () => { - const req = new BrowserRequest('POST', path, {}) - req.emit = jest.fn() + const req = new BrowserRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/failing' }, json: { foo: 'bar' } }) expect(await req['_request'](opts, undefined, 3)).toEqual({ value: 'caught' }) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(8) + expect(req.emit).toHaveBeenCalledTimes(8) expect(req.emit).toHaveBeenNthCalledWith(1, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(3, 'retry', expect.anything()) @@ -372,8 +374,8 @@ describe('webdriver request', () => { expect(req.emit).toHaveBeenNthCalledWith(6, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(7, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(8, 'performance', expect.objectContaining({ success: true })) - expect(warn.mock.calls).toHaveLength(3) - expect(error.mock.calls).toHaveLength(0) + expect(vi.mocked(warn).mock.calls).toHaveLength(3) + expect(vi.mocked(error).mock.calls).toHaveLength(0) }) it('should manage hub commands', async () => { @@ -405,7 +407,7 @@ describe('webdriver request', () => { it('should throw if timeout happens too often', async () => { const retryCnt = 3 const req = new BrowserRequest('POST', '/timeout', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -425,7 +427,7 @@ describe('webdriver request', () => { it('should return proper response if retry passes', async () => { const retryCnt = 7 const req = new BrowserRequest('POST', '/timeout', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -445,7 +447,7 @@ describe('webdriver request', () => { it('should retry on connection refused error', async () => { const retryCnt = 7 const req = new BrowserRequest('POST', '/connectionRefused', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -480,7 +482,7 @@ describe('webdriver request', () => { it('should correctly handle username and password options', async () => { const expectedResponse = { value: { 'element-6066-11e4-a52e-4f735466cecf': 'some-elem-123' } } - const req = new BrowserRequest('POST', path, {}) + const req = new BrowserRequest('POST', webdriverPath, {}) const opts = Object.assign( req.defaultOptions, @@ -490,7 +492,10 @@ describe('webdriver request', () => { const res = await req['_request'](opts) expect(res).toEqual(expectedResponse) - expect(ky.mock.calls[0][1].headers.Authorization).toEqual('Basic Zm9vOmJhcg==') + expect(ky).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ headers: { Authorization: 'Basic Zm9vOmJhcg==' } }) + ) }) }) @@ -498,8 +503,8 @@ describe('webdriver request', () => { // @ts-ignore ky.retryCnt = 0 - ky.mockClear() - warn.mockClear() - error.mockClear() + vi.mocked(ky).mockClear() + vi.mocked(warn).mockClear() + vi.mocked(error).mockClear() }) }) diff --git a/packages/webdriver/tests/request.test.ts b/packages/webdriver/tests/request.test.ts index e6b73ad48..b8ad61679 100644 --- a/packages/webdriver/tests/request.test.ts +++ b/packages/webdriver/tests/request.test.ts @@ -1,21 +1,20 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' + +import path from 'node:path' +import http from 'node:http' +import https from 'node:https' +import got, { Agents } from 'got' import logger from '@wdio/logger' -import * as gotOrig from 'got' -import http from 'http' -import https from 'https' +import { Options } from '@wdio/types' import * as utils from '../src/utils' -import { Options } from '@wdio/types' import WebDriverRequest from '../src/request/node' -type LogMock = { - warn: jest.Mock, - error: jest.Mock -} +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('got') +const { warn, error } = logger('test') -const { warn, error } = logger('test') as unknown as LogMock -const got = gotOrig.default as unknown as jest.Mock - -const path = '/session' +const webdriverPath = '/session' const defaultOptions = { protocol: 'http', hostname: 'localhost', @@ -24,7 +23,7 @@ const defaultOptions = { describe('webdriver request', () => { beforeEach(() => { - got.mockClear() + vi.mocked(got).mockClear() }) it('should have some default options', () => { @@ -33,24 +32,27 @@ describe('webdriver request', () => { expect(req.endpoint).toBe('/foo/bar') }) - it('should be able to make request', () => { + it('should be able to make request', async () => { const req = new WebDriverRequest('POST', '/foo/bar', { foo: 'bar' }) - req['_createOptions'] = jest.fn().mockImplementation((opts, sessionId) => ({ + req['_createOptions'] = vi.fn().mockImplementation((opts, sessionId) => ({ foo: 'bar', sessionId })) - req.emit = jest.fn() - req['_request'] = jest.fn() + req.emit = vi.fn() + req['_request'] = vi.fn() - req.makeRequest({ connectionRetryCount: 43, logLevel: 'warn' }, 'some_id') - expect((req['_request'] as jest.Mock).mock.calls[0][0].foo).toBe('bar') - expect((req['_request'] as jest.Mock).mock.calls[0][0].sessionId).toBe('some_id') - expect((req['_request'] as jest.Mock).mock.calls[0][2]).toBe(43) + await req.makeRequest({ connectionRetryCount: 43, logLevel: 'warn' }, 'some_id') + expect(req['_request']).toHaveBeenCalledWith( + expect.objectContaining({ foo: 'bar', sessionId: 'some_id' }), + undefined, + 43, + 0 + ) }) - it('should pick up the fullRequestOptions returned by transformRequest', async () => { + it.only('should pick up the fullRequestOptions returned by transformRequest', async () => { const req = new WebDriverRequest('POST', '/foo/bar', { foo: 'bar' }) - const transformRequest = jest.fn().mockImplementation((requestOptions) => ({ + const transformRequest = vi.fn().mockImplementation((requestOptions) => ({ ...requestOptions, json: { foo: 'baz' } })) @@ -63,19 +65,21 @@ describe('webdriver request', () => { path: '/wd/hub/', logLevel: 'warn' }, 'some_id') - - expect(got.mock.calls[0][1].json).toEqual({ foo: 'baz' }) + expect(got).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ json: { foo: 'baz' } }) + ) }) it('should resolve with the body returned by transformResponse', async () => { const req = new WebDriverRequest('POST', 'session/:sessionId/element', { foo: 'requestBody' }) - const transformResponse = jest.fn().mockImplementation((response) => ({ + const transformResponse = vi.fn().mockImplementation((response) => ({ ...response, body: { value: { foo: 'transformedResponse' } }, })) - got.mockClear() + vi.mocked(got).mockClear() const responseBody = await req.makeRequest({ transformResponse, protocol: 'https', @@ -88,18 +92,18 @@ describe('webdriver request', () => { expect(transformResponse.mock.calls[0][0]).toHaveProperty('body') expect(transformResponse.mock.calls[0][1].json).toEqual({ foo: 'requestBody' }) await expect(responseBody).toEqual({ value: { foo: 'transformedResponse' } }) - got.mockClear() + vi.mocked(got).mockClear() }) describe('createOptions', () => { it('fails if command requires sessionId but none given', () => { - const req = new WebDriverRequest('POST', `${path}/:sessionId/element`, {}) + const req = new WebDriverRequest('POST', `${webdriverPath}/:sessionId/element`, {}) expect(() => req['_createOptions']({ logLevel: 'warn' })).toThrow() }) - it('creates proper options set', () => { - const req = new WebDriverRequest('POST', `${path}/:sessionId/element`, {}) - const options: gotOrig.Options = req['_createOptions']({ + it('creates proper options set', async () => { + const req = new WebDriverRequest('POST', `${webdriverPath}/:sessionId/element`, {}) + const options: Options.RequestLibOptions = await req['_createOptions']({ protocol: 'https', hostname: 'localhost', port: 4445, @@ -120,13 +124,13 @@ describe('webdriver request', () => { expect(options.timeout).toBe(10 * 1000) }) - it('passes a custom agent', () => { - const req = new WebDriverRequest('POST', `${path}/:sessionId/element`, {}) + it('passes a custom agent', async () => { + const req = new WebDriverRequest('POST', `${webdriverPath}/:sessionId/element`, {}) const agent = { http: new http.Agent({ keepAlive: true }), https: new https.Agent({ keepAlive: true }) } - const options = req['_createOptions']({ + const options = await req['_createOptions']({ protocol: 'https', hostname: 'localhost', port: 4445, @@ -135,13 +139,13 @@ describe('webdriver request', () => { agent }, 'foobar12345') - expect((options.agent! as gotOrig.Agents).http).toBe(agent.http) - expect((options.agent! as gotOrig.Agents).https).toBe(agent.https) + expect((options.agent! as Agents).http).toBe(agent.http) + expect((options.agent! as Agents).https).toBe(agent.https) }) - it('ignors path when command is a hub command', () => { + it('ignors path when command is a hub command', async () => { const req = new WebDriverRequest('POST', '/grid/api/hub', {}, true) - const options = req['_createOptions']({ + const options = await req['_createOptions']({ protocol: 'https', hostname: 'localhost', port: 4445, @@ -151,9 +155,9 @@ describe('webdriver request', () => { expect((options.url as URL).href).toBe('https://localhost:4445/grid/api/hub') }) - it('should add auth if user and key is given', () => { - const req = new WebDriverRequest('POST', path, { some: 'body' }) - const options = req['_createOptions']({ + it('should add auth if user and key is given', async () => { + const req = new WebDriverRequest('POST', webdriverPath, { some: 'body' }) + const options = await req['_createOptions']({ ...defaultOptions, user: 'foo', key: 'bar', @@ -165,9 +169,9 @@ describe('webdriver request', () => { expect(options.json).toEqual({ some: 'body' }) }) - it('sets request body to "undefined" when request object is empty and DELETE is used', () => { - const req = new WebDriverRequest('DELETE', path, {}) - const options = req['_createOptions']({ + it('sets request body to "undefined" when request object is empty and DELETE is used', async () => { + const req = new WebDriverRequest('DELETE', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -175,9 +179,9 @@ describe('webdriver request', () => { expect(Boolean(options.json)).toEqual(false) }) - it('sets request body to "undefined" when request object is empty and GET is used', () => { - const req = new WebDriverRequest('GET', `${path}/title`, {}) - const options = req['_createOptions']({ + it('sets request body to "undefined" when request object is empty and GET is used', async () => { + const req = new WebDriverRequest('GET', `${webdriverPath}/title`, {}) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -185,9 +189,9 @@ describe('webdriver request', () => { expect(Boolean(options.json)).toEqual(false) }) - it('should attach an empty object body when POST is used', () => { + it('should attach an empty object body when POST is used', async () => { const req = new WebDriverRequest('POST', '/status', {}) - const options = req['_createOptions']({ + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -195,9 +199,9 @@ describe('webdriver request', () => { expect(options.json).toEqual({}) }) - it('should add the Content-Length header when a request object has a body', () => { - const req = new WebDriverRequest('POST', path, { foo: 'bar' }) - const options = req['_createOptions']({ + it('should add the Content-Length header when a request object has a body', async () => { + const req = new WebDriverRequest('POST', webdriverPath, { foo: 'bar' }) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', logLevel: 'warn' @@ -207,9 +211,9 @@ describe('webdriver request', () => { expect((options.headers as Record)['Content-Length']).toBe('13') }) - it('should add Content-Length as well any other header provided in the request options if there is body in the request object', () => { - const req = new WebDriverRequest('POST', path, { foo: 'bar' }) - const options = req['_createOptions']({ + it('should add Content-Length as well any other header provided in the request options if there is body in the request object', async () => { + const req = new WebDriverRequest('POST', webdriverPath, { foo: 'bar' }) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', headers: { foo: 'bar' }, logLevel: 'warn' @@ -220,9 +224,9 @@ describe('webdriver request', () => { expect(headers['Content-Length']).toBe('13') }) - it('should add only the headers provided if the request body is empty', () => { - const req = new WebDriverRequest('POST', path) - const options = req['_createOptions']({ + it('should add only the headers provided if the request body is empty', async () => { + const req = new WebDriverRequest('POST', webdriverPath) + const options = await req['_createOptions']({ ...defaultOptions, path: '/', headers: { foo: 'bar' }, @@ -246,64 +250,64 @@ describe('webdriver request', () => { delete process.env.strict_ssl }) - it('should contain key "rejectUnauthorized" with value "false" when "strictSSL" argument is given with false', () => { - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions']({ ...defaults, strictSSL: false }) + it('should contain key "rejectUnauthorized" with value "false" when "strictSSL" argument is given with false', async () => { + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaults, strictSSL: false }) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "false" when environment variable "STRICT_SSL" is defined with value "false"', () => { + it('should contain key "rejectUnauthorized" with value "false" when environment variable "STRICT_SSL" is defined with value "false"', async () => { process.env['STRICT_SSL'] = 'false' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "false" when environment variable "strict_ssl" is defined with value "false"', () => { + it('should contain key "rejectUnauthorized" with value "false" when environment variable "strict_ssl" is defined with value "false"', async () => { process.env['strict_ssl'] = 'false' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(false) }) - it('should contain key "rejectUnauthorized" with value "true" when "strictSSL" argument is given with true', () => { - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions']({ ...defaults, strictSSL: true }) + it('should contain key "rejectUnauthorized" with value "true" when "strictSSL" argument is given with true', async () => { + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions']({ ...defaults, strictSSL: true }) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with value "true"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with value "true"', async () => { process.env['STRICT_SSL'] = 'true' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with value "true"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with value "true"', async () => { process.env['strict_ssl'] = 'true' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" / "strict_ssl" is not defined', () => { - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" / "strict_ssl" is not defined', async () => { + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with any other value than "false"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "STRICT_SSL" is defined with any other value than "false"', async () => { process.env['STRICT_SSL'] = 'foo' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) - it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with any other value than "false"', () => { + it('should contain key "rejectUnauthorized" with value "true" when environment variable "strict_ssl" is defined with any other value than "false"', async () => { process.env['strict_ssl'] = 'foo' - const req = new WebDriverRequest('POST', path, {}) - const options = req['_createOptions'](defaults) + const req = new WebDriverRequest('POST', webdriverPath, {}) + const options = await req['_createOptions'](defaults) expect(options.https?.rejectUnauthorized).toEqual(true) }) }) @@ -312,8 +316,8 @@ describe('webdriver request', () => { describe('_request', () => { it('should make a request', async () => { const expectedResponse = { value: { 'element-6066-11e4-a52e-4f735466cecf': 'some-elem-123' } } - const req = new WebDriverRequest('POST', path, {}) - req.emit = jest.fn() + const req = new WebDriverRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign( req.defaultOptions, @@ -322,7 +326,7 @@ describe('webdriver request', () => { const res = await req['_request'](opts) expect(res).toEqual(expectedResponse) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', { result: expectedResponse }) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ request: opts, @@ -334,7 +338,7 @@ describe('webdriver request', () => { it('should short circuit if request throws a stale element exception', async () => { const req = new WebDriverRequest('POST', 'session/:sessionId/element', {}) - req.emit = jest.fn() + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/session/foobar-123/element/some-sub-sub-elem-231/click' }, @@ -343,50 +347,50 @@ describe('webdriver request', () => { const error = await req['_request'](opts).catch(err => err) expect(error.message).toBe('element is not attached to the page document') - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(1) - expect(warn.mock.calls).toEqual([['Request encountered a stale element - terminating request']]) + expect(vi.mocked(warn).mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toEqual([['Request encountered a stale element - terminating request']]) }) it('should not fail code due to an empty server response', async () => { - const req = new WebDriverRequest('POST', path, {}) - req.emit = jest.fn() + const req = new WebDriverRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/empty' } }) await expect(req['_request'](opts)).rejects.toEqual(new Error('Response has empty body')) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(2) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(2) expect(req.emit).toHaveBeenNthCalledWith(1, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(0) - expect(error.mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toHaveLength(0) + expect(vi.mocked(error).mock.calls).toHaveLength(1) }) it('should retry requests but still fail', async () => { - const req = new WebDriverRequest('POST', path, {}) - req.emit = jest.fn() + const req = new WebDriverRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/failing' } }) await expect(req['_request'](opts, undefined, 2)).rejects.toEqual(new Error('unknown error')) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(6) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(6) expect(req.emit).toHaveBeenNthCalledWith(1, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(3, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(4, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(5, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(6, 'performance', expect.objectContaining({ success: false })) - expect(warn.mock.calls).toHaveLength(2) - expect(error.mock.calls).toHaveLength(1) + expect(vi.mocked(warn).mock.calls).toHaveLength(2) + expect(vi.mocked(error).mock.calls).toHaveLength(1) }) it('should retry and eventually respond', async () => { - const req = new WebDriverRequest('POST', path, {}) - req.emit = jest.fn() + const req = new WebDriverRequest('POST', webdriverPath, {}) + req.emit = vi.fn() const opts = Object.assign(req.defaultOptions, { url: { pathname: '/failing' }, json: { foo: 'bar' } }) expect(await req['_request'](opts, undefined, 3)).toEqual({ value: 'caught' }) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(8) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(8) expect(req.emit).toHaveBeenNthCalledWith(1, 'retry', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(3, 'retry', expect.anything()) @@ -395,8 +399,8 @@ describe('webdriver request', () => { expect(req.emit).toHaveBeenNthCalledWith(6, 'performance', expect.objectContaining({ success: false })) expect(req.emit).toHaveBeenNthCalledWith(7, 'response', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(8, 'performance', expect.objectContaining({ success: true })) - expect(warn.mock.calls).toHaveLength(3) - expect(error.mock.calls).toHaveLength(0) + expect(vi.mocked(warn).mock.calls).toHaveLength(3) + expect(vi.mocked(error).mock.calls).toHaveLength(0) }) it('should manage hub commands', async () => { @@ -429,7 +433,7 @@ describe('webdriver request', () => { it('should throw if timeout happens too often', async () => { const retryCnt = 3 const req = new WebDriverRequest('POST', '/timeout', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -448,10 +452,10 @@ describe('webdriver request', () => { it('should use error from "getTimeoutError" helper', async () => { const timeoutErr = new Error('Timeout') - const spy = jest.spyOn(utils, 'getTimeoutError').mockReturnValue(timeoutErr) + const spy = vi.spyOn(utils, 'getTimeoutError').mockReturnValue(timeoutErr) const req = new WebDriverRequest('GET', '/timeout', {}, true) - req.emit = jest.fn() + req.emit = vi.fn() const reqOpts = { protocol: 'https', hostname: 'localhost', @@ -464,7 +468,7 @@ describe('webdriver request', () => { expect(spy).toBeCalledTimes(1) expect(spy).toBeCalledWith(expect.any(Error), expect.objectContaining({ method: 'GET' })) - expect((req.emit as jest.Mock).mock.calls).toHaveLength(3) + expect(vi.mocked(req.emit).mock.calls).toHaveLength(3) expect(req.emit).toHaveBeenNthCalledWith(1, 'request', expect.anything()) expect(req.emit).toHaveBeenNthCalledWith(2, 'response', { error: timeoutErr }) expect(req.emit).toHaveBeenNthCalledWith(3, 'performance', expect.objectContaining({ success: false })) @@ -476,7 +480,7 @@ describe('webdriver request', () => { it('should return proper response if retry passes', async () => { const retryCnt = 7 const req = new WebDriverRequest('POST', '/timeout', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -496,7 +500,7 @@ describe('webdriver request', () => { it('should retry on connection refused error', async () => { const retryCnt = 7 const req = new WebDriverRequest('POST', '/connectionRefused', {}, true) - const reqRetryCnt = jest.fn() + const reqRetryCnt = vi.fn() req.on('retry', reqRetryCnt) const result = await req.makeRequest({ protocol: 'https', @@ -534,8 +538,8 @@ describe('webdriver request', () => { // @ts-ignore got.retryCnt = 0 - got.mockClear() - warn.mockClear() - error.mockClear() + vi.mocked(got).mockClear() + vi.mocked(warn).mockClear() + vi.mocked(error).mockClear() }) }) diff --git a/packages/webdriver/tests/utils.test.ts b/packages/webdriver/tests/utils.test.ts index 7a54c78d5..f6bb02298 100644 --- a/packages/webdriver/tests/utils.test.ts +++ b/packages/webdriver/tests/utils.test.ts @@ -1,4 +1,5 @@ -import { URL } from 'url' +import { URL } from 'node:url' +import { describe, it, expect, MockedFunction, vi } from 'vitest' import { Capabilities, Options } from '@wdio/types' import { transformCommandLogResult } from '@wdio/utils' import { @@ -9,6 +10,9 @@ import { } from '../src/utils' import type { Client } from '../src/types' +vi.mock('@wdio/utils') +vi.mock('got') + describe('utils', () => { it('isSuccessfulResponse', () => { expect(isSuccessfulResponse(200, { value: { some: 'result' } })).toBe(true) @@ -176,25 +180,28 @@ describe('utils', () => { describe('setupDirectConnect', () => { class TestClient implements Client { - sessionId: string - capabilities: Capabilities.DesiredCapabilities | Capabilities.W3CCapabilities - requestedCapabilities: Capabilities.DesiredCapabilities | Capabilities.W3CCapabilities - options: Options.WebDriver + // @ts-expect-error + sessionId?: string + // @ts-expect-error + requestedCapabilities?: Capabilities.DesiredCapabilities | Capabilities.W3CCapabilities - constructor(capabilities, options) { + constructor( + public capabilities: Capabilities.DesiredCapabilities | Capabilities.W3CCapabilities, + public options: Options.WebDriver + ) { this.capabilities = capabilities this.options = options } } it('should do nothing if params contain no direct connect caps', function () { - const client = new TestClient({ platformName: 'baz' }, { hostname: 'bar' }) as Client + const client = new TestClient({ platformName: 'baz' }, { hostname: 'bar' } as any) as Client setupDirectConnect(client) expect(client.options.hostname).toEqual('bar') }) it('should do nothing if params contain incomplete direct connect caps', function () { - const client = new TestClient({ platformName: 'baz', directConnectHost: 'baz' }, { hostname: 'bar' }) as Client + const client = new TestClient({ platformName: 'baz', directConnectHost: 'baz' }, { hostname: 'bar' } as any) as Client setupDirectConnect(client) expect(client.options.hostname).toEqual('bar') }) @@ -211,7 +218,7 @@ describe('utils', () => { hostname: 'foo', port: 1234, path: '' - }) as Client + } as any) as Client setupDirectConnect(client) expect(client.options.protocol).toBe('https') expect(client.options.hostname).toBe('bar') @@ -231,7 +238,7 @@ describe('utils', () => { hostname: 'foo', port: 1234, path: '' - }) as Client + } as any) as Client setupDirectConnect(client) expect(client.options.protocol).toBe('https') expect(client.options.hostname).toBe('bar') @@ -327,7 +334,7 @@ describe('utils', () => { .toBe('mockBrowser') }) - it('should handle sessionRequest error', async () => { + it.only('should handle sessionRequest error', async () => { let error = await startWebDriverSession({ logLevel: 'warn', capabilities: {} @@ -409,7 +416,7 @@ describe('utils', () => { }) it('command args with base64 script', async () => { - (transformCommandLogResult as jest.Mock).mockReturnValueOnce('""') + (transformCommandLogResult as MockedFunction).mockReturnValueOnce('""') const err = new Error('Timeout') const cmdArgs = { script: Buffer.from('script').toString('base64') } @@ -435,7 +442,7 @@ describe('utils', () => { }) it('command args with base64 screenshot', async () => { - (transformCommandLogResult as jest.Mock).mockReturnValueOnce('""') + (transformCommandLogResult as MockedFunction).mockReturnValueOnce('""') const err = new Error('Timeout') const cmdArgs = { file: Buffer.from('screen').toString('base64') } diff --git a/packages/webdriverio/tests/__mocks__/archiver.ts b/packages/webdriverio/tests/__mocks__/archiver.ts deleted file mode 100644 index b313d5e48..000000000 --- a/packages/webdriverio/tests/__mocks__/archiver.ts +++ /dev/null @@ -1,9 +0,0 @@ -const mock = {} -mock.on = jest.fn().mockReturnValue(mock) -mock.append = jest.fn().mockReturnValue(mock) -mock.finalize = jest.fn().mockReturnValue(mock) - -export default (...args) => { - mock.args = args - return mock -} diff --git a/packages/webdriverio/tests/__mocks__/fs.ts b/packages/webdriverio/tests/__mocks__/fs.ts deleted file mode 100644 index 6c5589748..000000000 --- a/packages/webdriverio/tests/__mocks__/fs.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type * as fsType from 'fs' - -const fs = jest.genMockFromModule('fs') -fs.writeFileSync = jest.fn() -fs.existsSync = jest.fn(() => true) -module.exports = fs diff --git a/packages/webdriverio/tests/__mocks__/get-port.ts b/packages/webdriverio/tests/__mocks__/get-port.ts deleted file mode 100644 index 4265e31d5..000000000 --- a/packages/webdriverio/tests/__mocks__/get-port.ts +++ /dev/null @@ -1 +0,0 @@ -export default jest.fn().mockReturnValue(Promise.resolve(9222)) diff --git a/vitest.config.ts b/vitest.config.ts index 68a6276d2..f6cc8c6c0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,7 +5,8 @@ export default defineConfig({ include: [ 'packages/wdio-logger/**/*.test.ts', 'packages/wdio-utils/**/*.test.ts', - 'packages/wdio-config/**/*.test.ts' + 'packages/wdio-config/**/*.test.ts', + 'packages/webdriver/**/*.test.ts' ], coverage: { enabled: false,