From 3c541d3892bd967397a16d049a6864031ce04195 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 31 May 2022 10:24:40 +0000 Subject: [PATCH] rewrite @wdio/cli into ESM --- __mocks__/@wdio/utils.ts | 7 +- __mocks__/async-exit-hook.ts | 2 + __mocks__/chokidar.ts | 6 + __mocks__/ejs.ts | 4 + .../tests/__mocks__ => __mocks__}/inquirer.ts | 3 +- __mocks__/log-update.ts | 5 + __mocks__/recursive-readdir.ts | 2 + __mocks__/yargs/yargs.ts | 14 + __mocks__/yarn-install.ts | 2 + packages/wdio-cli/package.json | 21 +- packages/wdio-cli/src/commands/config.ts | 16 +- packages/wdio-cli/src/commands/install.ts | 10 +- packages/wdio-cli/src/commands/repl.ts | 11 +- packages/wdio-cli/src/commands/run.ts | 17 +- packages/wdio-cli/src/constants.ts | 9 +- packages/wdio-cli/src/index.ts | 10 +- packages/wdio-cli/src/interface.ts | 4 +- packages/wdio-cli/src/launcher.ts | 6 +- packages/wdio-cli/src/types.ts | 4 +- packages/wdio-cli/src/utils.ts | 14 +- packages/wdio-cli/src/watcher.ts | 6 +- packages/wdio-cli/tests/.eslintrc | 5 - .../tests/__mocks__/async-exit-hook.ts | 1 - packages/wdio-cli/tests/__mocks__/chokidar.ts | 5 - packages/wdio-cli/tests/__mocks__/ejs.ts | 3 - .../wdio-cli/tests/__mocks__/log-update.ts | 4 - .../tests/__mocks__/recursive-readdir.ts | 1 - .../wdio-cli/tests/__mocks__/yargs/yargs.ts | 13 - .../wdio-cli/tests/__mocks__/yarn-install.ts | 1 - .../tests/__snapshots__/utils.test.ts.snap | 54 +-- .../__snapshots__/config.test.ts.snap | 385 ++++++++---------- .../wdio-cli/tests/commands/config.test.ts | 139 ++++--- .../wdio-cli/tests/commands/install.test.ts | 72 ++-- packages/wdio-cli/tests/commands/repl.test.ts | 19 +- packages/wdio-cli/tests/commands/run.test.ts | 61 +-- packages/wdio-cli/tests/config.test.ts | 1 + packages/wdio-cli/tests/index.test.ts | 34 +- packages/wdio-cli/tests/interface.test.ts | 46 ++- packages/wdio-cli/tests/launcher.test.ts | 194 ++++----- packages/wdio-cli/tests/run.test.ts | 21 +- packages/wdio-cli/tests/utils.test.ts | 170 ++++---- packages/wdio-cli/tests/watcher.test.ts | 133 +++--- vitest.config.ts | 1 - 43 files changed, 794 insertions(+), 742 deletions(-) create mode 100644 __mocks__/async-exit-hook.ts create mode 100644 __mocks__/chokidar.ts create mode 100644 __mocks__/ejs.ts rename {packages/wdio-cli/tests/__mocks__ => __mocks__}/inquirer.ts (77%) create mode 100644 __mocks__/log-update.ts create mode 100644 __mocks__/recursive-readdir.ts create mode 100644 __mocks__/yargs/yargs.ts create mode 100644 __mocks__/yarn-install.ts delete mode 100644 packages/wdio-cli/tests/.eslintrc delete mode 100644 packages/wdio-cli/tests/__mocks__/async-exit-hook.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/chokidar.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/ejs.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/log-update.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/recursive-readdir.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/yargs/yargs.ts delete mode 100644 packages/wdio-cli/tests/__mocks__/yarn-install.ts diff --git a/__mocks__/@wdio/utils.ts b/__mocks__/@wdio/utils.ts index 3301e3e87..43d614612 100644 --- a/__mocks__/@wdio/utils.ts +++ b/__mocks__/@wdio/utils.ts @@ -24,7 +24,10 @@ class DotReporter { } } -class RunnerMock {} +class RunnerMock { + shutdown = vi.fn() + initialise = vi.fn() +} class FoobarServiceMock { beforeSuite () {} afterCommand () {} @@ -66,7 +69,7 @@ const pluginMocks = { } export const initialisePlugin = vi.fn().mockImplementation( - (name: keyof typeof frameworkMocks, type: keyof typeof pluginMocks) => ( + async (name: keyof typeof frameworkMocks, type: keyof typeof pluginMocks) => ( { default: (pluginMocks[type] as any)[name] } ) ) diff --git a/__mocks__/async-exit-hook.ts b/__mocks__/async-exit-hook.ts new file mode 100644 index 000000000..8b6d68c97 --- /dev/null +++ b/__mocks__/async-exit-hook.ts @@ -0,0 +1,2 @@ +import { vi } from 'vitest' +export default vi.fn() diff --git a/__mocks__/chokidar.ts b/__mocks__/chokidar.ts new file mode 100644 index 000000000..449f98530 --- /dev/null +++ b/__mocks__/chokidar.ts @@ -0,0 +1,6 @@ +import { vi } from 'vitest' +const chokidar: any = {} +chokidar.watch = vi.fn().mockReturnValue(chokidar) +chokidar.on =vi.fn().mockReturnValue(chokidar) + +export default chokidar diff --git a/__mocks__/ejs.ts b/__mocks__/ejs.ts new file mode 100644 index 000000000..61c576ec2 --- /dev/null +++ b/__mocks__/ejs.ts @@ -0,0 +1,4 @@ +import { vi } from 'vitest' +export default { + renderFile: vi.fn().mockImplementation((...args: any[]) => args.pop()()) +} diff --git a/packages/wdio-cli/tests/__mocks__/inquirer.ts b/__mocks__/inquirer.ts similarity index 77% rename from packages/wdio-cli/tests/__mocks__/inquirer.ts rename to __mocks__/inquirer.ts index 4a8043704..c14cb6933 100644 --- a/packages/wdio-cli/tests/__mocks__/inquirer.ts +++ b/__mocks__/inquirer.ts @@ -1,5 +1,6 @@ +import { vi } from 'vitest' export default { - prompt: jest.fn().mockReturnValue(Promise.resolve({ + prompt: vi.fn().mockReturnValue(Promise.resolve({ runner: '@wdio/local-runner--$local', framework: '@wdio/mocha-framework$--$mocha', reporters: ['@wdio/spec-reporter$--$spec'], diff --git a/__mocks__/log-update.ts b/__mocks__/log-update.ts new file mode 100644 index 000000000..312a9f667 --- /dev/null +++ b/__mocks__/log-update.ts @@ -0,0 +1,5 @@ +import { vi } from 'vitest' +const logUpdateMock: any = vi.fn() +logUpdateMock.clear = vi.fn() + +export default logUpdateMock diff --git a/__mocks__/recursive-readdir.ts b/__mocks__/recursive-readdir.ts new file mode 100644 index 000000000..8b6d68c97 --- /dev/null +++ b/__mocks__/recursive-readdir.ts @@ -0,0 +1,2 @@ +import { vi } from 'vitest' +export default vi.fn() diff --git a/__mocks__/yargs/yargs.ts b/__mocks__/yargs/yargs.ts new file mode 100644 index 000000000..fe18d1ac2 --- /dev/null +++ b/__mocks__/yargs/yargs.ts @@ -0,0 +1,14 @@ +import { vi } from 'vitest' +const yargs: any = {} +yargs.commandDir = vi.fn(() => yargs) +yargs.example = vi.fn(() => yargs) +yargs.argv = { _: ['wdio.conf.js'] } +yargs.epilogue = vi.fn().mockReturnValue(yargs) +yargs.options = vi.fn(() => yargs) +yargs.updateStrings = vi.fn(() => yargs) +yargs.parse = vi.fn().mockReturnValue(yargs.argv) +yargs.env = vi.fn().mockReturnValue(yargs) +yargs.help = vi.fn().mockReturnValue(yargs) + +export default vi.fn().mockReturnValue(yargs) +export { yargs } diff --git a/__mocks__/yarn-install.ts b/__mocks__/yarn-install.ts new file mode 100644 index 000000000..2e74be96a --- /dev/null +++ b/__mocks__/yarn-install.ts @@ -0,0 +1,2 @@ +import { vi } from 'vitest' +export default vi.fn().mockReturnValue({ status: 0 }) diff --git a/packages/wdio-cli/package.json b/packages/wdio-cli/package.json index ce9d73e27..8dac1d7e7 100644 --- a/packages/wdio-cli/package.json +++ b/packages/wdio-cli/package.json @@ -5,12 +5,11 @@ "author": "Christian Bromann ", "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-cli", "license": "MIT", - "main": "./build/index", "bin": { "wdio": "./bin/wdio.js" }, "engines": { - "node": "^16.13 || >=18" + "node": "^16.13 || >=18" }, "scripts": { "copy": "copyfiles -u 1 -V \"src/templates/**/*\" ./build/" @@ -28,6 +27,20 @@ "bugs": { "url": "https://github.com/webdriverio/webdriverio/issues" }, + "main": "./build/index.cjs.js", + "type": "module", + "module": "./build/index.js", + "exports": { + ".": [ + { + "import": "./build/index.js", + "require": "./build/index.cjs.js" + }, + "./build/index.cjs.js" + ] + }, + "types": "./build/index.d.ts", + "typeScriptVersion": "3.8.3", "dependencies": { "@types/ejs": "^3.0.5", "@types/fs-extra": "^9.0.4", @@ -61,5 +74,7 @@ "publishConfig": { "access": "public" }, - "types": "./build/index.d.ts" + "devDependencies": { + "@types/yargs": "^17.0.10" + } } diff --git a/packages/wdio-cli/src/commands/config.ts b/packages/wdio-cli/src/commands/config.ts index 5ee9dfcc5..28984e44c 100644 --- a/packages/wdio-cli/src/commands/config.ts +++ b/packages/wdio-cli/src/commands/config.ts @@ -3,22 +3,21 @@ import util from 'node:util' import fs from 'fs-extra' import inquirer from 'inquirer' import yarnInstall from 'yarn-install' +import type yargs from 'yargs' import { CONFIG_HELPER_INTRO, CLI_EPILOGUE, COMPILER_OPTIONS, TS_COMPILER_INSTRUCTIONS, SUPPORTED_PACKAGES, CONFIG_HELPER_SUCCESS_MESSAGE, - DEPENDENCIES_INSTALLATION_MESSAGE -} from '../constants' + DEPENDENCIES_INSTALLATION_MESSAGE, + pkg +} from '../constants.js' import { addServiceDeps, convertPackageHashToObject, renderConfigurationFile, hasFile, generateTestFiles, getAnswers, getPathForFileGeneration, hasPackage -} from '../utils' -import { ConfigCommandArguments, ParsedAnswers } from '../types' -import yargs from 'yargs' - -const pkg = require('../../package.json') +} from '../utils.js' +import type { ConfigCommandArguments, ParsedAnswers } from '../types' export const command = 'config' export const desc = 'Initialize WebdriverIO and setup configuration in your current project.' @@ -45,6 +44,8 @@ export const builder = (yargs: yargs.Argv) => { } const runConfig = async function (useYarn: boolean, yes: boolean, exit = false) { + console.info('1') + console.log(CONFIG_HELPER_INTRO) const answers = await getAnswers(yes) const frameworkPackage = convertPackageHashToObject(answers.framework) @@ -66,6 +67,7 @@ const runConfig = async function (useYarn: boolean, yes: boolean, exit = false) */ const parsedPaths = getPathForFileGeneration(answers) + console.info('2') const parsedAnswers: ParsedAnswers = { ...answers, diff --git a/packages/wdio-cli/src/commands/install.ts b/packages/wdio-cli/src/commands/install.ts index da4aeeaa0..9a75cf9e2 100644 --- a/packages/wdio-cli/src/commands/install.ts +++ b/packages/wdio-cli/src/commands/install.ts @@ -2,17 +2,17 @@ import path from 'node:path' import fs from 'fs-extra' import yarnInstall from 'yarn-install' +import type yargs from 'yargs' import { replaceConfig, findInConfig, addServiceDeps, convertPackageHashToObject -} from '../utils' -import { missingConfigurationPrompt } from './config' -import { InstallCommandArguments, SupportedPackage } from '../types' -import { SUPPORTED_PACKAGES, CLI_EPILOGUE } from '../constants' -import yargs from 'yargs' +} from '../utils.js' +import { missingConfigurationPrompt } from './config.js' +import { SUPPORTED_PACKAGES, CLI_EPILOGUE } from '../constants.js' +import type { InstallCommandArguments, SupportedPackage } from '../types' const supportedInstallations = { plugin: SUPPORTED_PACKAGES.plugin.map(({ value }) => convertPackageHashToObject(value)), diff --git a/packages/wdio-cli/src/commands/repl.ts b/packages/wdio-cli/src/commands/repl.ts index 02209f45c..cbdc94173 100644 --- a/packages/wdio-cli/src/commands/repl.ts +++ b/packages/wdio-cli/src/commands/repl.ts @@ -1,10 +1,11 @@ +import yargs from 'yargs' import pickBy from 'lodash.pickby' import { remote } from 'webdriverio' -import { cmdArgs as runCmdArgs } from './run' -import { getCapabilities } from '../utils' -import { ReplCommandArguments } from '../types' -import { CLI_EPILOGUE } from '../constants' -import yargs from 'yargs' + +import { cmdArgs as runCmdArgs } from './run.js' +import { getCapabilities } from '../utils.js' +import { CLI_EPILOGUE } from '../constants.js' +import type { ReplCommandArguments } from '../types' const IGNORED_ARGS = [ 'bail', 'framework', 'reporters', 'suite', 'spec', 'exclude', diff --git a/packages/wdio-cli/src/commands/run.ts b/packages/wdio-cli/src/commands/run.ts index 28a8961a0..1fa41ac24 100644 --- a/packages/wdio-cli/src/commands/run.ts +++ b/packages/wdio-cli/src/commands/run.ts @@ -1,14 +1,13 @@ import path from 'node:path' import fs from 'fs-extra' - -import { missingConfigurationPrompt } from './config' -import { RunCommandArguments } from '../types' - -import Launcher from '../launcher' -import Watcher from '../watcher' -import { CLI_EPILOGUE } from '../constants' import yargs from 'yargs' +import Launcher from '../launcher.js' +import Watcher from '../watcher.js' +import { missingConfigurationPrompt } from './config.js' +import { CLI_EPILOGUE } from '../constants.js' +import type { RunCommandArguments } from '../types' + export const command = 'run ' export const desc = 'Run your WDIO configuration file to initialize your tests. (default)' @@ -129,14 +128,14 @@ export function launch (wdioConfPath: string, params: Partial { /* istanbul ignore if */ - if (!process.env.JEST_WORKER_ID) { + if (!process.env.VITEST_WORKER_ID) { process.exit(...args) } }) .catch(err => { console.error(err) /* istanbul ignore if */ - if (!process.env.JEST_WORKER_ID) { + if (!process.env.VITEST_WORKER_ID) { process.exit(1) } }) diff --git a/packages/wdio-cli/src/constants.ts b/packages/wdio-cli/src/constants.ts index 9d092dbfa..9b12c6a42 100644 --- a/packages/wdio-cli/src/constants.ts +++ b/packages/wdio-cli/src/constants.ts @@ -1,7 +1,10 @@ -import { validateServiceAnswers, hasFile, getDefaultFiles } from './utils' -import { Questionnair } from './types' +import { createRequire } from 'node:module' -const pkg = require('../package.json') +import { validateServiceAnswers, hasFile, getDefaultFiles } from './utils.js' +import type { Questionnair } from './types' + +const require = createRequire(import.meta.url) +export const pkg = require('../package.json') export const CLI_EPILOGUE = `Documentation: https://webdriver.io\n@wdio/cli (v${pkg.version})` diff --git a/packages/wdio-cli/src/index.ts b/packages/wdio-cli/src/index.ts index 24c2148dd..39c84b2b4 100644 --- a/packages/wdio-cli/src/index.ts +++ b/packages/wdio-cli/src/index.ts @@ -4,10 +4,10 @@ import path from 'node:path' import yargs from 'yargs/yargs' import { hideBin } from 'yargs/helpers' -import Launcher from './launcher' -import { handler, cmdArgs } from './commands/run' -import { CLI_EPILOGUE } from './constants' -import { RunCommandArguments } from './types' +import Launcher from './launcher.js' +import { handler, cmdArgs } from './commands/run.js' +import { CLI_EPILOGUE } from './constants.js' +import type { RunCommandArguments } from './types' const DEFAULT_CONFIG_FILENAME = 'wdio.conf.js' const DESCRIPTION = [ @@ -82,4 +82,4 @@ export const run = async () => { } export default Launcher -export * from './types' +export * from './types.js' diff --git a/packages/wdio-cli/src/interface.ts b/packages/wdio-cli/src/interface.ts index dbeb08228..022fc6685 100644 --- a/packages/wdio-cli/src/interface.ts +++ b/packages/wdio-cli/src/interface.ts @@ -1,9 +1,9 @@ -import chalk from 'chalk' import { EventEmitter } from 'node:events' +import chalk from 'chalk' import logger from '@wdio/logger' import type { Options, Capabilities, Workers } from '@wdio/types' -import { getRunnerName, HookError } from './utils' +import { getRunnerName, HookError } from './utils.js' const log = logger('@wdio/cli') diff --git a/packages/wdio-cli/src/launcher.ts b/packages/wdio-cli/src/launcher.ts index e98647851..78e34fa15 100644 --- a/packages/wdio-cli/src/launcher.ts +++ b/packages/wdio-cli/src/launcher.ts @@ -7,9 +7,9 @@ import { ConfigParser } from '@wdio/config' import { initialisePlugin, initialiseLauncherService, sleep } from '@wdio/utils' import type { Options, Capabilities, Services } from '@wdio/types' -import CLInterface from './interface' -import { RunCommandArguments } from './types' -import { runLauncherHook, runOnCompleteHook, runServiceHook, HookError } from './utils' +import CLInterface from './interface.js' +import { runLauncherHook, runOnCompleteHook, runServiceHook, HookError } from './utils.js' +import type { RunCommandArguments } from './types' const log = logger('@wdio/cli:launcher') diff --git a/packages/wdio-cli/src/types.ts b/packages/wdio-cli/src/types.ts index 832245e72..1afba882d 100644 --- a/packages/wdio-cli/src/types.ts +++ b/packages/wdio-cli/src/types.ts @@ -1,5 +1,5 @@ -import { Options, Reporters } from '@wdio/types' -import { BACKEND_CHOICES, REGION_OPTION, COMPILER_OPTION_ANSWERS } from './constants' +import type { Options, Reporters } from '@wdio/types' +import type { BACKEND_CHOICES, REGION_OPTION, COMPILER_OPTION_ANSWERS } from './constants' type ValueOf = T[keyof T] diff --git a/packages/wdio-cli/src/utils.ts b/packages/wdio-cli/src/utils.ts index 23d8ee07b..28d243bd7 100644 --- a/packages/wdio-cli/src/utils.ts +++ b/packages/wdio-cli/src/utils.ts @@ -1,3 +1,9 @@ +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' +import { createRequire } from 'node:module' +import { execSync } from 'node:child_process' +import { promisify } from 'node:util' + import fs from 'fs-extra' import ejs from 'ejs' import path from 'node:path' @@ -6,16 +12,16 @@ import pickBy from 'lodash.pickby' import logger from '@wdio/logger' import readDir from 'recursive-readdir' import { SevereServiceError } from 'webdriverio' -import { execSync } from 'node:child_process' -import { promisify } from 'node:util' import { ConfigParser } from '@wdio/config' import { CAPABILITY_KEYS } from '@wdio/protocols' import type { Options, Capabilities, Services } from '@wdio/types' -import { ReplCommandArguments, Questionnair, SupportedPackage, OnCompleteResult, ParsedAnswers } from './types' -import { EXCLUSIVE_SERVICES, ANDROID_CONFIG, IOS_CONFIG, QUESTIONNAIRE } from './constants' +import { EXCLUSIVE_SERVICES, ANDROID_CONFIG, IOS_CONFIG, QUESTIONNAIRE } from './constants.js' +import type { ReplCommandArguments, Questionnair, SupportedPackage, OnCompleteResult, ParsedAnswers } from './types' +const require = createRequire(import.meta.url) const log = logger('@wdio/cli:utils') +const __dirname = dirname(fileURLToPath(import.meta.url)) const TEMPLATE_ROOT_DIR = path.join(__dirname, 'templates', 'exampleFiles') const renderFile = promisify(ejs.renderFile) as (path: string, data: Record) => Promise diff --git a/packages/wdio-cli/src/watcher.ts b/packages/wdio-cli/src/watcher.ts index 7786957a0..e5102b28a 100644 --- a/packages/wdio-cli/src/watcher.ts +++ b/packages/wdio-cli/src/watcher.ts @@ -3,10 +3,10 @@ import logger from '@wdio/logger' import pickBy from 'lodash.pickby' import flattenDeep from 'lodash.flattendeep' import union from 'lodash.union' - -import Launcher from './launcher' import type { Capabilities, Workers } from '@wdio/types' -import { RunCommandArguments, ValueKeyIteratee } from './types.js' + +import Launcher from './launcher.js' +import type { RunCommandArguments, ValueKeyIteratee } from './types' const log = logger('@wdio/cli:watch') diff --git a/packages/wdio-cli/tests/.eslintrc b/packages/wdio-cli/tests/.eslintrc deleted file mode 100644 index 55f121d15..000000000 --- a/packages/wdio-cli/tests/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "jest": true - } -} diff --git a/packages/wdio-cli/tests/__mocks__/async-exit-hook.ts b/packages/wdio-cli/tests/__mocks__/async-exit-hook.ts deleted file mode 100644 index bbabe6d0d..000000000 --- a/packages/wdio-cli/tests/__mocks__/async-exit-hook.ts +++ /dev/null @@ -1 +0,0 @@ -export default jest.fn() diff --git a/packages/wdio-cli/tests/__mocks__/chokidar.ts b/packages/wdio-cli/tests/__mocks__/chokidar.ts deleted file mode 100644 index 240a80657..000000000 --- a/packages/wdio-cli/tests/__mocks__/chokidar.ts +++ /dev/null @@ -1,5 +0,0 @@ -const chokidar = {} -chokidar.watch = jest.fn().mockReturnValue(chokidar) -chokidar.on =jest.fn().mockReturnValue(chokidar) - -export default chokidar diff --git a/packages/wdio-cli/tests/__mocks__/ejs.ts b/packages/wdio-cli/tests/__mocks__/ejs.ts deleted file mode 100644 index a288c8b36..000000000 --- a/packages/wdio-cli/tests/__mocks__/ejs.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default { - renderFile: jest.fn().mockImplementation((...args) => args.pop()()) -} diff --git a/packages/wdio-cli/tests/__mocks__/log-update.ts b/packages/wdio-cli/tests/__mocks__/log-update.ts deleted file mode 100644 index ea880ce59..000000000 --- a/packages/wdio-cli/tests/__mocks__/log-update.ts +++ /dev/null @@ -1,4 +0,0 @@ -const logUpdateMock = jest.fn() -logUpdateMock.clear = jest.fn() - -export default logUpdateMock diff --git a/packages/wdio-cli/tests/__mocks__/recursive-readdir.ts b/packages/wdio-cli/tests/__mocks__/recursive-readdir.ts deleted file mode 100644 index bbabe6d0d..000000000 --- a/packages/wdio-cli/tests/__mocks__/recursive-readdir.ts +++ /dev/null @@ -1 +0,0 @@ -export default jest.fn() diff --git a/packages/wdio-cli/tests/__mocks__/yargs/yargs.ts b/packages/wdio-cli/tests/__mocks__/yargs/yargs.ts deleted file mode 100644 index 7222ba621..000000000 --- a/packages/wdio-cli/tests/__mocks__/yargs/yargs.ts +++ /dev/null @@ -1,13 +0,0 @@ -const yargs: any = {} -yargs.commandDir = jest.fn(() => yargs) -yargs.example = jest.fn(() => yargs) -yargs.argv = { _: ['wdio.conf.js'] } -yargs.epilogue = jest.fn().mockReturnValue(yargs) -yargs.options = jest.fn(() => yargs) -yargs.updateStrings = jest.fn(() => yargs) -yargs.parse = jest.fn().mockReturnValue(yargs.argv) -yargs.env = jest.fn().mockReturnValue(yargs) -yargs.help = jest.fn().mockReturnValue(yargs) - -export default jest.fn().mockReturnValue(yargs) -export { yargs } diff --git a/packages/wdio-cli/tests/__mocks__/yarn-install.ts b/packages/wdio-cli/tests/__mocks__/yarn-install.ts deleted file mode 100644 index 83aff966a..000000000 --- a/packages/wdio-cli/tests/__mocks__/yarn-install.ts +++ /dev/null @@ -1 +0,0 @@ -export default jest.fn().mockReturnValue({ status: 0 }) diff --git a/packages/wdio-cli/tests/__snapshots__/utils.test.ts.snap b/packages/wdio-cli/tests/__snapshots__/utils.test.ts.snap index 3aedd622a..f297e9ae6 100644 --- a/packages/wdio-cli/tests/__snapshots__/utils.test.ts.snap +++ b/packages/wdio-cli/tests/__snapshots__/utils.test.ts.snap @@ -1,22 +1,8 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1 -exports[`getCapabilities should get capability from wdio.conf.js 1`] = ` -Object { - "capabilities": Object { - "acceptInsecureCerts": true, - "browserName": "chrome", - "goog:chromeOptions": Object { - "args": Array [ - "window-size=8000,1200", - ], - }, - }, -} -`; - -exports[`getCapabilities should return driver with capabilities for android 1`] = ` -Object { - "capabilities": Object { +exports[`getCapabilities > should return driver with capabilities for android 1`] = ` +{ + "capabilities": { "app": "foo.apk", "automationName": "UiAutomator2", "deviceName": "Test", @@ -27,9 +13,9 @@ Object { } `; -exports[`getCapabilities should return driver with capabilities for android 2`] = ` -Object { - "capabilities": Object { +exports[`getCapabilities > should return driver with capabilities for android 2`] = ` +{ + "capabilities": { "automationName": "UiAutomator2", "browserName": "Chrome", "deviceName": "Test", @@ -40,17 +26,17 @@ Object { } `; -exports[`getCapabilities should return driver with capabilities for desktop 1`] = ` -Object { - "capabilities": Object { +exports[`getCapabilities > should return driver with capabilities for desktop 1`] = ` +{ + "capabilities": { "browserName": "chrome", }, } `; -exports[`getCapabilities should return driver with capabilities for ios 1`] = ` -Object { - "capabilities": Object { +exports[`getCapabilities > should return driver with capabilities for ios 1`] = ` +{ + "capabilities": { "app": "foo.app", "automationName": "XCUITest", "deviceName": "fooName", @@ -61,9 +47,9 @@ Object { } `; -exports[`getCapabilities should return driver with capabilities for ios 2`] = ` -Object { - "capabilities": Object { +exports[`getCapabilities > should return driver with capabilities for ios 2`] = ` +{ + "capabilities": { "automationName": "XCUITest", "browserName": "Safari", "deviceName": "iPhone Simulator", @@ -74,10 +60,10 @@ Object { } `; -exports[`getCapabilities should through capability not found 1`] = `"No capability found in given config file with the provided capability indexed/named property: 5. Please check the capability in your wdio config file."`; +exports[`getCapabilities > should through capability not found 1`] = `"No capability found in given config file with the provided capability indexed/named property: 5. Please check the capability in your wdio config file."`; -exports[`getCapabilities should throw capability not provided 1`] = `"Please provide index/named property of capability to use from the capabilities array/object in wdio config file"`; +exports[`getCapabilities > should throw capability not provided 1`] = `"Please provide index/named property of capability to use from the capabilities array/object in wdio config file"`; -exports[`getCapabilities should throw config not found error 1`] = `"Config File not found: ./test.js"`; +exports[`getCapabilities > should throw config not found error 1`] = `"Config File not found: ./test.js"`; -exports[`getCapabilities should throw config not found error 2`] = `"Could not parse ./test.js, failed with error : ups"`; +exports[`getCapabilities > should throw config not found error 2`] = `"Could not parse ./test.js, failed with error : ups"`; diff --git a/packages/wdio-cli/tests/commands/__snapshots__/config.test.ts.snap b/packages/wdio-cli/tests/commands/__snapshots__/config.test.ts.snap index 288537e54..2b1771399 100644 --- a/packages/wdio-cli/tests/commands/__snapshots__/config.test.ts.snap +++ b/packages/wdio-cli/tests/commands/__snapshots__/config.test.ts.snap @@ -1,150 +1,15 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1 -exports[`install compliant NPM tag packages it should install tagged version if cli is tagged with beta 1`] = ` -Array [ - Array [ +exports[`install compliant NPM tag packages > it should install tagged version if cli is tagged with beta 1`] = ` +[ + [ " ========================= WDIO Configuration Helper ========================= ", ], - Array [ - " -Installing wdio packages: --", - "@wdio/local-runner@beta -- @wdio/mocha-framework@beta -- @wdio/crossbrowsertesting-service@beta -- wdio-lambdatest-service -- ts-node -- typescript", - ], - Array [ - " -Packages installed successfully, creating configuration file...", - ], - Array [ - "To have TypeScript support please add the following packages to your \\"types\\" list: -{ - \\"compilerOptions\\": { - \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] - } -} - -For for information on TypeScript integration check out: https://webdriver.io/docs/typescript -", - ], - Array [ - " -Configuration file was created successfully! -To run your tests, execute: -$ npx wdio run test/wdio.conf.ts -", - ], -] -`; - -exports[`install compliant NPM tag packages it should install tagged version if cli is tagged with latest 1`] = ` -Array [ - Array [ - " -========================= -WDIO Configuration Helper -========================= -", - ], - Array [ - " -Installing wdio packages: --", - "@wdio/local-runner@latest -- @wdio/mocha-framework@latest -- @wdio/crossbrowsertesting-service@latest -- wdio-lambdatest-service -- ts-node -- typescript", - ], - Array [ - " -Packages installed successfully, creating configuration file...", - ], - Array [ - "To have TypeScript support please add the following packages to your \\"types\\" list: -{ - \\"compilerOptions\\": { - \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] - } -} - -For for information on TypeScript integration check out: https://webdriver.io/docs/typescript -", - ], - Array [ - " -Configuration file was created successfully! -To run your tests, execute: -$ npx wdio run test/wdio.conf.ts -", - ], -] -`; - -exports[`install compliant NPM tag packages it should install tagged version if cli is tagged with next 1`] = ` -Array [ - Array [ - " -========================= -WDIO Configuration Helper -========================= -", - ], - Array [ - " -Installing wdio packages: --", - "@wdio/local-runner@next -- @wdio/mocha-framework@next -- @wdio/crossbrowsertesting-service@next -- wdio-lambdatest-service -- ts-node -- typescript", - ], - Array [ - " -Packages installed successfully, creating configuration file...", - ], - Array [ - "To have TypeScript support please add the following packages to your \\"types\\" list: -{ - \\"compilerOptions\\": { - \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] - } -} - -For for information on TypeScript integration check out: https://webdriver.io/docs/typescript -", - ], - Array [ - " -Configuration file was created successfully! -To run your tests, execute: -$ npx wdio run test/wdio.conf.ts -", - ], -] -`; - -exports[`install compliant NPM tag packages it should not install tagged version if cli is tagged with a specific version 1`] = ` -Array [ - Array [ - " -========================= -WDIO Configuration Helper -========================= -", - ], - Array [ + [ " Installing wdio packages: -", @@ -155,11 +20,11 @@ Installing wdio packages: - ts-node - typescript", ], - Array [ + [ " Packages installed successfully, creating configuration file...", ], - Array [ + [ "To have TypeScript support please add the following packages to your \\"types\\" list: { \\"compilerOptions\\": { @@ -170,7 +35,142 @@ Packages installed successfully, creating configuration file...", For for information on TypeScript integration check out: https://webdriver.io/docs/typescript ", ], - Array [ + [ + " +Configuration file was created successfully! +To run your tests, execute: +$ npx wdio run test/wdio.conf.ts +", + ], +] +`; + +exports[`install compliant NPM tag packages > it should install tagged version if cli is tagged with latest 1`] = ` +[ + [ + " +========================= +WDIO Configuration Helper +========================= +", + ], + [ + " +Installing wdio packages: +-", + "@wdio/local-runner +- @wdio/mocha-framework +- @wdio/crossbrowsertesting-service +- wdio-lambdatest-service +- ts-node +- typescript", + ], + [ + " +Packages installed successfully, creating configuration file...", + ], + [ + "To have TypeScript support please add the following packages to your \\"types\\" list: +{ + \\"compilerOptions\\": { + \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] + } +} + +For for information on TypeScript integration check out: https://webdriver.io/docs/typescript +", + ], + [ + " +Configuration file was created successfully! +To run your tests, execute: +$ npx wdio run test/wdio.conf.ts +", + ], +] +`; + +exports[`install compliant NPM tag packages > it should install tagged version if cli is tagged with next 1`] = ` +[ + [ + " +========================= +WDIO Configuration Helper +========================= +", + ], + [ + " +Installing wdio packages: +-", + "@wdio/local-runner +- @wdio/mocha-framework +- @wdio/crossbrowsertesting-service +- wdio-lambdatest-service +- ts-node +- typescript", + ], + [ + " +Packages installed successfully, creating configuration file...", + ], + [ + "To have TypeScript support please add the following packages to your \\"types\\" list: +{ + \\"compilerOptions\\": { + \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] + } +} + +For for information on TypeScript integration check out: https://webdriver.io/docs/typescript +", + ], + [ + " +Configuration file was created successfully! +To run your tests, execute: +$ npx wdio run test/wdio.conf.ts +", + ], +] +`; + +exports[`install compliant NPM tag packages > it should not install tagged version if cli is tagged with a specific version 1`] = ` +[ + [ + " +========================= +WDIO Configuration Helper +========================= +", + ], + [ + " +Installing wdio packages: +-", + "@wdio/local-runner +- @wdio/mocha-framework +- @wdio/crossbrowsertesting-service +- wdio-lambdatest-service +- ts-node +- typescript", + ], + [ + " +Packages installed successfully, creating configuration file...", + ], + [ + "To have TypeScript support please add the following packages to your \\"types\\" list: +{ + \\"compilerOptions\\": { + \\"types\\": [\\"node\\", \\"webdriverio/async\\", \\"@wdio/mocha-framework\\", \\"expect-webdriverio\\", \\"@wdio/crossbrowsertesting-service\\"] + } +} + +For for information on TypeScript integration check out: https://webdriver.io/docs/typescript +", + ], + [ " Configuration file was created successfully! To run your tests, execute: @@ -181,15 +181,15 @@ $ npx wdio run test/wdio.conf.ts `; exports[`prints TypeScript setup message 1`] = ` -Array [ - Array [ +[ + [ " ========================= WDIO Configuration Helper ========================= ", ], - Array [ + [ " Installing wdio packages: -", @@ -200,11 +200,11 @@ Installing wdio packages: - ts-node - typescript", ], - Array [ + [ " Packages installed successfully, creating configuration file...", ], - Array [ + [ "To have TypeScript support please add the following packages to your \\"types\\" list: { \\"compilerOptions\\": { @@ -215,7 +215,7 @@ Packages installed successfully, creating configuration file...", For for information on TypeScript integration check out: https://webdriver.io/docs/typescript ", ], - Array [ + [ " Configuration file was created successfully! To run your tests, execute: @@ -226,15 +226,15 @@ $ npx wdio run test/wdio.conf.ts `; exports[`prints TypeScript setup message with ts-node installed 1`] = ` -Array [ - Array [ +[ + [ " ========================= WDIO Configuration Helper ========================= ", ], - Array [ + [ " Installing wdio packages: -", @@ -245,11 +245,11 @@ Installing wdio packages: - ts-node - typescript", ], - Array [ + [ " Packages installed successfully, creating configuration file...", ], - Array [ + [ "To have TypeScript support please add the following packages to your \\"types\\" list: { \\"compilerOptions\\": { @@ -260,7 +260,7 @@ Packages installed successfully, creating configuration file...", For for information on TypeScript integration check out: https://webdriver.io/docs/typescript ", ], - Array [ + [ " Configuration file was created successfully! To run your tests, execute: @@ -271,15 +271,15 @@ $ npx wdio run test/wdio.conf.ts `; exports[`should create config file 1`] = ` -Object { - "installedPackages": Array [ +{ + "installedPackages": [ "@wdio/local-runner", "@wdio/mocha-framework", "@wdio/spec-reporter", "wdio-wait-for", "@wdio/sauce-service", ], - "parsedAnswers": Object { + "parsedAnswers": { "_async": "async ", "_await": "await ", "framework": "mocha", @@ -287,22 +287,22 @@ Object { "isUsingBabel": false, "isUsingTypeScript": false, "npmInstall": true, - "packagesToInstall": Array [ + "packagesToInstall": [ "@wdio/local-runner", "@wdio/mocha-framework", "@wdio/spec-reporter", "wdio-wait-for", "@wdio/sauce-service", ], - "plugins": Array [ + "plugins": [ "wait-for", ], "relativePath": "", - "reporters": Array [ + "reporters": [ "spec", ], "runner": "local", - "services": Array [ + "services": [ "sauce", ], "tsConfigFilePath": "test/tsconfig.json", @@ -312,15 +312,15 @@ Object { `; exports[`should not install @babel/register if existing 1`] = ` -Array [ - Array [ +[ + [ " ========================= WDIO Configuration Helper ========================= ", ], - Array [ + [ " Installing wdio packages: -", @@ -332,11 +332,11 @@ Installing wdio packages: - @babel/core - @babel/preset-env", ], - Array [ + [ " Packages installed successfully, creating configuration file...", ], - Array [ + [ " Configuration file was created successfully! To run your tests, execute: @@ -346,57 +346,18 @@ $ npx wdio run wdio.conf.js ] `; -exports[`should not install npm packages when npmInstall is false 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - " -========================= -WDIO Configuration Helper -========================= -", - ], - Array [ - " -To install dependencies, execute: -npm i --save-dev @wdio/local-runner @wdio/mocha-framework @wdio/spec-reporter wdio-wait-for @wdio/sauce-service -", - ], - Array [ - " -Configuration file was created successfully! -To run your tests, execute: -$ npx wdio run wdio.conf.js -", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], -} -`; +exports[`should not install npm packages when npmInstall is false 1`] = `[Function]`; exports[`should setup Babel if not existing 1`] = ` -Array [ - Array [ +[ + [ " ========================= WDIO Configuration Helper ========================= ", ], - Array [ + [ " Installing wdio packages: -", @@ -408,11 +369,11 @@ Installing wdio packages: - @babel/core - @babel/preset-env", ], - Array [ + [ " Packages installed successfully, creating configuration file...", ], - Array [ + [ " Configuration file was created successfully! To run your tests, execute: diff --git a/packages/wdio-cli/tests/commands/config.test.ts b/packages/wdio-cli/tests/commands/config.test.ts index aafa6f43d..ebfdae96b 100644 --- a/packages/wdio-cli/tests/commands/config.test.ts +++ b/packages/wdio-cli/tests/commands/config.test.ts @@ -1,3 +1,4 @@ +import { vi, describe, it, test, expect, afterEach, beforeEach } from 'vitest' import path from 'node:path' // @ts-expect-error mock import { yargs } from 'yargs/yargs' @@ -7,34 +8,54 @@ import inquirer from 'inquirer' import pkg from '../../package.json' import { handler, builder, missingConfigurationPrompt } from '../../src/commands/config' -import { addServiceDeps, convertPackageHashToObject, renderConfigurationFile, generateTestFiles, getPathForFileGeneration } from '../../src/utils' +import { + getAnswers, addServiceDeps, convertPackageHashToObject, renderConfigurationFile, + generateTestFiles, getPathForFileGeneration +} from '../../src/utils' -jest.mock('../../src/utils', () => ({ - addServiceDeps: jest.fn(), - convertPackageHashToObject: jest.fn().mockImplementation(jest.requireActual('../../src/utils').convertPackageHashToObject), - renderConfigurationFile: jest.fn(), - hasFile: jest.fn().mockReturnValue(false), - hasPackage: jest.fn().mockReturnValue(false), - getAnswers: jest.fn().mockImplementation(jest.requireActual('../../src/utils').getAnswers), - generateTestFiles: jest.fn(), - getPathForFileGeneration: jest.fn().mockImplementation(jest.requireActual('../../src/utils').getPathForFileGeneration), +vi.mock('yargs/yargs') +vi.mock('yarn-install') +vi.mock('inquirer') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) + +const utils: any = await vi.importActual('../../src/utils') +vi.mocked(convertPackageHashToObject).mockImplementation(utils.convertPackageHashToObject) +vi.mocked(getPathForFileGeneration).mockImplementation(utils.getPathForFileGeneration) +vi.mocked(getAnswers).mockImplementation(utils.getAnswers) + +vi.mock('../../src/utils', async () => { + return { + addServiceDeps: vi.fn(), + convertPackageHashToObject: vi.fn(), + renderConfigurationFile: vi.fn(), + hasFile: vi.fn().mockReturnValue(false), + hasPackage: vi.fn().mockReturnValue(false), + getAnswers: vi.fn(), + generateTestFiles: vi.fn(), + getPathForFileGeneration: vi.fn() + } +}) + +vi.mock('fs', () => ({ + default: { + writeFile: vi.fn().mockResolvedValue(''), + existsSync: vi.fn() + } })) -jest.mock('fs') - -jest.mock('../../package.json', () => { - const pkg = jest.requireActual('../../package.json') +vi.mock('../../package.json', async () => { + const pkg = await vi.importActual('../../package.json') as any pkg.setFetchSpec = (fetchSpec: string) => { pkg._requested = { fetchSpec } } pkg.clearFetchSpec = () => { delete pkg._requested } - return pkg + return { default: pkg } }) -const errorLogSpy = jest.spyOn(console, 'error') -const consoleLogSpy = jest.spyOn(console, 'log') +const errorLogSpy = vi.spyOn(console, 'error') +const consoleLogSpy = vi.spyOn(console, 'log') const args = { framework: '@wdio/mocha-framework$--$mocha', @@ -52,8 +73,8 @@ const args = { } beforeEach(() => { - (yarnInstall as any as jest.Mock).mockClear() - ;(yarnInstall as any as jest.Mock).mockReturnValue({ status: 0 }) + vi.mocked(yarnInstall).mockClear() + vi.mocked(yarnInstall).mockReturnValue({ status: 0 } as any) errorLogSpy.mockClear() consoleLogSpy.mockClear() @@ -65,16 +86,14 @@ afterEach(() => { }) test('should create config file', async () => { - - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(args)) - // @ts-expect-error - fs.promises = { writeFile: jest.fn() - .mockReturnValue(Promise.resolve('')) } + vi.mocked(inquirer.prompt).mockResolvedValue(args) const result = await handler({} as any) + // @ts-expect-error delete result.parsedAnswers.destPageObjectRootPath + // @ts-expect-error delete result.parsedAnswers.destSpecRootPath - const fileName = `${path.basename(path.dirname(result.parsedAnswers.tsConfigFilePath))}/${path.basename(result.parsedAnswers.tsConfigFilePath)}` - result.parsedAnswers.tsConfigFilePath = fileName + const fileName = `${path.basename(path.dirname(result.parsedAnswers!.tsConfigFilePath))}/${path.basename(result.parsedAnswers!.tsConfigFilePath)}` + result.parsedAnswers!.tsConfigFilePath = fileName expect(result).toMatchSnapshot() expect(addServiceDeps).toBeCalledTimes(1) expect(convertPackageHashToObject).toBeCalledTimes(5) @@ -97,7 +116,7 @@ test('it should properly build command', () => { }) test('should throw error if creating config file fails', async () => { - (renderConfigurationFile as jest.Mock).mockReturnValueOnce(Promise.reject(new Error('boom!'))) + vi.mocked(renderConfigurationFile).mockReturnValueOnce(Promise.reject(new Error('boom!'))) const err = await handler({} as any).catch((err) => err) expect(err.message).toContain('Error: boom!') }) @@ -129,10 +148,10 @@ describe('install compliant NPM tag packages', () => { } test('it should install tagged version if cli is tagged with beta', async () => { - setFetchSpec('beta'); - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(args)) + setFetchSpec('beta') + vi.mocked(inquirer.prompt).mockResolvedValue(args) // @ts-expect-error - fs.promises = { writeFile: jest.fn() + fs.promises = { writeFile: vi.fn() .mockReturnValue(Promise.resolve('')) } await handler({} as any) @@ -140,21 +159,21 @@ describe('install compliant NPM tag packages', () => { }) test('it should install tagged version if cli is tagged with next', async () => { - setFetchSpec('next'); - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(args)) - // @ts-expect-error - fs.promises = { writeFile: jest.fn() - .mockReturnValue(Promise.resolve('')) } + setFetchSpec('next') + vi.mocked(inquirer.prompt).mockResolvedValue(args) + fs.promises = { + writeFile: vi.fn().mockReturnValue(Promise.resolve('')) + } as any await handler({} as any) expect(consoleLogSpy.mock.calls).toMatchSnapshot() }) test('it should install tagged version if cli is tagged with latest', async () => { - setFetchSpec('latest'); - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(args)) + setFetchSpec('latest') + vi.mocked(inquirer.prompt).mockResolvedValue(args) // @ts-expect-error - fs.promises = { writeFile: jest.fn() + fs.promises = { writeFile: vi.fn() .mockReturnValue(Promise.resolve('')) } await handler({} as any) @@ -162,10 +181,10 @@ describe('install compliant NPM tag packages', () => { }) test('it should not install tagged version if cli is tagged with a specific version', async () => { - setFetchSpec('7.0.8'); - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(args)) + setFetchSpec('7.0.8') + vi.mocked(inquirer.prompt).mockResolvedValue(args) // @ts-expect-error - fs.promises = { writeFile: jest.fn() + fs.promises = { writeFile: vi.fn() .mockReturnValue(Promise.resolve('')) } await handler({} as any) @@ -179,7 +198,7 @@ describe('install compliant NPM tag packages', () => { }) test('prints TypeScript setup message', async () => { - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve({ + vi.mocked(inquirer.prompt).mockResolvedValue({ framework: '@wdio/mocha-framework$--$mocha', reporters: [], plugins: [], @@ -190,14 +209,14 @@ test('prints TypeScript setup message', async () => { generateTestFiles: false, isUsingCompiler: 'TypeScript (https://www.typescriptlang.org/)', npmInstall: true - })) + }) await handler({} as any) expect(consoleLogSpy.mock.calls).toMatchSnapshot() }) test('prints TypeScript setup message with ts-node installed', async () => { process.env.WDIO_TEST_THROW_RESOLVE = '1' - ;(inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve({ + vi.mocked(inquirer.prompt).mockResolvedValue({ framework: '@wdio/mocha-framework$--$mocha', reporters: [], plugins: [], @@ -208,7 +227,7 @@ test('prints TypeScript setup message with ts-node installed', async () => { generateTestFiles: false, isUsingCompiler: 'TypeScript (https://www.typescriptlang.org/)', npmInstall: true - })) + }) const config = { compilerOptions: { @@ -228,7 +247,7 @@ test('prints TypeScript setup message with ts-node installed', async () => { JSON.stringify(config, null, 4)) // @ts-expect-error - fs.promises = { writeFile: jest.fn() + fs.promises = { writeFile: vi.fn() .mockReturnValue(Promise.resolve('')) } await handler({} as any) expect(consoleLogSpy.mock.calls).toMatchSnapshot() @@ -236,7 +255,7 @@ test('prints TypeScript setup message with ts-node installed', async () => { test('should setup Babel if not existing', async () => { process.env.WDIO_TEST_THROW_RESOLVE = '1' - ;(inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve({ + vi.mocked(inquirer.prompt).mockResolvedValue({ framework: '@wdio/mocha-framework$--$mocha', reporters: [], plugins: [], @@ -247,9 +266,9 @@ test('should setup Babel if not existing', async () => { generateTestFiles: false, isUsingCompiler: 'Babel (https://babeljs.io/)', npmInstall: true - })) + }) // @ts-expect-error - fs.promises = { writeFile: jest.fn() + fs.promises = { writeFile: vi.fn() .mockReturnValue(Promise.resolve('')) } await handler({} as any) expect(consoleLogSpy.mock.calls).toMatchSnapshot() @@ -257,7 +276,7 @@ test('should setup Babel if not existing', async () => { test('should not install @babel/register if existing', async () => { delete process.env.WDIO_TEST_THROW_RESOLVE - ;(inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve({ + vi.mocked(inquirer.prompt).mockResolvedValue({ framework: '@wdio/mocha-framework$--$mocha', reporters: [], plugins: [], @@ -268,14 +287,14 @@ test('should not install @babel/register if existing', async () => { generateTestFiles: false, isUsingCompiler: 'Babel (https://babeljs.io/)', npmInstall: true - })) + }) await handler({} as any) expect(consoleLogSpy.mock.calls).toMatchSnapshot() }) test('should not install npm packages when npmInstall is false', async () => { - const ans = { ...args, npmInstall: false }; - (inquirer.prompt as any as jest.Mock).mockReturnValue(Promise.resolve(ans)) + const ans = { ...args, npmInstall: false } + vi.mocked(inquirer.prompt).mockResolvedValue(ans) await handler({} as any) expect(consoleLogSpy).toMatchSnapshot() expect(yarnInstall).not.toHaveBeenCalled() @@ -283,25 +302,25 @@ test('should not install npm packages when npmInstall is false', async () => { describe('missingConfigurationPromp', () => { it('should prompt user', async () => { - (inquirer.prompt as any as jest.Mock).mockImplementation(() => ({ config: true })) - await missingConfigurationPrompt('run', 'foobar', false, jest.fn()) - expect((inquirer.prompt as any as jest.Mock)).toHaveBeenCalled() + vi.mocked(inquirer.prompt).mockImplementation(() => ({ config: true }) as any) + await missingConfigurationPrompt('run', 'foobar', false, vi.fn()) + expect(vi.mocked(inquirer.prompt)).toHaveBeenCalled() }) it('should call function to initalize configuration helper', async () => { - const runConfig = jest.fn() + const runConfig = vi.fn() await missingConfigurationPrompt('test', 'foobar', false, runConfig) expect(runConfig).toHaveBeenCalledWith(false, false, true) }) it('should pass "yarn" flag to runConfig', async () => { - const runConfig = jest.fn() + const runConfig = vi.fn() await missingConfigurationPrompt('test', 'test message', true, runConfig) expect(runConfig).toHaveBeenCalledWith(true, false, true) }) it('should throw if error occurs', async () => { - const runConfig = jest.fn().mockImplementation(Promise.reject) + const runConfig = vi.fn().mockImplementation(Promise.reject) try { await missingConfigurationPrompt('test', 'foobar', false, runConfig) @@ -311,6 +330,6 @@ describe('missingConfigurationPromp', () => { }) afterEach(() => { - (inquirer.prompt as any as jest.Mock).mockClear() + vi.mocked(inquirer.prompt).mockClear() }) }) diff --git a/packages/wdio-cli/tests/commands/install.test.ts b/packages/wdio-cli/tests/commands/install.test.ts index ef0a177d5..8353aee53 100644 --- a/packages/wdio-cli/tests/commands/install.test.ts +++ b/packages/wdio-cli/tests/commands/install.test.ts @@ -1,3 +1,5 @@ +import path from 'node:path' +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' // @ts-expect-error mock import { yargs } from 'yargs/yargs' import fs from 'fs-extra' @@ -6,20 +8,20 @@ import * as configCmd from '../../src/commands/config' import * as utils from '../../src/utils' import yarnInstall from 'yarn-install' -jest.mock('yarn-install') -jest.mock('fs-extra') +vi.mock('yargs/yargs') +vi.mock('yarn-install') +vi.mock('fs-extra') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) -let findInConfigMock: jest.SpyInstance +const findInConfigMock = vi.spyOn(utils, 'findInConfig') describe('Command: install', () => { beforeEach(() => { - jest.spyOn(console, 'log') - jest.spyOn(process, 'exit').mockImplementation((code?: number): never => code as never) - jest.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation(() => Promise.resolve() as any) - jest.spyOn(utils, 'addServiceDeps') - jest.spyOn(utils, 'replaceConfig').mockReturnValueOnce({ foo: 123 } as any) - - findInConfigMock = jest.spyOn(utils, 'findInConfig') + vi.spyOn(console, 'log') + vi.spyOn(process, 'exit').mockImplementation((code?: number): never => code as never) + vi.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation(() => Promise.resolve() as any) + vi.spyOn(utils, 'addServiceDeps') + vi.spyOn(utils, 'replaceConfig').mockReturnValueOnce({ foo: 123 } as any) }) it('it should properly build command', () => { @@ -45,8 +47,8 @@ describe('Command: install', () => { }) it('should prompt missing configuration', async () => { - jest.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation(() => Promise.reject()) - ;(fs.existsSync as jest.Mock).mockReturnValue(false) + vi.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation(() => Promise.reject()) + vi.mocked(fs.existsSync).mockReturnValue(false) await installCmd.handler({ type: 'service', name: 'chromedriver', config: './wdio.conf.js' } as any) @@ -56,7 +58,7 @@ You can create one by running 'wdio config'`, undefined) it('should verify if configuration already has desired installation', async () => { findInConfigMock.mockReturnValue(['chromedriver']) - ;(fs.existsSync as jest.Mock).mockReturnValue(true) + vi.mocked(fs.existsSync).mockReturnValue(true) await installCmd.handler({ type: 'service', name: 'chromedriver', config: './wdio.conf.js' } as any) @@ -65,9 +67,9 @@ You can create one by running 'wdio config'`, undefined) it('should correctly install packages', async () => { findInConfigMock.mockReturnValue(['']) - ;(fs.existsSync as jest.Mock).mockReturnValue(true) - ;(fs.readFileSync as jest.Mock).mockReturnValue('module.config = {}') - ;(yarnInstall as any as jest.Mock).mockImplementation(() => ({ status: 0 })) + vi.mocked(fs.existsSync).mockReturnValue(true) + vi.mocked(fs.readFileSync).mockReturnValue('module.config = {}') + vi.mocked(yarnInstall).mockImplementation(() => ({ status: 0 }) as any) await installCmd.handler({ type: 'service', name: 'chromedriver', config: './wdio.conf.js' } as any) @@ -81,10 +83,10 @@ You can create one by running 'wdio config'`, undefined) it('should fail if config could not be updated', async () => { findInConfigMock.mockReturnValue(['']) - ;(fs.readFileSync as jest.Mock).mockReturnValue('module.config = {}') - ;(yarnInstall as any as jest.Mock).mockImplementation(() => ({ status: 0 })) - ;(utils.replaceConfig as jest.Mock).mockRestore() - jest.spyOn(utils, 'replaceConfig').mockReturnValue('') + vi.mocked(fs.readFileSync).mockReturnValue('module.config = {}') + vi.mocked(yarnInstall).mockImplementation(() => ({ status: 0 }) as any) + vi.mocked(utils.replaceConfig).mockRestore() + vi.spyOn(utils, 'replaceConfig').mockReturnValue('') const err = await installCmd.handler({ type: 'service', name: 'chromedriver', config: './wdio.conf.js' } as any) .catch((err: Error) => err) as Error @@ -94,9 +96,9 @@ You can create one by running 'wdio config'`, undefined) it('allows for custom config location', async () => { findInConfigMock.mockReturnValue(['']) - ;(fs.existsSync as jest.Mock).mockReturnValue(true) - ;(fs.readFileSync as jest.Mock).mockReturnValue('module.config = {}') - ;(yarnInstall as any as jest.Mock).mockImplementation(() => ({ status: 0 })) + vi.mocked(fs.existsSync).mockReturnValue(true) + vi.mocked(fs.readFileSync).mockReturnValue('module.config = {}') + vi.mocked(yarnInstall).mockImplementation(() => ({ status: 0 }) as any) await installCmd.handler({ type: 'service', name: 'chromedriver', config: './path/to/wdio.conf.js' } as any) @@ -110,9 +112,9 @@ You can create one by running 'wdio config'`, undefined) it('should exit if there is an error while installing', async () => { findInConfigMock.mockReturnValue(['']) - ;(fs.existsSync as jest.Mock).mockReturnValue(true) - ;(yarnInstall as any as jest.Mock).mockImplementation(() => ({ status: 1, stderr: 'test error' })) - jest.spyOn(console, 'error') + vi.mocked(fs.existsSync).mockReturnValue(true) + vi.mocked(yarnInstall).mockImplementation(() => ({ status: 1, stderr: 'test error' })) + vi.spyOn(console, 'error') await installCmd.handler({ type: 'service', name: 'chromedriver', config: './wdio.conf.js' } as any) @@ -121,15 +123,15 @@ You can create one by running 'wdio config'`, undefined) }) afterEach(() => { - (console.log as jest.Mock).mockClear() - ;(process.exit as any as jest.Mock).mockClear() - ;(fs.readFileSync as jest.Mock).mockClear() - ;(fs.existsSync as jest.Mock).mockClear() - ;(fs.writeFileSync as jest.Mock).mockClear() + vi.mocked(console.log).mockClear() + vi.mocked(process.exit).mockClear() + vi.mocked(fs.readFileSync).mockClear() + vi.mocked(fs.existsSync).mockClear() + vi.mocked(fs.writeFileSync).mockClear() - ;(utils.findInConfig as jest.Mock).mockClear() - ;(utils.addServiceDeps as jest.Mock).mockClear() - ;(utils.replaceConfig as jest.Mock).mockClear() - ;(configCmd.missingConfigurationPrompt as jest.Mock).mockClear() + vi.mocked(utils.findInConfig).mockClear() + vi.mocked(utils.addServiceDeps).mockClear() + vi.mocked(utils.replaceConfig).mockClear() + vi.mocked(configCmd.missingConfigurationPrompt).mockClear() }) }) diff --git a/packages/wdio-cli/tests/commands/repl.test.ts b/packages/wdio-cli/tests/commands/repl.test.ts index 58c415b9c..448cf6f71 100644 --- a/packages/wdio-cli/tests/commands/repl.test.ts +++ b/packages/wdio-cli/tests/commands/repl.test.ts @@ -1,21 +1,27 @@ +import path from 'node:path' +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' // @ts-expect-error mock import { yargs } from 'yargs/yargs' import { remote } from 'webdriverio' import { handler, builder } from '../../src/commands/repl' -jest.mock('@wdio/utils', () => { +vi.mock('@wdio/utils', () => { let syncSupport = false return { - setSyncSupport: (val: boolean) => (syncSupport = val), - get hasWdioSyncSupport () { - return syncSupport + default: { + setSyncSupport: (val: boolean) => (syncSupport = val), + get hasWdioSyncSupport () { + return syncSupport + } } } }) -jest.mock('repl') +vi.mock('repl') +vi.mock('yargs/yargs') +vi.mock('webdriverio', () => import(path.join(process.cwd(), '__mocks__', 'webdriverio'))) describe('repl commandDir', () => { it('should call debug command', async () => { @@ -44,7 +50,7 @@ describe('repl commandDir', () => { describe('Command: repl', () => { beforeEach(() => { - (remote as jest.Mock).mockClear() + vi.mocked(remote).mockClear() }) it('should attach global variables', async () => { @@ -52,6 +58,7 @@ describe('Command: repl', () => { expect(global.$).not.toBeUndefined() expect(global.$$).not.toBeUndefined() + // @ts-expect-error expect(global.browser).not.toBeUndefined() }) diff --git a/packages/wdio-cli/tests/commands/run.test.ts b/packages/wdio-cli/tests/commands/run.test.ts index 5b2de1836..b6ef2d2ee 100644 --- a/packages/wdio-cli/tests/commands/run.test.ts +++ b/packages/wdio-cli/tests/commands/run.test.ts @@ -1,54 +1,59 @@ +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' // @ts-expect-error mock import { yargs } from 'yargs/yargs' import fs from 'fs-extra' import * as runCmd from '../../src/commands/run' import * as configCmd from '../../src/commands/config' -jest.mock('./../../src/launcher', () => class { - run() { - return { - then: jest.fn().mockReturnValue({ - catch: jest.fn().mockReturnValue('launcher-mock') - }) +vi.mock('yargs/yargs') +vi.mock('fs-extra') +vi.mock('./../../src/launcher', () => ({ + default: class { + run() { + return { + then: vi.fn().mockReturnValue({ + catch: vi.fn().mockReturnValue('launcher-mock') + }) + } } } -}) -jest.mock('./../../src/watcher', () => class { - watch() { - return 'watching-test' +})) +vi.mock('./../../src/watcher', () => ({ + default: class { + watch() { + return 'watching-test' + } } -}) - -jest.mock('fs-extra') +})) describe('Command: run', () => { - const setEncodingMock = jest.fn() - const onMock = jest.fn((s, c) => c()) + const setEncodingMock = vi.fn() + const onMock = vi.fn((s, c) => c()) beforeEach(() => { - (fs.existsSync as jest.Mock).mockImplementation(() => true) - ;(fs.existsSync as jest.Mock).mockClear() - jest.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation((): Promise => { + vi.mocked(fs.existsSync).mockImplementation(() => true) + vi.mocked(fs.existsSync).mockClear() + vi.spyOn(configCmd, 'missingConfigurationPrompt').mockImplementation((): Promise => { return undefined as never }) - jest.spyOn(console, 'error') - jest.spyOn(process, 'openStdin').mockImplementation( + vi.spyOn(console, 'error') + vi.spyOn(process, 'openStdin').mockImplementation( () => ({ setEncoding: setEncodingMock, on: onMock }) as any) }) it('should call missingConfigurationPrompt if no config found', async () => { - (fs.existsSync as jest.Mock).mockImplementation(() => false) + vi.mocked(fs.existsSync).mockImplementation(() => false) await runCmd.handler({} as any) expect(configCmd.missingConfigurationPrompt).toHaveBeenCalledTimes(1) - expect((configCmd.missingConfigurationPrompt as jest.Mock).mock.calls[0][1]) + expect(vi.mocked(configCmd.missingConfigurationPrompt).mock.calls[0][1]) .toContain('No WebdriverIO configuration found in "') - ;(fs.existsSync as jest.Mock).mockClear() + vi.mocked(fs.existsSync).mockClear() }) it('should use local conf if nothing defined', async () => { - (fs.existsSync as jest.Mock).mockImplementation(() => true) + vi.mocked(fs.existsSync).mockImplementation(() => true) await runCmd.handler({ argv: {} } as any) expect(fs.existsSync).toBeCalledTimes(2) }) @@ -90,9 +95,9 @@ describe('Command: run', () => { }) afterEach(() => { - (process.openStdin as jest.Mock).mockReset() - ;(console.error as any as jest.Mock).mockReset() - ;(fs.existsSync as jest.Mock).mockClear() - ;(configCmd.missingConfigurationPrompt as jest.Mock).mockClear() + vi.mocked(process.openStdin).mockReset() + vi.mocked(console.error).mockReset() + vi.mocked(fs.existsSync).mockClear() + vi.mocked(configCmd.missingConfigurationPrompt).mockClear() }) }) diff --git a/packages/wdio-cli/tests/config.test.ts b/packages/wdio-cli/tests/config.test.ts index 09addabd5..8e7c54bd0 100644 --- a/packages/wdio-cli/tests/config.test.ts +++ b/packages/wdio-cli/tests/config.test.ts @@ -1,3 +1,4 @@ +import { test, expect } from 'vitest' import { getAnswers } from '../src/utils' test('runConfig with yes param', async () => { diff --git a/packages/wdio-cli/tests/index.test.ts b/packages/wdio-cli/tests/index.test.ts index 2ad23261c..f66c364e1 100644 --- a/packages/wdio-cli/tests/index.test.ts +++ b/packages/wdio-cli/tests/index.test.ts @@ -1,36 +1,38 @@ import { join, resolve } from 'node:path' +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' // @ts-expect-error mock import { yargs as yargsMock } from 'yargs/yargs' import { run } from '../src/index' import { handler } from '../src/commands/run' -jest.mock('./../src/commands/run', () => ({ - ...jest.requireActual('./../src/commands/run') as object, - handler: jest.fn().mockReturnValue(Promise.resolve('success')) +vi.mock('yargs/yargs') +vi.mock('./../src/commands/run', async () => ({ + ...(await vi.importActual('./../src/commands/run')) as object, + handler: vi.fn().mockReturnValue(Promise.resolve('success')) })) const consoleError = console.error describe('index', () => { beforeEach(() => { - (handler as jest.Mock).mockClear() - ;(yargsMock.parse as jest.Mock).mockClear() - console.error = jest.fn() + vi.mocked(handler).mockClear() + vi.mocked(yargsMock.parse).mockClear() + console.error = vi.fn() }) it('should call config if no known command is used', async () => { await run() - expect((handler as jest.Mock).mock.calls[0][0]).toEqual({ + expect(vi.mocked(handler).mock.calls[0][0]).toEqual({ configPath: join(`${process.cwd()}`, 'wdio.conf.js'), _: ['wdio.conf.js'] }) }) it('should set default config filename if not set', async () => { - (yargsMock.parse as jest.Mock).mockReturnValue({ _: [], spec: ['/foo/bar'] }) as any + vi.mocked(yargsMock.parse).mockReturnValue({ _: [], spec: ['/foo/bar'] }) as any await run() - expect((handler as jest.Mock).mock.calls[0][0]).toEqual({ + expect(vi.mocked(handler).mock.calls[0][0]).toEqual({ configPath: join(`${process.cwd()}`, 'wdio.conf.js'), spec: ['/foo/bar'], _: [] @@ -39,15 +41,15 @@ describe('index', () => { it('should work properly with absolute paths', async () => { const expectedPath = resolve('/some/absolute/path/here/wdio.conf.js') - ;(yargsMock.parse as jest.Mock).mockReturnValue({ ...yargsMock.argv, _: [expectedPath] }) as any + vi.mocked(yargsMock.parse).mockReturnValue({ ...yargsMock.argv, _: [expectedPath] }) as any await run().catch() expect(handler).toHaveBeenCalledTimes(1) - expect((handler as jest.Mock).mock.calls[0][0]).toEqual({ + expect(vi.mocked(handler).mock.calls[0][0]).toEqual({ configPath: expectedPath, _: [expectedPath] }) - ;(yargsMock.epilogue as jest.Mock).mockClear() + vi.mocked(yargsMock.epilogue).mockClear() }) /** @@ -55,19 +57,19 @@ describe('index', () => { * for some reason the `cb` variable is not the callback passed in */ it.skip('should gracefully fail', async () => { - (yargsMock.parse as jest.Mock).mockImplementation((str, cb) => { + vi.mocked(yargsMock.parse).mockImplementation((str: never, cb: Function) => { cb(null, null, 'test') return yargsMock.argv }) - ;(handler as jest.Mock).mockRejectedValue(new Error('ups')) - jest.spyOn(console, 'error') + vi.mocked(handler).mockRejectedValue(new Error('ups')) + vi.spyOn(console, 'error') await run() expect(console.error).toHaveBeenCalled() }) it('should do nothing if command was called', async () => { - (yargsMock.parse as jest.Mock).mockReturnValue({ ...yargsMock.argv, _: ['run'] }) as any + vi.mocked(yargsMock.parse).mockReturnValue({ ...yargsMock.argv, _: ['run'] }) as any expect(typeof (await run())).toBe('undefined') }) diff --git a/packages/wdio-cli/tests/interface.test.ts b/packages/wdio-cli/tests/interface.test.ts index 58d2e469b..690cd7b60 100644 --- a/packages/wdio-cli/tests/interface.test.ts +++ b/packages/wdio-cli/tests/interface.test.ts @@ -1,3 +1,5 @@ +import path from 'node:path' +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' import WDIOCLInterface from '../src/interface' import { HookError } from '../src/utils' import chalk from 'chalk' @@ -8,13 +10,16 @@ const EMPTY_INTERFACE_MESSAGE_OBJECT = { debugger: {} } +vi.mock('chalk') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) + describe('cli interface', () => { let wdioClInterface: WDIOCLInterface beforeEach(() => { - global.console.log = jest.fn() + global.console.log = vi.fn() wdioClInterface = new WDIOCLInterface(config as any, 5) - wdioClInterface.log = jest.fn().mockImplementation((...args) => args) + wdioClInterface.log = vi.fn().mockImplementation((...args: any[]) => args) }) it('should add jobs', () => { @@ -99,7 +104,7 @@ describe('cli interface', () => { }) it('should print test error', () => { - wdioClInterface.onTestError = jest.fn() + wdioClInterface.onTestError = vi.fn() wdioClInterface.onMessage({ origin: 'reporter', name: 'printFailureMessage', @@ -110,7 +115,7 @@ describe('cli interface', () => { }) it('should trigger job:start event on testFrameworkInit', () => { - wdioClInterface.emit = jest.fn() + wdioClInterface.emit = vi.fn() wdioClInterface.onMessage({ name: 'testFrameworkInit', content: 'content' @@ -120,7 +125,7 @@ describe('cli interface', () => { it('should print reporter messages in watch mode', () => { wdioClInterface['_isWatchMode'] = true - wdioClInterface.printReporters = jest.fn() + wdioClInterface.printReporters = vi.fn() wdioClInterface.onMessage({ origin: 'reporter', @@ -136,7 +141,7 @@ describe('cli interface', () => { }) it('should not store any other messages', () => { - wdioClInterface.printReporters = jest.fn() + wdioClInterface.printReporters = vi.fn() expect(wdioClInterface.onMessage({ cid: '0-0', @@ -201,7 +206,7 @@ describe('cli interface', () => { describe('setup', () => { it('called within constructor', () => { - wdioClInterface.onStart = jest.fn() + wdioClInterface.onStart = vi.fn() expect(wdioClInterface.result).toEqual({ finished: 0, passed: 0, @@ -212,7 +217,7 @@ describe('cli interface', () => { }) it('called explicitly', () => { - wdioClInterface.onStart = jest.fn() + wdioClInterface.onStart = vi.fn() wdioClInterface['_messages'] = { reporter: {}, debugger: {} @@ -315,22 +320,23 @@ describe('cli interface', () => { scenarios.forEach(scenario => { it(scenario.method, () => { - wdioClInterface.onJobComplete = jest.fn() + wdioClInterface.onJobComplete = vi.fn() wdioClInterface['_jobs'].set(scenario.cid, scenario.job) + // @ts-expect-error wdioClInterface[scenario.method](scenario.cid, scenario.job, scenario.retries) expect(wdioClInterface.onJobComplete).toBeCalledWith(scenario.cid, scenario.job, scenario.retries, scenario.message) }) }) it('onSpecSkip', () => { - wdioClInterface.onJobComplete = jest.fn() + wdioClInterface.onJobComplete = vi.fn() wdioClInterface['_jobs'].set('cid', job) wdioClInterface.onSpecSkip(cid, job) expect(wdioClInterface.onJobComplete).toBeCalledWith(cid, job, 0, 'SKIPPED', expect.any(Function)) }) it('onSpecRetry with delay', () => { - wdioClInterface.onJobComplete = jest.fn() + wdioClInterface.onJobComplete = vi.fn() wdioClInterface['_specFileRetriesDelay'] = 2 wdioClInterface['_jobs'].set('cid', job) wdioClInterface.onSpecRetry(cid, job, 3) @@ -345,10 +351,12 @@ describe('cli interface', () => { specs: ['bar'], hasTests: true }) + // @ts-expect-error expect(wdioClInterface.sigintTrigger()[0]).toContain('Ending WebDriver sessions gracefully') }) it('should print message without jobs', () => { + // @ts-expect-error expect(wdioClInterface.sigintTrigger()[0]).toContain('Ended WebDriver sessions gracefully') }) @@ -369,15 +377,15 @@ describe('cli interface', () => { wdioClInterface.printReporters() expect(wdioClInterface['_messages'].reporter).toEqual({}) expect(wdioClInterface.log).toBeCalledTimes(2) - expect((wdioClInterface.log as jest.Mock).mock.calls[0][1]).toContain('"foo" Reporter:') - expect((wdioClInterface.log as jest.Mock).mock.calls[1][0]).toContain('bar') + expect(vi.mocked(wdioClInterface.log).mock.calls[0][1]).toContain('"foo" Reporter:') + expect(vi.mocked(wdioClInterface.log).mock.calls[1][0]).toContain('bar') }) }) describe('finalise', () => { it('finalise should print reporters and summary', () => { - wdioClInterface.printReporters = jest.fn() - wdioClInterface.printSummary = jest.fn() + wdioClInterface.printReporters = vi.fn() + wdioClInterface.printSummary = vi.fn() wdioClInterface.finalise() expect(wdioClInterface.printReporters).toBeCalledTimes(1) expect(wdioClInterface.printSummary).toBeCalledTimes(1) @@ -385,8 +393,8 @@ describe('cli interface', () => { it('finalise should do nothing in watch mode', () => { wdioClInterface['_isWatchMode'] = true - wdioClInterface.printReporters = jest.fn() - wdioClInterface.printSummary = jest.fn() + wdioClInterface.printReporters = vi.fn() + wdioClInterface.printSummary = vi.fn() wdioClInterface.finalise() expect(wdioClInterface.printReporters).toBeCalledTimes(0) expect(wdioClInterface.printSummary).toBeCalledTimes(0) @@ -423,7 +431,7 @@ describe('cli interface', () => { }) }) - test('logHookError', () => { + it('logHookError', () => { const err = new HookError('foobar', 'somewhere') expect(wdioClInterface.logHookError(err)[0]) .toContain('red SevereServiceError in "somewhere"') @@ -492,6 +500,6 @@ describe('cli interface', () => { afterEach(() => { wdioClInterface['_specFileRetriesDelay'] = 0 - ;(global.console.log as jest.Mock).mockRestore() + vi.mocked(global.console.log).mockRestore() }) }) diff --git a/packages/wdio-cli/tests/launcher.test.ts b/packages/wdio-cli/tests/launcher.test.ts index 811bffe84..a3fecb83b 100644 --- a/packages/wdio-cli/tests/launcher.test.ts +++ b/packages/wdio-cli/tests/launcher.test.ts @@ -1,3 +1,5 @@ +import path from 'node:path' +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' import Launcher from '../src/launcher' import logger from '@wdio/logger' import { sleep } from '@wdio/utils' @@ -5,20 +7,25 @@ import fs from 'fs-extra' const caps: WebDriver.DesiredCapabilities = { maxInstances: 1, browserName: 'chrome' } -jest.mock('fs-extra') -jest.mock('../src/interface', () => class { - emit = jest.fn() - on = jest.fn() - sigintTrigger = jest.fn() - onMessage = jest.fn() -}) +vi.mock('fs-extra') +vi.mock('@wdio/utils', () => import(path.join(process.cwd(), '__mocks__', '@wdio/utils'))) +vi.mock('@wdio/config', () => import(path.join(process.cwd(), '__mocks__', '@wdio/config'))) +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('../src/interface', () => ({ + default: class { + emit = vi.fn() + on = vi.fn() + sigintTrigger = vi.fn() + onMessage = vi.fn() + } +})) describe('launcher', () => { - const emitSpy = jest.spyOn(process, 'emit') + const emitSpy = vi.spyOn(process, 'emit') let launcher: Launcher beforeEach(() => { - global.console.log = jest.fn() + global.console.log = vi.fn() emitSpy.mockClear() launcher = new Launcher('./') }) @@ -50,7 +57,7 @@ describe('launcher', () => { describe('capabilities', () => { it('should NOT fail when capabilities are passed', async () => { - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) const exitCode = await launcher.runMode({ specs: ['./'] } as any, [caps]) expect(launcher.runSpecs).toBeCalled() expect(exitCode).toEqual(0) @@ -58,15 +65,15 @@ describe('launcher', () => { }) it('should fail if no specs were found', async () => { - launcher.runSpecs = jest.fn() - launcher.configParser.getSpecs = jest.fn().mockReturnValue([]) + launcher.runSpecs = vi.fn() + launcher.configParser.getSpecs = vi.fn().mockReturnValue([]) const exitCode = await launcher.runMode({ specs: ['./'] } as any, [caps]) expect(launcher.runSpecs).toBeCalledTimes(0) expect(exitCode).toBe(1) }) it('should fail when no capabilities are passed', async () => { - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) // @ts-ignore test invalid parameter const exitCode = await launcher.runMode({ specs: ['./'] as any }) expect(exitCode).toEqual(1) @@ -74,14 +81,14 @@ describe('launcher', () => { }) it('should fail when no capabilities are set', async () => { - launcher.runSpecs = jest.fn().mockReturnValue(1) - const exitCode = await launcher.runMode({ specs: ['./'] } as any, undefined) + launcher.runSpecs = vi.fn().mockReturnValue(1) + const exitCode = await launcher.runMode({ specs: ['./'] } as any, undefined as any) expect(exitCode).toEqual(1) expect(logger('').error).toBeCalledWith('Missing capabilities, exiting with failure') }) it('should start instance in multiremote', () => { - launcher.runSpecs = jest.fn() + launcher.runSpecs = vi.fn() launcher.isMultiremote = true launcher.runMode( { specs: ['./'], specFileRetries: 2 } as any, @@ -96,7 +103,7 @@ describe('launcher', () => { }) it('should start instance with grouped specs', () => { - launcher.runSpecs = jest.fn() + launcher.runSpecs = vi.fn() launcher.isMultiremote = false launcher.runMode( { specs: [['/a.js', '/b.js']], specFileRetries: 2 } as any, @@ -111,7 +118,7 @@ describe('launcher', () => { }) it('should start instance in multiremote with grouped specs', () => { - launcher.runSpecs = jest.fn() + launcher.runSpecs = vi.fn() launcher.isMultiremote = true launcher.runMode( { specs: [['/a.js', '/b.js']], specFileRetries: 2 } as any, @@ -126,7 +133,7 @@ describe('launcher', () => { }) it('should ignore specFileRetries in watch mode', () => { - launcher.runSpecs = jest.fn() + launcher.runSpecs = vi.fn() launcher['_isWatchMode'] = true launcher.runMode({ specs: ['./'], specFileRetries: 2 } as any, [caps, caps]) @@ -138,7 +145,7 @@ describe('launcher', () => { }) it('should apply maxInstancesPerCapability if maxInstances is not passed', () => { - launcher.runSpecs = jest.fn() + launcher.runSpecs = vi.fn() launcher.runMode( { specs: ['./'], specFileRetries: 3, maxInstancesPerCapability: 4 } as any, [{ browserName: 'chrome' }] @@ -154,14 +161,14 @@ describe('launcher', () => { describe('hasTriggeredExitRoutine', () => { it('should return false if there are specs left of running when hasTriggeredExitRoutine is false', () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) launcher['_hasTriggeredExitRoutine'] = false const returnValue = launcher.runSpecs() expect(returnValue).toEqual(false) }) it('should return true when hasTriggeredExitRoutine is true', () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) launcher['_hasTriggeredExitRoutine'] = true const returnValue = launcher.runSpecs() expect(returnValue).toEqual(true) @@ -169,18 +176,18 @@ describe('launcher', () => { }) describe('endHandler', () => { - const config = { onWorkerEnd: jest.fn() } + const config = { onWorkerEnd: vi.fn() } beforeEach(() => { launcher['_launcher'] = [] - launcher.configParser.getConfig = jest.fn().mockReturnValue(config) + launcher.configParser.getConfig = vi.fn().mockReturnValue(config) }) it('should emit and resolve failed status', async () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }] - launcher['_resolve'] = jest.fn() + launcher['_resolve'] = vi.fn() await launcher.endHandler({ cid: '0-1', exitCode: 1, specs: [], retries: 0 } as any) expect(launcher.interface.emit).toBeCalledWith('job:end', { cid: '0-1', passed: false, retries: 0 }) expect(launcher['_resolve']).toBeCalledWith(1) @@ -188,43 +195,43 @@ describe('launcher', () => { }) it('should emit and resolve passed status', async () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }] - launcher['_resolve'] = jest.fn() + launcher['_resolve'] = vi.fn() await launcher.endHandler({ cid: '0-1', exitCode: 0 } as any) expect(launcher.interface.emit).toBeCalledWith('job:end', { cid: '0-1', passed: true }) expect(launcher['_resolve']).toBeCalledWith(0) }) it('should do nothing if not all specs are run', async () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) - launcher.runSpecs = jest.fn().mockReturnValue(0) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(0) launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }] - launcher['_resolve'] = jest.fn() + launcher['_resolve'] = vi.fn() await launcher.endHandler({ cid: '0-1', exitCode: 0 } as any) expect(launcher.interface.emit).toBeCalledWith('job:end', { cid: '0-1', passed: true }) expect(launcher['_resolve']).toBeCalledTimes(0) }) it('should do nothing if watch mode is still running', async () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) launcher['_isWatchMode'] = true - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }] - launcher['_resolve'] = jest.fn() + launcher['_resolve'] = vi.fn() await launcher.endHandler({ cid: '0-1', exitCode: 1 } as any) expect(launcher.interface.emit).toBeCalledWith('job:end', { cid: '0-1', passed: false }) expect(launcher['_resolve']).toBeCalledTimes(0) }) it('should resolve and not emit on watch mode stop', async () => { - launcher.getNumberOfRunningInstances = jest.fn().mockReturnValue(1) + launcher.getNumberOfRunningInstances = vi.fn().mockReturnValue(1) launcher['_isWatchMode'] = true launcher['_hasTriggeredExitRoutine'] = true - launcher.runSpecs = jest.fn().mockReturnValue(1) + launcher.runSpecs = vi.fn().mockReturnValue(1) launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }] - launcher['_resolve'] = jest.fn() + launcher['_resolve'] = vi.fn() await launcher.endHandler({ cid: '0-1', exitCode: 1 } as any) expect(launcher.interface.emit).not.toBeCalled() expect(launcher['_resolve']).toBeCalledWith(0) @@ -237,7 +244,7 @@ describe('launcher', () => { }) it('should requeue retried specfiles at beginning of queue', async () => { - launcher.configParser.getConfig = jest.fn().mockReturnValue({ specFileRetriesDeferred: false, onWorkerEnd: jest.fn() }) + launcher.configParser.getConfig = vi.fn().mockReturnValue({ specFileRetriesDeferred: false, onWorkerEnd: vi.fn() }) launcher['_schedule'] = [{ cid: 0, specs: [{ files: ['b.js'] }] }] as any await launcher.endHandler({ cid: '0-5', exitCode: 1, retries: 1, specs: ['a.js'] }) expect(launcher['_schedule']).toMatchObject([{ cid: 0, specs: [{ rid: '0-5', files: ['a.js'], retries: 0 }, { files: ['b.js'] }] }]) @@ -253,36 +260,36 @@ describe('launcher', () => { describe('exitHandler', () => { it('should do nothing if no callback is given', () => { launcher['_hasTriggeredExitRoutine'] = false - launcher.runner = { shutdown: jest.fn() + launcher.runner = { shutdown: vi.fn() .mockReturnValue(Promise.resolve()) } as any launcher.exitHandler() expect(launcher['_hasTriggeredExitRoutine']).toBe(false) expect(launcher.interface.sigintTrigger).toBeCalledTimes(0) - expect(launcher.runner.shutdown).toBeCalledTimes(0) + expect(launcher.runner?.shutdown).toBeCalledTimes(0) }) it('should do nothing if shutdown was called before', () => { launcher['_hasTriggeredExitRoutine'] = true - launcher.runner = { shutdown: jest.fn().mockReturnValue(Promise.resolve()) } as any + launcher.runner = { shutdown: vi.fn().mockReturnValue(Promise.resolve()) } as any expect(launcher.exitHandler(() => 'foo')).toBe('foo') expect(launcher['_hasTriggeredExitRoutine']).toBe(true) expect(launcher.interface.sigintTrigger).toBeCalledTimes(0) - expect(launcher.runner.shutdown).toBeCalledTimes(0) + expect(launcher.runner?.shutdown).toBeCalledTimes(0) }) it('should shutdown', () => { launcher['_hasTriggeredExitRoutine'] = false - launcher.runner = { shutdown: jest.fn().mockReturnValue(Promise.resolve()) } as any + launcher.runner = { shutdown: vi.fn().mockReturnValue(Promise.resolve()) } as any - launcher.exitHandler(jest.fn()) + launcher.exitHandler(vi.fn()) expect(launcher['_hasTriggeredExitRoutine']).toBe(true) expect(launcher.interface.sigintTrigger).toBeCalledTimes(1) - expect(launcher.runner.shutdown).toBeCalledTimes(1) + expect(launcher.runner?.shutdown).toBeCalledTimes(1) }) }) @@ -325,7 +332,7 @@ describe('launcher', () => { ] // Mock the return value of getSpecs so we are not doing cross // module testing - launcher.configParser = { getSpecs: jest.fn().mockReturnValue( + launcher.configParser = { getSpecs: vi.fn().mockReturnValue( ['/a.js', ['/b.js', '/c.js', '/d.js'], '/e.js'] ) } as any expect(launcher.formatSpecs(capabilities as any, specFileRetries)).toStrictEqual(expected) @@ -334,11 +341,11 @@ describe('launcher', () => { describe('runSpecs', () => { beforeEach(() => { - launcher.startInstance = jest.fn() + launcher.startInstance = vi.fn() }) it('should not start running anything if exit routine is triggered', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100 }) } as any launcher['_hasTriggeredExitRoutine'] = true @@ -347,7 +354,7 @@ describe('launcher', () => { }) it('should run all specs', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100 }) } as any launcher['_schedule'] = [{ @@ -384,7 +391,7 @@ describe('launcher', () => { }) it('should run arrayed specs in a single instance', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100 }) } as any launcher['_schedule'] = [{ @@ -422,7 +429,7 @@ describe('launcher', () => { it('should not run anything if runner failed', () => { launcher['_runnerFailed'] = 2 - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100, bail: 1 }) } as any @@ -454,7 +461,7 @@ describe('launcher', () => { }) it('should run as much as maxInstances allows', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 5 }) } as any launcher['_schedule'] = [{ @@ -485,7 +492,7 @@ describe('launcher', () => { }) it('should not allow to schedule more runner if no instances are available', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100 }) } as any launcher['_schedule'] = [{ @@ -522,7 +529,7 @@ describe('launcher', () => { }) it('should not run if all specs were executed', () => { - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ maxInstances: 100 }) } as any launcher['_schedule'] = [{ @@ -541,12 +548,17 @@ describe('launcher', () => { describe('startInstance', () => { beforeEach(() => { - (launcher.runner.run as jest.Mock) = jest.fn().mockReturnValue({ on: () => {} }) + // launcher.runner?.run = vi.fn().mockReturnValue({ on: () => {} }) launcher['_launcher'] = [] + launcher.runner = { + run: vi.fn().mockReturnValue({ + on: vi.fn() + }) + } as any }) it('should start an instance', async () => { - const onWorkerStartMock = jest.fn() + const onWorkerStartMock = vi.fn() const caps = { browserName: 'chrome' } @@ -564,7 +576,7 @@ describe('launcher', () => { expect(sleep).not.toHaveBeenCalled() expect(launcher['_runnerStarted']).toBe(1) - expect((launcher.runner.run as jest.Mock).mock.calls[0][0]).toHaveProperty('cid', '0-5') + expect(vi.mocked(launcher.runner?.run!).mock.calls[0][0]).toHaveProperty('cid', '0-5') expect(launcher.getRunnerId(0)).toBe('0-0') expect(onWorkerStartMock).toHaveBeenCalledWith( @@ -572,16 +584,16 @@ describe('launcher', () => { caps, ['/foo.test.js'], { hostname: '127.0.0.2' }, - ['--no-wasm-code-gc'] + [] ) }) it('should wait before starting an instance on retry', async () => { - const onWorkerStartMock = jest.fn() + const onWorkerStartMock = vi.fn() const caps = { browserName: 'chrome' } - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ onWorkerStart: onWorkerStartMock, specFileRetries: 2, specFileRetriesDelay: 0.01 @@ -604,16 +616,16 @@ describe('launcher', () => { caps, ['/foo.test.js'], { hostname: '127.0.0.3' }, - ['--no-wasm-code-gc'] + [] ) }) it('should not wait before starting an instance on the first run', async () => { - const onWorkerStartMock = jest.fn() + const onWorkerStartMock = vi.fn() const caps = { browserName: 'chrome' } - launcher.configParser = { getConfig: jest.fn().mockReturnValue({ + launcher.configParser = { getConfig: vi.fn().mockReturnValue({ onWorkerStart: onWorkerStartMock, specFileRetries: 4, specFileRetriesDelay: 0.01 @@ -635,22 +647,18 @@ describe('launcher', () => { caps, ['/foo.test.js'], { hostname: '127.0.0.4' }, - ['--no-wasm-code-gc'] + [] ) }) }) describe('config options', () => { - let ensureDirSyncSpy: any - beforeEach(() => { - ensureDirSyncSpy = jest.spyOn(fs, 'ensureDirSync') - }) it('should create directory when the config options have a outputDir option', () => { - expect(ensureDirSyncSpy).toHaveBeenCalled() - expect(ensureDirSyncSpy).toHaveBeenCalledWith('tempDir') + expect(vi.mocked(fs.ensureDirSync)).toHaveBeenCalled() + expect(vi.mocked(fs.ensureDirSync)).toHaveBeenCalledWith('tempDir') }) afterEach(() => { - ensureDirSyncSpy.mockClear() + vi.mocked(fs.ensureDirSync).mockClear() }) }) @@ -658,33 +666,36 @@ describe('launcher', () => { let config: WebdriverIO.Config = { capabilities: {} } beforeEach(() => { - global.console.error = jest.fn() + global.console.error = vi.fn() config = { // ConfigParser.addFileConfig() will return onPrepare and onComplete as arrays of functions - onPrepare: [jest.fn()], - onComplete: [jest.fn()], - capabilities: {} + onPrepare: [vi.fn()], + onComplete: [vi.fn()], + capabilities: {}, + runner: 'local' } launcher.configParser = { - getCapabilities: jest.fn().mockReturnValue(0), - getConfig: jest.fn().mockReturnValue(config), - autoCompile: jest.fn() + getCapabilities: vi.fn().mockReturnValue(0), + getConfig: vi.fn().mockReturnValue(config), + autoCompile: vi.fn() } as any - launcher.runner = { initialise: jest.fn(), shutdown: jest.fn() } as any - launcher.runMode = jest.fn().mockImplementation((config, caps) => caps) - launcher.interface = { finalise: jest.fn() } as any + launcher.runner = { initialise: vi.fn(), shutdown: vi.fn() } as any + launcher.runMode = vi.fn().mockImplementation((config, caps) => caps) + launcher.interface = { finalise: vi.fn() } as any }) it('exit code 0', async () => { expect(await launcher.run()).toEqual(0) - expect(launcher.runner.shutdown).toBeCalled() + expect(launcher.runner!.shutdown).toBeCalled() expect(launcher.configParser.getCapabilities).toBeCalledTimes(1) expect(launcher.configParser.getConfig).toBeCalledTimes(1) - expect(launcher.runner.initialise).toBeCalledTimes(1) + expect(launcher.runner!.initialise).toBeCalledTimes(1) + // @ts-expect-error expect(config.onPrepare[0]).toBeCalledTimes(1) expect(launcher.runMode).toBeCalledTimes(1) + // @ts-expect-error expect(config.onPrepare[0]).toBeCalledTimes(1) expect(launcher.interface.finalise).toBeCalledTimes(1) }) @@ -692,7 +703,7 @@ describe('launcher', () => { it('should not shutdown runner if was called before', async () => { launcher['_hasTriggeredExitRoutine'] = true expect(await launcher.run()).toEqual(0) - expect(launcher.runner.shutdown).not.toBeCalled() + expect(launcher.runner!.shutdown).not.toBeCalled() }) it('onComplete error', async () => { @@ -700,10 +711,11 @@ describe('launcher', () => { config.onComplete = [() => { throw new Error() }] expect(await launcher.run()).toEqual(1) - expect(launcher.runner.shutdown).toBeCalled() + expect(launcher.runner!.shutdown).toBeCalled() }) it('should shutdown runner on error', async () => { + // @ts-expect-error delete logger.waitForBuffer let error @@ -712,17 +724,17 @@ describe('launcher', () => { } catch (err: any) { error = err } - expect(launcher.runner.shutdown).toBeCalled() + expect(launcher.runner!.shutdown).toBeCalled() expect(error).toBeInstanceOf(Error) }) afterEach(() => { - (global.console.error as jest.Mock).mockRestore() + vi.mocked(global.console.error).mockRestore() }) }) afterEach(() => { - (global.console.log as jest.Mock).mockRestore() - ;(sleep as jest.Mock).mockClear() + vi.mocked(global.console.log).mockRestore() + vi.mocked(sleep).mockClear() }) }) diff --git a/packages/wdio-cli/tests/run.test.ts b/packages/wdio-cli/tests/run.test.ts index 94d4ac97e..414bb5461 100644 --- a/packages/wdio-cli/tests/run.test.ts +++ b/packages/wdio-cli/tests/run.test.ts @@ -1,40 +1,43 @@ +import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest' import { launch, cmdArgs } from '../src/commands/run' import Launcher from '../src/launcher' -jest.mock('../src/launcher', () => jest.fn().mockImplementation((conf, result) => ({ - run: () => Number.isInteger(result) ? Promise.resolve(result) : Promise.reject(result) -}))) +vi.mock('../src/launcher', () => ({ + default: vi.fn().mockImplementation((conf, result) => ({ + run: () => Number.isInteger(result) ? Promise.resolve(result) : Promise.reject(result) + })) +})) describe('launch', () => { beforeEach(() => { - jest.spyOn(console, 'error').mockImplementation(() => {}) + vi.spyOn(console, 'error').mockImplementation(() => {}) }) it('should exit with code 0', async () => { // @ts-ignore mock feature await launch('configFile', 0) expect(Launcher).toBeCalledWith('configFile', 0) - expect((Launcher as jest.Mock).mock.instances).toHaveLength(1) + expect(vi.mocked(Launcher).mock.instances).toHaveLength(1) }) it('should exit with code 1', async () => { // @ts-ignore mock feature await launch('configFile', 1) expect(Launcher).toBeCalledWith('configFile', 1) - expect((Launcher as jest.Mock).mock.instances).toHaveLength(1) + expect(vi.mocked(Launcher).mock.instances).toHaveLength(1) }) it('should catch errors', async () => { // @ts-ignore mock feature await launch('configFile', 'foobar') expect(Launcher).toBeCalledWith('configFile', 'foobar') - expect((Launcher as jest.Mock).mock.instances).toHaveLength(1) + expect(vi.mocked(Launcher).mock.instances).toHaveLength(1) expect(console.error).toBeCalledWith('foobar') }) afterEach(() => { - (Launcher as jest.Mock).mockClear() - ;(console.error as jest.Mock).mockRestore() + vi.mocked(Launcher).mockClear() + vi.mocked(console.error).mockRestore() }) }) diff --git a/packages/wdio-cli/tests/utils.test.ts b/packages/wdio-cli/tests/utils.test.ts index c71f8fcca..eb6d19f1e 100644 --- a/packages/wdio-cli/tests/utils.test.ts +++ b/packages/wdio-cli/tests/utils.test.ts @@ -1,12 +1,13 @@ +import { vi, describe, it, expect, afterEach, beforeEach, test } from 'vitest' +import path from 'node:path' import fs from 'fs-extra' import ejs from 'ejs' -import readDirMock from 'recursive-readdir' -import childProcess from 'node:child_process' +import readDir from 'recursive-readdir' +// @ts-ignore mock feature +import { execSyncRes } from 'node:child_process' import { SevereServiceError } from 'webdriverio' import { ConfigParser } from '@wdio/config' -const readDir = readDirMock as jest.Mock - import { runLauncherHook, runOnCompleteHook, @@ -27,7 +28,10 @@ import { } from '../src/utils' import { COMPILER_OPTION_ANSWERS } from '../src/constants' -jest.mock('child_process', function () { +vi.mock('ejs') +vi.mock('recursive-readdir') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('child_process', function () { const m = { execSyncRes: 'APPIUM_MISSING', execSync: function () { return m.execSyncRes } @@ -35,15 +39,17 @@ jest.mock('child_process', function () { return m }) -jest.mock('../src/commands/config', () => ({ - runConfig: jest.fn() +vi.mock('../src/commands/config', () => ({ + runConfig: vi.fn() })) -jest.mock('fs-extra', () => ({ - existsSync: jest.fn(), - ensureDirSync: jest.fn(), - promises: { - writeFile: jest.fn().mockReturnValue(Promise.resolve()) +vi.mock('fs-extra', () => ({ + default: { + existsSync: vi.fn(), + ensureDirSync: vi.fn(), + promises: { + writeFile: vi.fn().mockReturnValue(Promise.resolve()) + } } })) @@ -51,7 +57,7 @@ jest.mock('fs-extra', () => ({ * it is necessary here to create a new mock for the ConfigParser because it * doesn't seem to be possible to spy on jest mock instances */ -jest.mock('@wdio/config', () => ({ +vi.mock('@wdio/config', () => ({ ConfigParser: class ConfigParserMock { addConfigFile () {} autoCompile () {} @@ -60,13 +66,13 @@ jest.mock('@wdio/config', () => ({ })) beforeEach(() => { - global.console.log = jest.fn() + global.console.log = vi.fn() }) describe('runServiceHook', () => { - const hookSuccess = jest.fn() - const slowSetupFn = jest.fn() - const asyncHookSuccess = jest.fn().mockImplementation(() => new Promise(resolve => { + const hookSuccess = vi.fn() + const slowSetupFn = vi.fn() + const asyncHookSuccess = vi.fn().mockImplementation(() => new Promise(resolve => { setTimeout(() => { slowSetupFn() resolve() @@ -79,7 +85,7 @@ describe('runServiceHook', () => { asyncHookSuccess.mockClear() }) - test('run sync and async hooks successfully', async () => { + it('run sync and async hooks successfully', async () => { await runServiceHook([ { onPrepare: hookSuccess }, { onPrepare: asyncHookSuccess }, @@ -92,7 +98,7 @@ describe('runServiceHook', () => { }) it('executes all hooks and continues after a hook throws error', async () => { - const hookFailing = jest.fn().mockImplementation(() => { throw new Error('buhh') }) + const hookFailing = vi.fn().mockImplementation(() => { throw new Error('buhh') }) await runServiceHook([ { onPrepare: hookSuccess }, @@ -109,7 +115,7 @@ describe('runServiceHook', () => { }) it('executes all hooks and stops after a hook throws SevereServiceError', async () => { - const hookFailing = jest.fn().mockImplementation(() => { throw new SevereServiceError() }) + const hookFailing = vi.fn().mockImplementation(() => { throw new SevereServiceError() }) try { await runServiceHook([ @@ -131,8 +137,8 @@ describe('runServiceHook', () => { }) test('runLauncherHook handles array of functions', () => { - const hookSuccess = jest.fn() - const hookFailing = jest.fn().mockImplementation(() => { throw new Error('buhh') }) + const hookSuccess = vi.fn() + const hookFailing = vi.fn().mockImplementation(() => { throw new Error('buhh') }) runLauncherHook([hookSuccess, hookFailing], 1, 2, 3, 4, 5, 6) expect(hookSuccess).toBeCalledTimes(1) @@ -150,7 +156,7 @@ test('runLauncherHook handles async functions', async () => { }) test('runLauncherHook handles a single function', () => { - const hookSuccess = jest.fn() + const hookSuccess = vi.fn() runLauncherHook(hookSuccess, 1, 2, 3, 4, 5, 6) expect(hookSuccess).toBeCalledTimes(1) @@ -158,8 +164,8 @@ test('runLauncherHook handles a single function', () => { }) test('runOnCompleteHook handles array of functions', () => { - const hookSuccess = jest.fn() - const secondHook = jest.fn() + const hookSuccess = vi.fn() + const secondHook = vi.fn() runOnCompleteHook([hookSuccess, secondHook], { capabilities: {} }, {}, 0, {} as any) expect(hookSuccess).toBeCalledTimes(1) @@ -175,15 +181,15 @@ test('runOnCompleteHook handles async functions', async () => { }) test('runOnCompleteHook handles a single function', () => { - const hookSuccess = jest.fn() + const hookSuccess = vi.fn() runOnCompleteHook(hookSuccess, { capabilities: {} }, {}, 0, {} as any) expect(hookSuccess).toBeCalledTimes(1) }) test('runOnCompleteHook with no failure returns 0', async () => { - const hookSuccess = jest.fn() - const hookFailing = jest.fn() + const hookSuccess = vi.fn() + const hookFailing = vi.fn() const result = await runOnCompleteHook([hookSuccess, hookFailing], { capabilities: {} }, {}, 0, {} as any) @@ -193,8 +199,8 @@ test('runOnCompleteHook with no failure returns 0', async () => { }) test('runOnCompleteHook with failure returns 1', async () => { - const hookSuccess = jest.fn() - const hookFailing = jest.fn().mockImplementation(() => { throw new Error('buhh') }) + const hookSuccess = vi.fn() + const hookFailing = vi.fn().mockImplementation(() => { throw new Error('buhh') }) const result = await runOnCompleteHook([hookSuccess, hookFailing], { capabilities: {} }, {}, 0, {} as any) @@ -204,8 +210,8 @@ test('runOnCompleteHook with failure returns 1', async () => { }) test('runOnCompleteHook fails with SevereServiceError', async () => { - const hookSuccess = jest.fn() - const hookFailing = jest.fn().mockImplementation(() => { throw new SevereServiceError('buhh') }) + const hookSuccess = vi.fn() + const hookFailing = vi.fn().mockImplementation(() => { throw new SevereServiceError('buhh') }) const result = await runOnCompleteHook([hookSuccess, hookFailing], { capabilities: {} }, {}, 0, {} as any) .catch(() => 'some error') @@ -255,30 +261,31 @@ describe('findInConfig', () => { describe('renderConfigurationFile', () => { it('should write file', async () => { - // @ts-ignore mock feature - jest.spyOn(ejs, 'renderFile').mockImplementation((a, b, c) => c(null, true)) + vi.mocked(ejs.renderFile).mockImplementation((a, b, c: any) => c(null, true)) await renderConfigurationFile({ foo: 'bar' } as any) expect(ejs.renderFile).toHaveBeenCalled() expect(fs.promises.writeFile).toHaveBeenCalled() - expect((fs.promises.writeFile as jest.Mock).mock.calls[0][0].endsWith('wdio.conf.js')).toBe(true) + expect((vi.mocked(fs.promises.writeFile).mock.calls[0][0] as string) + .endsWith('wdio.conf.js')).toBe(true) }) it('should write TS file', async () => { // @ts-ignore mock feature - jest.spyOn(ejs, 'renderFile').mockImplementation((a, b, c) => c(null, true)) + vi.mocked(ejs.renderFile).mockImplementation((a, b, c) => c(null, true)) await renderConfigurationFile({ isUsingTypeScript: true } as any) expect(ejs.renderFile).toHaveBeenCalled() expect(fs.promises.writeFile).toHaveBeenCalled() - expect((fs.promises.writeFile as jest.Mock).mock.calls[0][0].endsWith('wdio.conf.ts')).toBe(true) + expect((vi.mocked(fs.promises.writeFile).mock.calls[0][0] as string) + .endsWith('wdio.conf.ts')).toBe(true) }) it('should throw error', async () => { // @ts-ignore mock feature - jest.spyOn(ejs, 'renderFile').mockImplementationOnce((a, b, c) => c('test error', null)) + vi.mocked(ejs.renderFile).mockImplementationOnce((a, b, c) => c('test error', null)) try { await renderConfigurationFile({ foo: 'bar' } as any) @@ -340,8 +347,9 @@ describe('addServiceDeps', () => { }) it('should not add appium if globally installed', () => { - // @ts-ignore mock feature - childProcess.execSyncRes = '1.13.0' + // @ts-ignore + // eslint-disable-next-line no-import-assign, @typescript-eslint/no-unused-vars + execSyncRes = '1.13.0' const packages: any = [] addServiceDeps([{ package: '@wdio/appium-service', short: 'appium' }], packages) expect(packages).toEqual([]) @@ -370,7 +378,7 @@ describe('addServiceDeps', () => { }) afterEach(() => { - (global.console.log as jest.Mock).mockClear() + vi.mocked(global.console.log).mockClear() }) }) @@ -404,7 +412,8 @@ describe('getCapabilities', () => { }) it('should return driver with capabilities for ios', () => { - expect(getCapabilities({ option: 'foo.app', deviceName: 'fooName', udid: 'num', platformVersion: 'fooNum' } as any)).toMatchSnapshot() + expect(getCapabilities({ option: 'foo.app', deviceName: 'fooName', udid: 'num', platformVersion: 'fooNum' } as any)) + .toMatchSnapshot() expect(getCapabilities({ option: 'ios' } as any)).toMatchSnapshot() }) @@ -413,7 +422,7 @@ describe('getCapabilities', () => { }) it('should throw config not found error', () => { - const addConfigFileMock = jest.spyOn(ConfigParser.prototype, 'addConfigFile') + const addConfigFileMock = vi.spyOn(ConfigParser.prototype, 'addConfigFile') addConfigFileMock.mockImplementationOnce(() => { const error: any = new Error('ups') error.code = 'MODULE_NOT_FOUND' @@ -433,15 +442,15 @@ describe('getCapabilities', () => { it('should through capability not found', () => { const cap = { browserName: 'chrome' } - const getCapabilitiesMock = jest.spyOn(ConfigParser.prototype, 'getCapabilities') + const getCapabilitiesMock = vi.spyOn(ConfigParser.prototype, 'getCapabilities') getCapabilitiesMock.mockReturnValue([cap, cap, cap, cap, cap]) expect(() => getCapabilities({ option: '/path/to/config.js', capabilities: 5 } as any)) .toThrowErrorMatchingSnapshot() }) it('should get capability from wdio.conf.js', () => { - const autoCompileMock = jest.spyOn(ConfigParser.prototype, 'autoCompile') - const getCapabilitiesMock = jest.spyOn(ConfigParser.prototype, 'getCapabilities') + const autoCompileMock = vi.spyOn(ConfigParser.prototype, 'autoCompile') + const getCapabilitiesMock = vi.spyOn(ConfigParser.prototype, 'getCapabilities') getCapabilitiesMock.mockReturnValue([ { browserName: 'chrome' }, { @@ -462,9 +471,9 @@ describe('getCapabilities', () => { }) test('hasFile', () => { - (fs.existsSync as jest.Mock).mockReturnValue(true) + vi.mocked(fs.existsSync).mockReturnValue(true) expect(hasFile('package.json')).toBe(true) - ; (fs.existsSync as jest.Mock).mockReturnValue(false) + vi.mocked(fs.existsSync).mockReturnValue(false) expect(hasFile('xyz')).toBe(false) }) @@ -475,10 +484,10 @@ test('hasPackage', () => { describe('generateTestFiles', () => { it('Mocha with page objects', async () => { - readDir.mockReturnValue(Promise.resolve([ + vi.mocked(readDir).mockResolvedValue([ '/foo/bar/loo/page.js.ejs', '/foo/bar/example.e2e.js' - ])) + ] as any) const answers = { framework: 'mocha', usePageObjects: true, @@ -490,14 +499,14 @@ describe('generateTestFiles', () => { await generateTestFiles(answers as any) expect(readDir).toBeCalledTimes(2) - expect(readDir.mock.calls[0][0]).toContain('mocha') - expect(readDir.mock.calls[1][0]).toContain('pageobjects') + expect(vi.mocked(readDir).mock.calls[0][0]).toContain('mocha') + expect(vi.mocked(readDir).mock.calls[1][0]).toContain('pageobjects') /** * test readDir callback */ - const readDirCb = readDir.mock.calls[0][1][0] - const stats = { isDirectory: jest.fn().mockReturnValue(false) } + const readDirCb = vi.mocked(readDir).mock.calls[0][1][0] as Function + const stats = { isDirectory: vi.fn().mockReturnValue(false) } expect(readDirCb('/foo/bar.lala', stats)).toBe(true) expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false) expect(readDirCb('/foo/bar.feature', stats)).toBe(false) @@ -518,17 +527,17 @@ describe('generateTestFiles', () => { expect.any(Function) ) expect(fs.ensureDirSync).toBeCalledTimes(4) - expect((fs.promises.writeFile as jest.Mock).mock.calls[0][0].endsWith('/page/objects/model/page.js')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[0][0] as string).endsWith('/page/objects/model/page.js')) .toBe(true) - expect((fs.promises.writeFile as jest.Mock).mock.calls[1][0].endsWith('/example.e2e.js')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[1][0] as string).endsWith('/example.e2e.js')) .toBe(true) }) it('jasmine with page objects', async () => { - readDir.mockReturnValue(Promise.resolve([ + vi.mocked(readDir).mockResolvedValue([ '/foo/bar/loo/page.js.ejs', '/foo/bar/example.e2e.js' - ])) + ] as any) const answers = { framework: 'jasmine', usePageObjects: true, @@ -540,14 +549,14 @@ describe('generateTestFiles', () => { await generateTestFiles(answers as any) expect(readDir).toBeCalledTimes(2) - expect(readDir.mock.calls[0][0]).toContain('jasmine') - expect(readDir.mock.calls[1][0]).toContain('pageobjects') + expect(vi.mocked(readDir).mock.calls[0][0]).toContain('jasmine') + expect(vi.mocked(readDir).mock.calls[1][0]).toContain('pageobjects') /** * test readDir callback */ - const readDirCb = readDir.mock.calls[0][1][0] - const stats = { isDirectory: jest.fn().mockReturnValue(false) } + const readDirCb = vi.mocked(readDir).mock.calls[0][1][0] as Function + const stats = { isDirectory: vi.fn().mockReturnValue(false) } expect(readDirCb('/foo/bar.lala', stats)).toBe(true) expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false) expect(readDirCb('/foo/bar.feature', stats)).toBe(false) @@ -568,15 +577,16 @@ describe('generateTestFiles', () => { expect.any(Function) ) expect(fs.ensureDirSync).toBeCalledTimes(4) - expect((fs.promises.writeFile as jest.Mock).mock.calls[0][0].endsWith('/page/objects/model/page.js')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[0][0] as string) + .endsWith('/page/objects/model/page.js')) .toBe(true) - expect((fs.promises.writeFile as jest.Mock).mock.calls[1][0].endsWith('/example.e2e.js')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[1][0] as string) + .endsWith('/example.e2e.js')) .toBe(true) }) it('Jasmine with page generation and no pageObjects', async () => { - readDir.mockReturnValue(Promise.resolve([ - ])) + vi.mocked(readDir).mockResolvedValue([] as any) const answers = { specs: './tests/e2e/**/*.js', framework: 'jasmine', @@ -591,7 +601,7 @@ describe('generateTestFiles', () => { }) it('Cucumber with page generation and no pageObjects', async () => { - readDir.mockReturnValue(Promise.resolve([])) + vi.mocked(readDir).mockResolvedValue([] as any) const answers = { specs: './tests/e2e/**/*.js', framework: 'cucumber', @@ -606,10 +616,10 @@ describe('generateTestFiles', () => { }) it('Cucumber without page objects', async () => { - readDir.mockReturnValue(Promise.resolve([ + vi.mocked(readDir).mockResolvedValue([ '/foo/bar/loo/step_definition/example.step.js', '/foo/bar/example.feature' - ])) + ] as any) const answers = { specs: './tests/e2e/*.js', framework: 'cucumber', @@ -620,7 +630,7 @@ describe('generateTestFiles', () => { await generateTestFiles(answers as any) expect(readDir).toBeCalledTimes(1) - expect(readDir.mock.calls[0][0]).toContain('cucumber') + expect(vi.mocked(readDir).mock.calls[0][0]).toContain('cucumber') expect(ejs.renderFile).toBeCalledTimes(2) expect(ejs.renderFile).toBeCalledWith( '/foo/bar/loo/step_definition/example.step.js', @@ -636,11 +646,11 @@ describe('generateTestFiles', () => { }) it('Cucumber with page objects and TypeScript', async () => { - readDir.mockReturnValue(Promise.resolve([ + vi.mocked(readDir).mockResolvedValue([ '/foo/bar/loo/page.js.ejs', '/foo/bar/loo/step_definition/example.step.js', '/foo/bar/example.feature' - ])) + ] as any) const answers = { framework: 'cucumber', usePageObjects: true, @@ -652,7 +662,7 @@ describe('generateTestFiles', () => { await generateTestFiles(answers as any) expect(readDir).toBeCalledTimes(2) - expect(readDir.mock.calls[0][0]).toContain('cucumber') + expect(vi.mocked(readDir).mock.calls[0][0]).toContain('cucumber') expect(ejs.renderFile).toBeCalledTimes(6) expect(ejs.renderFile).toBeCalledWith( '/foo/bar/loo/step_definition/example.step.js', @@ -665,9 +675,9 @@ describe('generateTestFiles', () => { expect.any(Function) ) expect(fs.ensureDirSync).toBeCalledTimes(6) - expect((fs.promises.writeFile as jest.Mock).mock.calls[0][0].endsWith('/some/page/objects/page.ts')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[0][0] as string).endsWith('/some/page/objects/page.ts')) .toBe(true) - expect((fs.promises.writeFile as jest.Mock).mock.calls[2][0].endsWith('/example.feature')) + expect((vi.mocked(fs.promises.writeFile).mock.calls[2][0] as string).endsWith('/example.feature')) .toBe(true) }) }) @@ -751,9 +761,9 @@ test('getDefaultFiles', () => { }) afterEach(() => { - (console.log as jest.Mock).mockRestore() - readDir.mockClear() - ; (fs.promises.writeFile as jest.Mock).mockClear() - ; (fs.ensureDirSync as jest.Mock).mockClear() - ; (ejs.renderFile as jest.Mock).mockClear() + vi.mocked(console.log).mockRestore() + vi.mocked(readDir).mockClear() + vi.mocked(fs.promises.writeFile).mockClear() + vi.mocked(fs.ensureDirSync).mockClear() + vi.mocked(ejs.renderFile).mockClear() }) diff --git a/packages/wdio-cli/tests/watcher.test.ts b/packages/wdio-cli/tests/watcher.test.ts index 3b2d95fc4..3ce8cac9e 100644 --- a/packages/wdio-cli/tests/watcher.test.ts +++ b/packages/wdio-cli/tests/watcher.test.ts @@ -1,3 +1,4 @@ +import { vi, describe, it, expect, afterEach } from 'vitest' import path from 'node:path' import chokidar from 'chokidar' import EventEmitter from 'node:events' @@ -6,8 +7,12 @@ import type { Workers } from '@wdio/types' import { RunCommandArguments } from '../src/types' import Watcher from '../src/watcher' -jest.mock('../src/launcher', () => { - const { ConfigParser } = require('@wdio/config') +vi.mock('chokidar') +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) +vi.mock('@wdio/config', () => import(path.join(process.cwd(), '__mocks__', '@wdio/config'))) +vi.mock('@wdio/utils', () => import(path.join(process.cwd(), '__mocks__', '@wdio/utils'))) +vi.mock('../src/launcher', async () => { + const { ConfigParser } = await import('@wdio/config') interface LauncherMockRunCommandArguments extends Omit { isMultiremote?: boolean; @@ -28,12 +33,12 @@ jest.mock('../src/launcher', () => { this.isMultiremote = args.isMultiremote || false this.runner = {} this.interface = { - emit: jest.fn(), - setup: jest.fn() + emit: vi.fn(), + setup: vi.fn() } } } - return LauncherMock + return { default: LauncherMock } }) interface WorkerMockRunPayload extends Partial { @@ -49,7 +54,7 @@ class WorkerMock extends EventEmitter implements Workers.Worker { capabilities: WebDriver.DesiredCapabilities sessionId: string isBusy: boolean - postMessage = jest.fn() + postMessage = vi.fn() constructor ({ cid, specs, sessionId, isBusy = false }: WorkerMockRunPayload) { super() @@ -59,7 +64,7 @@ class WorkerMock extends EventEmitter implements Workers.Worker { this.capabilities = this.caps this.sessionId = sessionId || `${Math.random()}` this.isBusy = isBusy - this.on = jest.fn() + this.on = vi.fn() } } @@ -85,12 +90,12 @@ describe('watcher', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) watcher['_launcher'] = { - run: jest.fn(), + run: vi.fn(), interface: { - finalise: jest.fn() + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch: [] }) + getConfig: vi.fn().mockReturnValue({ filesToWatch: [] }) }, runner: { workerPool: { @@ -107,12 +112,12 @@ describe('watcher', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) watcher['_launcher'] = { - run: jest.fn(), + run: vi.fn(), interface: { - finalise: jest.fn() + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch: [] }) + getConfig: vi.fn().mockReturnValue({ filesToWatch: [] }) }, runner: { workerPool: { @@ -129,12 +134,12 @@ describe('watcher', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) watcher['_launcher'] = { - run: jest.fn(), + run: vi.fn(), interface: { - finalise: jest.fn() + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch: ['/foo/bar'] }) + getConfig: vi.fn().mockReturnValue({ filesToWatch: ['/foo/bar'] }) }, runner: { workerPool: { @@ -146,7 +151,7 @@ describe('watcher', () => { expect(chokidar.watch).toHaveBeenCalledTimes(2) - const worker = watcher['_launcher'].runner.workerPool['0-0'] + const worker = watcher['_launcher'].runner!.workerPool['0-0'] expect(worker.on).toBeCalledTimes(1) const eventHandler = worker.on.mock.calls[0][1] @@ -163,12 +168,12 @@ describe('watcher', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) watcher['_launcher'] = { - run: jest.fn(), + run: vi.fn(), interface: { - finalise: jest.fn() + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch: ['/foo/bar'] }) + getConfig: vi.fn().mockReturnValue({ filesToWatch: ['/foo/bar'] }) }, runner: { workerPool: { @@ -176,17 +181,17 @@ describe('watcher', () => { } } } as any - watcher.run = jest.fn() + watcher.run = vi.fn() await watcher.watch() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[0][1]('/some/path.js') + vi.mocked(chokidar.on).mock.calls[0][1]('/some/path.js') // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[1][1]('/some/other/path.js') + vi.mocked(chokidar.on).mock.calls[1][1]('/some/other/path.js') // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[2][1]('/some/another/path.js') + vi.mocked(chokidar.on).mock.calls[2][1]('/some/another/path.js') // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[3][1]('/some/another/path.js') + vi.mocked(chokidar.on).mock.calls[3][1]('/some/another/path.js') expect(watcher.run).toHaveBeenNthCalledWith(1, { spec: '/some/path.js' }) expect(watcher.run).toHaveBeenNthCalledWith(2, { spec: '/some/other/path.js' }) expect(watcher.run).toHaveBeenNthCalledWith(3, {}) @@ -201,7 +206,7 @@ describe('watcher', () => { '0-1': new WorkerMock({ cid: '0-1', specs: ['/foo/bar2.js'], isBusy: true }), '1-0': new WorkerMock({ cid: '1-0', specs: ['/bar/foo.js'] }) } - watcher['_launcher'].runner.workerPool = workerPool + watcher['_launcher'].runner!.workerPool = workerPool expect(watcher.getWorkers(null, true)).toEqual(workerPool) expect(watcher.getWorkers()).toEqual({ @@ -216,14 +221,14 @@ describe('watcher', () => { it('should run workers on existing session', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) - watcher['_launcher'].runner.workerPool = { + watcher['_launcher'].runner!.workerPool = { // @ts-ignore mock feature '0-0': new WorkerMock({ cid: '0-0', specs: ['/foo/bar.js'] }), '0-1': new WorkerMock({ cid: '0-1', specs: ['/foo/bar2.js'], isBusy: true }), // @ts-ignore mock feature '1-0': new WorkerMock({ cid: '1-0', specs: ['/bar/foo.js'] }) } - watcher['_launcher'].interface.emit = jest.fn() + watcher['_launcher'].interface.emit = vi.fn() watcher.run({ spec: '/foo/bar.js' } as any) expect(watcher['_launcher'].interface.emit).toHaveBeenCalledWith('job:start', { cid: '0-0', @@ -231,7 +236,7 @@ describe('watcher', () => { specs: ['/foo/bar.js'] }) - const { postMessage, sessionId } = watcher['_launcher'].runner.workerPool['0-0'] + const { postMessage, sessionId } = watcher['_launcher'].runner!.workerPool['0-0'] expect(postMessage).toHaveBeenCalledWith('run', { sessionId, spec: '/foo/bar.js' }) expect(watcher['_launcher'].interface.totalWorkerCnt).toBe(1) }) @@ -239,14 +244,14 @@ describe('watcher', () => { it('should not clean if no watcher is running', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) - watcher['_launcher'].runner.workerPool = { + watcher['_launcher'].runner!.workerPool = { // @ts-ignore mock feature '0-0': new WorkerMock({ cid: '0-0', specs: ['/foo/bar.js'] }), '0-1': new WorkerMock({ cid: '0-1', specs: ['/foo/bar2.js'], isBusy: true }), // @ts-ignore mock feature '1-0': new WorkerMock({ cid: '1-0', specs: ['/bar/foo.js'] }) } - watcher['_launcher'].interface.emit = jest.fn() + watcher['_launcher'].interface.emit = vi.fn() watcher.run({ spec: '/foo/bar2.js' } as any) expect(watcher['_launcher'].interface.emit).toHaveBeenCalledTimes(0) }) @@ -255,8 +260,8 @@ describe('watcher', () => { const wdioConf = path.join(__dirname, '__fixtures__', 'wdio.conf') const watcher = new Watcher(wdioConf, {}) watcher['_launcher'].interface.totalWorkerCnt = 1 - watcher.cleanUp = jest.fn() - watcher['_launcher'].runner.workerPool = { + watcher.cleanUp = vi.fn() + watcher['_launcher'].runner!.workerPool = { // @ts-ignore mock feature '0-0': new WorkerMock({ cid: '0-0', specs: ['/foo/bar.js'] }), '0-1': new WorkerMock({ cid: '0-1', specs: ['/foo/bar2.js'], isBusy: true }), @@ -267,7 +272,7 @@ describe('watcher', () => { expect(watcher['_launcher'].interface.totalWorkerCnt).toBe(2) - const worker00 = watcher['_launcher'].runner.workerPool['0-0'] + const worker00 = watcher['_launcher'].runner!.workerPool['0-0'] expect(worker00.postMessage).toHaveBeenCalledWith( 'run', { sessionId: worker00.sessionId }) @@ -276,7 +281,7 @@ describe('watcher', () => { caps: { browserName: 'chrome' }, specs: ['/foo/bar.js'] }) - const worker10 = watcher['_launcher'].runner.workerPool['0-0'] + const worker10 = watcher['_launcher'].runner!.workerPool['0-0'] expect(worker10.postMessage).toHaveBeenCalledWith( 'run', { sessionId: worker10.sessionId }) @@ -294,13 +299,13 @@ describe('watcher', () => { const watcher = new Watcher(wdioConf, {}) watcher['_launcher'] = { __args: { spec }, - run: jest.fn(), + run: vi.fn(), interface: { - emit: jest.fn(), - finalise: jest.fn() + emit: vi.fn(), + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch }) + getConfig: vi.fn().mockReturnValue({ filesToWatch }) }, runner: { workerPool: { @@ -309,31 +314,31 @@ describe('watcher', () => { } } } as any - const runSpy = jest.spyOn(watcher, 'run') + const runSpy = vi.spyOn(watcher, 'run') const emitSpy = watcher['_launcher'].interface.emit - watcher.cleanUp = jest.fn() + watcher.cleanUp = vi.fn() await watcher.watch() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[0][1](spec[0]) + vi.mocked(chokidar.on).mock.calls[0][1](spec[0]) expect(runSpy).toHaveBeenNthCalledWith(1, { spec: spec[0] }) expect(emitSpy).toHaveBeenCalledTimes(1) // Only one Worker called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[1][1](someOtherExcludedPath) + vi.mocked(chokidar.on).mock.calls[1][1](someOtherExcludedPath) expect(runSpy).toHaveBeenNthCalledWith(2, { spec: someOtherExcludedPath }) expect(emitSpy).not.toHaveBeenCalled() // No Workers called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[2][1](filesToWatch[0]) + vi.mocked(chokidar.on).mock.calls[2][1](filesToWatch[0]) expect(runSpy).toHaveBeenNthCalledWith(3, {}) expect(emitSpy).toHaveBeenCalledTimes(2) // Both Workers called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[3][1](filesToWatch[0]) + vi.mocked(chokidar.on).mock.calls[3][1](filesToWatch[0]) expect(runSpy).toHaveBeenNthCalledWith(4, {}) expect(emitSpy).toHaveBeenCalledTimes(2) // Both Workers called }) @@ -347,52 +352,52 @@ describe('watcher', () => { // @ts-ignore watcher['_launcher'] = { __args: { spec }, - run: jest.fn(), + run: vi.fn(), interface: { - emit: jest.fn(), - finalise: jest.fn() + emit: vi.fn(), + finalise: vi.fn() }, configParser: { - getConfig: jest.fn().mockReturnValue({ filesToWatch }) + getConfig: vi.fn().mockReturnValue({ filesToWatch }) }, runner: { workerPool: { - '0-0': new WorkerMock({ cid: '0-0', specs: [spec[0]] }), - '0-1': new WorkerMock({ cid: '0-1', specs: [spec[1]] }) + '0-0': new WorkerMock({ cid: '0-0', specs: [spec[0] as any] }), + '0-1': new WorkerMock({ cid: '0-1', specs: [spec[1] as any] }) } } } as any - const runSpy = jest.spyOn(watcher, 'run') + const runSpy = vi.spyOn(watcher, 'run') const emitSpy = watcher['_launcher'].interface.emit - watcher.cleanUp = jest.fn() + watcher.cleanUp = vi.fn() await watcher.watch() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[0][1](spec[0]) + vi.mocked(chokidar.on).mock.calls[0][1](spec[0]) expect(runSpy).toHaveBeenNthCalledWith(1, { spec: spec[0] }) expect(emitSpy).toHaveBeenCalledTimes(1) // Only one Worker called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[1][1](someOtherExcludedPath) + vi.mocked(chokidar.on).mock.calls[1][1](someOtherExcludedPath) expect(runSpy).toHaveBeenNthCalledWith(2, { spec: someOtherExcludedPath }) expect(emitSpy).not.toHaveBeenCalled() // No Workers called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[2][1](filesToWatch[0]) + vi.mocked(chokidar.on).mock.calls[2][1](filesToWatch[0]) expect(runSpy).toHaveBeenNthCalledWith(3, {}) expect(emitSpy).toHaveBeenCalledTimes(2) // Both Workers called - ;(emitSpy as jest.Mock).mockClear() + vi.mocked(emitSpy).mockClear() // @ts-ignore mock feature - ;(chokidar.on as jest.Mock).mock.calls[3][1](filesToWatch[0]) + vi.mocked(chokidar.on).mock.calls[3][1](filesToWatch[0]) expect(runSpy).toHaveBeenNthCalledWith(4, {}) expect(emitSpy).toHaveBeenCalledTimes(2) // Both Workers called }) afterEach(() => { - (chokidar.watch as jest.Mock).mockClear() + vi.mocked(chokidar.watch).mockClear() // @ts-ignore mock feature chokidar.on.mockClear() }) diff --git a/vitest.config.ts b/vitest.config.ts index 2c101c374..ffe990ab9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -10,7 +10,6 @@ export default defineConfig({ 'dist', '.idea', '.git', '.cache', '**/node_modules/**', 'packages/eslint-plugin-wdio/**/*', - 'packages/wdio-cli/**/*.test.ts', 'packages/webdriverio/**/*.test.ts', 'packages/wdio-jasmine-framework/**/*.test.ts' ],