rewrite @wdio/utils
This commit is contained in:
parent
62023041e8
commit
34f194a992
42 changed files with 362 additions and 632 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -94,5 +94,6 @@ wallaby.conf.js
|
|||
tags
|
||||
tests/.retry_succeeded
|
||||
*.tsbuildinfo
|
||||
*.mjs
|
||||
website/docs/Contribute.md
|
||||
website/community/Resources.md
|
||||
|
|
|
|||
3
__mocks__/@wdio/foobar-service.ts
Normal file
3
__mocks__/@wdio/foobar-service.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default class FoobarService {
|
||||
public foo = 'foobar'
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
import { vi } from 'vitest'
|
||||
|
||||
const mock = () => logMock
|
||||
mock.setLevel = vi.fn()
|
||||
mock.setLogLevelsConfig = vi.fn()
|
||||
mock.waitForBuffer = vi.fn()
|
||||
export const logMock = {
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
|
|
@ -11,5 +7,8 @@ export const logMock = {
|
|||
info: vi.fn(),
|
||||
trace: vi.fn()
|
||||
}
|
||||
|
||||
const mock = () => logMock
|
||||
mock.setLevel = vi.fn()
|
||||
mock.setLogLevelsConfig = vi.fn()
|
||||
mock.waitForBuffer = vi.fn()
|
||||
export default mock
|
||||
3
__mocks__/@wdio/scoped-service.ts
Normal file
3
__mocks__/@wdio/scoped-service.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default class MyService {
|
||||
public isScoped = true
|
||||
}
|
||||
|
|
@ -35,6 +35,10 @@ module.exports = {
|
|||
isolatedModules: true
|
||||
}
|
||||
},
|
||||
exclude: [
|
||||
'packages/wdio-logger/**/*.test.ts',
|
||||
'packages/wdio-utils/**/*.test.ts'
|
||||
],
|
||||
testMatch: [
|
||||
'**/tests/**/*.test.(js|ts)'
|
||||
],
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import nodeLogger2 from '../build/node'
|
|||
import type log from 'loglevel'
|
||||
|
||||
vi.useFakeTimers()
|
||||
vi.mock('chalk', () => import('./__mocks__/chalk'))
|
||||
vi.mock('chalk')
|
||||
|
||||
describe('wdio-logger node', () => {
|
||||
describe('log level', () => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,20 @@
|
|||
"author": "Christian Bromann <mail@bromann.dev>",
|
||||
"homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-utils",
|
||||
"license": "MIT",
|
||||
"main": "./build/index",
|
||||
"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",
|
||||
"engines": {
|
||||
"node": "^16.13 || >=18"
|
||||
},
|
||||
|
|
@ -29,6 +42,5 @@
|
|||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"types": "./build/index.d.ts"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
/* istanbul ignore file */
|
||||
|
||||
import initialisePlugin from './initialisePlugin'
|
||||
import { initialiseWorkerService, initialiseLauncherService } from './initialiseServices'
|
||||
import webdriverMonad from './monad'
|
||||
import initialisePlugin from './initialisePlugin.js'
|
||||
import { initialiseWorkerService, initialiseLauncherService } from './initialiseServices.js'
|
||||
import webdriverMonad from './monad.js'
|
||||
import {
|
||||
commandCallStructure, isValidParameter, getArgumentType, safeRequire,
|
||||
commandCallStructure, isValidParameter, getArgumentType, safeImport,
|
||||
isFunctionAsync, transformCommandLogResult, canAccess, sleep
|
||||
} from './utils'
|
||||
} from './utils.js'
|
||||
import {
|
||||
wrapCommand, runFnInFiberContext, executeHooksWithArgs,
|
||||
wrapCommand, executeHooksWithArgs,
|
||||
hasWdioSyncSupport, executeSync, executeAsync
|
||||
} from './shim'
|
||||
import { testFnWrapper, runTestInFiberContext } from './test-framework'
|
||||
} from './shim.js'
|
||||
import { testFnWrapper, runTestInFiberContext } from './test-framework/index.js'
|
||||
import {
|
||||
isW3C, capabilitiesEnvironmentDetector,
|
||||
sessionEnvironmentDetector, devtoolsEnvironmentDetector
|
||||
} from './envDetector'
|
||||
import { UNICODE_CHARACTERS } from './constants'
|
||||
} from './envDetector.js'
|
||||
import { UNICODE_CHARACTERS } from './constants.js'
|
||||
|
||||
export {
|
||||
initialisePlugin,
|
||||
|
|
@ -28,7 +28,7 @@ export {
|
|||
commandCallStructure,
|
||||
isValidParameter,
|
||||
getArgumentType,
|
||||
safeRequire,
|
||||
safeImport,
|
||||
canAccess,
|
||||
sleep,
|
||||
|
||||
|
|
@ -38,7 +38,6 @@ export {
|
|||
wrapCommand,
|
||||
executeSync,
|
||||
executeAsync,
|
||||
runFnInFiberContext,
|
||||
runTestInFiberContext,
|
||||
testFnWrapper,
|
||||
executeHooksWithArgs,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import path from 'path'
|
||||
import path from 'node:path'
|
||||
import { Services } from '@wdio/types'
|
||||
|
||||
import { safeRequire } from './utils'
|
||||
import { safeImport } from './utils.js'
|
||||
|
||||
/**
|
||||
* initialise WebdriverIO compliant plugins like reporter or services in the following way:
|
||||
|
|
@ -9,12 +9,12 @@ import { safeRequire } from './utils'
|
|||
* 2. otherwise try to require "@wdio/<name>-<type>"
|
||||
* 3. otherwise try to require "wdio-<name>-<type>"
|
||||
*/
|
||||
export default function initialisePlugin (name: string, type?: string): Services.ServicePlugin | Services.RunnerPlugin {
|
||||
export default async function initialisePlugin (name: string, type?: string): Promise<Services.ServicePlugin | Services.RunnerPlugin> {
|
||||
/**
|
||||
* directly import packages that are scoped or start with an absolute path
|
||||
*/
|
||||
if (name[0] === '@' || path.isAbsolute(name)) {
|
||||
const service = safeRequire(name)
|
||||
const service = await safeImport(name)
|
||||
|
||||
if (service) {
|
||||
return service
|
||||
|
|
@ -28,7 +28,11 @@ export default function initialisePlugin (name: string, type?: string): Services
|
|||
/**
|
||||
* check for scoped version of plugin first (e.g. @wdio/sauce-service)
|
||||
*/
|
||||
const scopedPlugin = safeRequire(`@wdio/${name.toLowerCase()}-${type}`)
|
||||
console.log('AHHHJ', `@wdio/${name.toLowerCase()}-${type}`)
|
||||
|
||||
const scopedPlugin = await safeImport(`@wdio/${name.toLowerCase()}-${type}`)
|
||||
console.log(scopedPlugin)
|
||||
|
||||
if (scopedPlugin) {
|
||||
return scopedPlugin
|
||||
}
|
||||
|
|
@ -36,7 +40,7 @@ export default function initialisePlugin (name: string, type?: string): Services
|
|||
/**
|
||||
* check for old type of
|
||||
*/
|
||||
const plugin = safeRequire(`wdio-${name.toLowerCase()}-${type}`)
|
||||
const plugin = await safeImport(`wdio-${name.toLowerCase()}-${type}`)
|
||||
if (plugin) {
|
||||
return plugin
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Capabilities, Services, Options } from '@wdio/types'
|
||||
import logger from '@wdio/logger'
|
||||
|
||||
import initialisePlugin from './initialisePlugin'
|
||||
import initialisePlugin from './initialisePlugin.js'
|
||||
|
||||
const log = logger('@wdio/utils:initialiseServices')
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ type ServiceWithOptions = [Service, Services.ServiceOption]
|
|||
* @param {Object} caps capabilities of running session
|
||||
* @return {[(Object|Class), Object][]} list of services with their config objects
|
||||
*/
|
||||
function initialiseServices (services: ServiceWithOptions[]): IntialisedService[] {
|
||||
async function initialiseServices (services: ServiceWithOptions[]): Promise<IntialisedService[]> {
|
||||
const initialisedServices: IntialisedService[] = []
|
||||
for (let [serviceName, serviceConfig = {}] of services) {
|
||||
/**
|
||||
|
|
@ -65,7 +65,7 @@ function initialiseServices (services: ServiceWithOptions[]): IntialisedService[
|
|||
* ```
|
||||
*/
|
||||
log.debug(`initialise service "${serviceName}" as NPM package`)
|
||||
const service = initialisePlugin(serviceName, 'service')
|
||||
const service = await initialisePlugin(serviceName, 'service')
|
||||
initialisedServices.push([service as Services.ServiceClass, serviceConfig, serviceName])
|
||||
}
|
||||
|
||||
|
|
@ -91,12 +91,12 @@ function sanitizeServiceArray (service: Services.ServiceEntry): ServiceWithOptio
|
|||
* as a list of services that don't need to be
|
||||
* required in the worker
|
||||
*/
|
||||
export function initialiseLauncherService (config: Omit<Options.Testrunner, 'capabilities' | keyof Services.HookFunctions>, caps: Capabilities.DesiredCapabilities) {
|
||||
export async function initialiseLauncherService (config: Omit<Options.Testrunner, 'capabilities' | keyof Services.HookFunctions>, caps: Capabilities.DesiredCapabilities) {
|
||||
const ignoredWorkerServices = []
|
||||
const launcherServices: Services.ServiceInstance[] = []
|
||||
|
||||
try {
|
||||
const services = initialiseServices(config.services!.map(sanitizeServiceArray))
|
||||
const services = await initialiseServices(config.services!.map(sanitizeServiceArray))
|
||||
for (const [service, serviceConfig, serviceName] of services) {
|
||||
/**
|
||||
* add custom services as object or function
|
||||
|
|
@ -150,17 +150,17 @@ export function initialiseLauncherService (config: Omit<Options.Testrunner, 'cap
|
|||
* as they don't export a service for it
|
||||
* @return {Object[]} list if worker initiated worker services
|
||||
*/
|
||||
export function initialiseWorkerService (
|
||||
export async function initialiseWorkerService (
|
||||
config: Options.Testrunner,
|
||||
caps: Capabilities.DesiredCapabilities,
|
||||
ignoredWorkerServices: string[] = []
|
||||
): Services.ServiceInstance[] {
|
||||
): Promise<Services.ServiceInstance[]> {
|
||||
const workerServices = config.services!
|
||||
.map(sanitizeServiceArray)
|
||||
.filter(([serviceName]) => !ignoredWorkerServices.includes(serviceName as string))
|
||||
|
||||
try {
|
||||
const services = initialiseServices(workerServices)
|
||||
const services = await initialiseServices(workerServices)
|
||||
return services.map(([service, serviceConfig, serviceName]) => {
|
||||
/**
|
||||
* add object service
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Clients } from '@wdio/types'
|
||||
|
||||
import { EventEmitter } from 'events'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import logger from '@wdio/logger'
|
||||
|
||||
import { commandCallStructure, overwriteElementCommands } from './utils'
|
||||
import { commandCallStructure, overwriteElementCommands } from './utils.js'
|
||||
|
||||
const SCOPE_TYPES: Record<string, Function> = {
|
||||
browser: /* istanbul ignore next */ function Browser () {},
|
||||
|
|
|
|||
|
|
@ -6,21 +6,12 @@ const log = logger('@wdio/utils:shim')
|
|||
|
||||
let inCommandHook = false
|
||||
let hasWdioSyncSupport = false
|
||||
let runSync: (this: unknown, fn: Function, repeatTest: any, args: unknown[]) => (resolve: Function, reject: Function) => unknown
|
||||
|
||||
interface Retries {
|
||||
limit: number
|
||||
attempts: number
|
||||
}
|
||||
|
||||
interface WDIOSync {
|
||||
runFnInFiberContext: any
|
||||
wrapCommand: any
|
||||
executeHooksWithArgs: any
|
||||
executeSync: any
|
||||
runSync: any
|
||||
}
|
||||
|
||||
declare global {
|
||||
var _HAS_FIBER_CONTEXT: boolean
|
||||
var browser: any
|
||||
|
|
@ -80,27 +71,8 @@ const PROMISE_METHODS = ['then', 'catch', 'finally']
|
|||
/**
|
||||
* shim to make sure that we only wrap commands if wdio-sync is installed as dependency
|
||||
*/
|
||||
let wdioSync: WDIOSync | undefined
|
||||
export let runAsync = false
|
||||
export let asyncSpec = false
|
||||
try {
|
||||
const packageName = '@wdio/sync'
|
||||
wdioSync = require(packageName)
|
||||
hasWdioSyncSupport = true
|
||||
|
||||
/**
|
||||
* only print within worker process
|
||||
*/
|
||||
if (process.send) {
|
||||
log.warn(
|
||||
'You are running tests with @wdio/sync which will be discontinued starting Node.js v16.' +
|
||||
'Read more on https://github.com/webdriverio/webdriverio/discussions/6702'
|
||||
)
|
||||
}
|
||||
} catch (err: any) {
|
||||
runAsync = true
|
||||
asyncSpec = true
|
||||
}
|
||||
export let runAsync = true
|
||||
export let asyncSpec = true
|
||||
|
||||
let executeHooksWithArgs = async function executeHooksWithArgsShim<T> (hookName: string, hooks: Function | Function[] = [], args: any[] = []): Promise<(T | Error)[]> {
|
||||
runAsync = true
|
||||
|
|
@ -151,13 +123,6 @@ let executeHooksWithArgs = async function executeHooksWithArgsShim<T> (hookName:
|
|||
return result
|
||||
}
|
||||
|
||||
let runFnInFiberContext = function (fn: Function) {
|
||||
return function (this: any, ...args: any[]) {
|
||||
runAsync = true
|
||||
return Promise.resolve(fn.apply(this, args))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrap command to enable before and after command to be executed
|
||||
* @param commandName name of the command (e.g. getTitle)
|
||||
|
|
@ -334,11 +299,9 @@ let wrapCommand = function wrapCommand<T>(commandName: string, fn: Function): (.
|
|||
* also if we run command asynchronous and the command suppose to return an element, we
|
||||
* apply `chainElementQuery` to allow chaining of these promises.
|
||||
*/
|
||||
const command = hasWdioSyncSupport && wdioSync && Boolean(global.browser) && !runAsync && !asyncSpec
|
||||
? wdioSync!.wrapCommand(commandName, fn)
|
||||
: ELEMENT_QUERY_COMMANDS.includes(commandName) || commandName.endsWith('$')
|
||||
? chainElementQuery
|
||||
: wrapCommandFn
|
||||
const command = ELEMENT_QUERY_COMMANDS.includes(commandName) || commandName.endsWith('$')
|
||||
? chainElementQuery
|
||||
: wrapCommandFn
|
||||
|
||||
return command.apply(this, args)
|
||||
}
|
||||
|
|
@ -387,11 +350,13 @@ async function executeSyncFn (this: any, fn: Function, retries: Retries, args: a
|
|||
* @return {Promise} that gets resolved once test/hook is done or was retried enough
|
||||
*/
|
||||
async function executeAsync(this: any, fn: Function, retries: Retries, args: any[] = []): Promise<unknown> {
|
||||
// @ts-expect-error
|
||||
const isJasmine = global.jasmine && global.expectAsync
|
||||
const asyncSpecBefore = asyncSpec
|
||||
this.wdioRetries = retries.attempts
|
||||
|
||||
if (!expectSync) {
|
||||
// @ts-ignore
|
||||
if (!expectSync && typeof global.expect === 'function') {
|
||||
// @ts-ignore
|
||||
expectSync = global.expect.bind({})
|
||||
}
|
||||
|
|
@ -467,25 +432,10 @@ export function switchSyncFlag (fn: Function) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* only require `@wdio/sync` if `WDIO_NO_SYNC_SUPPORT` which allows us to
|
||||
* create a smoke test scenario to test actual absence of the package
|
||||
* (internal use only)
|
||||
*/
|
||||
/* istanbul ignore if */
|
||||
if (!process.env.WDIO_NO_SYNC_SUPPORT && hasWdioSyncSupport && wdioSync) {
|
||||
runFnInFiberContext = switchSyncFlag(wdioSync.runFnInFiberContext)
|
||||
executeHooksWithArgs = switchSyncFlag(wdioSync.executeHooksWithArgs)
|
||||
executeSync = switchSyncFlag(wdioSync.executeSync)
|
||||
runSync = switchSyncFlag(wdioSync.runSync)
|
||||
}
|
||||
|
||||
export {
|
||||
executeHooksWithArgs,
|
||||
runFnInFiberContext,
|
||||
wrapCommand,
|
||||
hasWdioSyncSupport,
|
||||
executeSync,
|
||||
executeAsync,
|
||||
runSync
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* istanbul ignore file */
|
||||
|
||||
import { testFnWrapper } from './testFnWrapper'
|
||||
import { runTestInFiberContext } from './testInterfaceWrapper'
|
||||
import { testFnWrapper } from './testFnWrapper.js'
|
||||
import { runTestInFiberContext } from './testInterfaceWrapper.js'
|
||||
|
||||
export { testFnWrapper, runTestInFiberContext }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { isFunctionAsync } from '../utils'
|
||||
import { logHookError } from './errorHandler'
|
||||
import { executeHooksWithArgs, executeAsync, runSync } from '../shim'
|
||||
import { logHookError } from './errorHandler.js'
|
||||
import { executeHooksWithArgs, executeAsync } from '../shim.js'
|
||||
|
||||
import type {
|
||||
WrapperMethods,
|
||||
|
|
@ -39,7 +38,7 @@ export const testFnWrapper = function (
|
|||
number
|
||||
]
|
||||
) {
|
||||
return testFrameworkFnWrapper.call(this, { executeHooksWithArgs, executeAsync, runSync }, ...args)
|
||||
return testFrameworkFnWrapper.call(this, { executeHooksWithArgs, executeAsync }, ...args)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,7 +55,7 @@ export const testFnWrapper = function (
|
|||
*/
|
||||
export const testFrameworkFnWrapper = async function (
|
||||
this: unknown,
|
||||
{ executeHooksWithArgs, executeAsync, runSync }: WrapperMethods,
|
||||
{ executeHooksWithArgs, executeAsync }: WrapperMethods,
|
||||
type: string,
|
||||
{ specFn, specFnArgs }: SpecFunction,
|
||||
{ beforeFn, beforeFnArgs }: BeforeHookParam<unknown>,
|
||||
|
|
@ -68,21 +67,12 @@ export const testFrameworkFnWrapper = async function (
|
|||
const beforeArgs = beforeFnArgs(this)
|
||||
await logHookError(`Before${type}`, await executeHooksWithArgs(`before${type}`, beforeFn, beforeArgs), cid)
|
||||
|
||||
let promise
|
||||
let result
|
||||
let error
|
||||
/**
|
||||
* user wants handle async command using promises, no need to wrap in fiber context
|
||||
*/
|
||||
if (isFunctionAsync(specFn) || !runSync) {
|
||||
promise = executeAsync.call(this, specFn, retries, specFnArgs)
|
||||
} else {
|
||||
promise = new Promise(runSync.call(this, specFn, retries, specFnArgs))
|
||||
}
|
||||
|
||||
const testStart = Date.now()
|
||||
try {
|
||||
result = await promise
|
||||
result = await executeAsync.call(this, specFn, retries, specFnArgs)
|
||||
} catch (err: any) {
|
||||
if (err.stack) {
|
||||
err.stack = filterStackTrace(err.stack)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
* NOTE: not used by cucumber test framework. `testFnWrapper` is called directly there
|
||||
*/
|
||||
|
||||
import { filterSpecArgs } from '../utils'
|
||||
import { testFnWrapper } from './testFnWrapper'
|
||||
import { filterSpecArgs } from '../utils.js'
|
||||
import { testFnWrapper } from './testFnWrapper.js'
|
||||
|
||||
import type {
|
||||
HookFnArgs,
|
||||
|
|
@ -173,7 +173,9 @@ export const wrapTestFunction = function (
|
|||
* Jasmine uses a timeout value as last parameter, in this case the arguments
|
||||
* should be [title, fn, timeout, retryCnt]
|
||||
*/
|
||||
// @ts-expect-error
|
||||
let timeout = global.jasmine?.DEFAULT_TIMEOUT_INTERVAL
|
||||
// @ts-expect-error
|
||||
if (global.jasmine) {
|
||||
// if we have [title, fn, timeout, retryCnt]
|
||||
if (typeof specArguments[specArguments.length - 1] === 'number') {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import type { executeHooksWithArgs, executeAsync, runSync } from '../shim'
|
||||
import type { executeHooksWithArgs, executeAsync } from '../shim'
|
||||
|
||||
export type HookFnArgs<T> = (ctx: T) => [unknown, T]
|
||||
|
||||
export interface WrapperMethods {
|
||||
executeHooksWithArgs: typeof executeHooksWithArgs
|
||||
executeAsync: typeof executeAsync
|
||||
runSync: typeof runSync
|
||||
}
|
||||
|
||||
export interface SpecFunction {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import fs from 'node:fs'
|
||||
|
||||
import type { Services, Clients } from '@wdio/types'
|
||||
|
||||
|
|
@ -158,8 +157,8 @@ export function getArgumentType (arg: any) {
|
|||
* @param {string} name of package
|
||||
* @return {object} package content
|
||||
*/
|
||||
export function safeRequire (name: string): Services.ServicePlugin | null {
|
||||
let requirePath
|
||||
export async function safeImport (name: string): Promise<Services.ServicePlugin | null> {
|
||||
let requirePath = name
|
||||
try {
|
||||
/**
|
||||
* Check if cli command was called from local directory, if not require
|
||||
|
|
@ -169,28 +168,16 @@ export function safeRequire (name: string): Services.ServicePlugin | null {
|
|||
* also allows to link the package to a random place and have plugins
|
||||
* imported correctly (for dev purposes).
|
||||
*/
|
||||
const localNodeModules = path.join(process.cwd(), '/node_modules')
|
||||
/* istanbul ignore if */
|
||||
if (!require?.main?.paths.includes(localNodeModules)) {
|
||||
require?.main?.paths.push(localNodeModules)
|
||||
|
||||
/**
|
||||
* don't set requireOpts when running unit tests as it
|
||||
* confuses Jest require magic
|
||||
*/
|
||||
const requireOpts = process.env.JEST_WORKER_ID
|
||||
? {}
|
||||
: { paths: require?.main?.paths }
|
||||
requirePath = require.resolve(name, requireOpts)
|
||||
} else {
|
||||
requirePath = require.resolve(name)
|
||||
if (import.meta.resolve) {
|
||||
requirePath = await import.meta.resolve(name)
|
||||
}
|
||||
} catch (err: any) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return require(requirePath)
|
||||
return import(requirePath)
|
||||
} catch (e: any) {
|
||||
throw new Error(`Couldn't initialise "${name}".\n${e.stack}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = class FoobarService {
|
||||
constructor () {
|
||||
this.foo = 'foobar'
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = class MyService {
|
||||
constructor () {
|
||||
this.isScoped = true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { Capabilities } from '@wdio/types'
|
||||
import { sessionEnvironmentDetector, capabilitiesEnvironmentDetector } from '../src/envDetector'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +1,63 @@
|
|||
import type { ServiceInstance } from 'webdriverio'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import type { Services } from '@wdio/types'
|
||||
|
||||
import initialisePlugin from '../src/initialisePlugin'
|
||||
|
||||
interface TestService extends ServiceInstance {
|
||||
interface TestService extends Services.ServiceInstance {
|
||||
foo: string
|
||||
isScoped: boolean
|
||||
}
|
||||
|
||||
describe('initialisePlugin', () => {
|
||||
it('should allow to load a scoped service plugin', () => {
|
||||
const Service = initialisePlugin('foobar', 'service')
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
vi.mock('@wdio/foobar-service')
|
||||
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
describe.skip('initialisePlugin', () => {
|
||||
it('should allow to load a scoped service plugin', async () => {
|
||||
const Service = await initialisePlugin('foobar', 'service')
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.foo).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should allow to load unscoped service plugin from wdio', () => {
|
||||
const Service = initialisePlugin('@wdio/foobar-service', 'service')
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
it('should allow to load unscoped service plugin from wdio', async () => {
|
||||
const Service = await initialisePlugin('@wdio/foobar-service', 'service')
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.foo).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should allow to load unscoped service plugin', () => {
|
||||
const Service = initialisePlugin('test', 'service')
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
it('should allow to load unscoped service plugin', async () => {
|
||||
const Service = await initialisePlugin('test', 'service')
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.foo).toBe('bar')
|
||||
})
|
||||
|
||||
it('should allow to load scoped services', () => {
|
||||
const Service = initialisePlugin('@saucelabs/wdio-foobar-reporter', 'reporter')
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
it('should allow to load scoped services', async () => {
|
||||
const Service = await initialisePlugin('@saucelabs/wdio-foobar-reporter', 'reporter')
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.foo).toBe('barfoo')
|
||||
})
|
||||
|
||||
it('should allow to load service referenced with an absolute path', () => {
|
||||
it('should allow to load service referenced with an absolute path', async () => {
|
||||
const path = require.resolve(__dirname + '/__mocks__/@saucelabs/wdio-foobar-reporter')
|
||||
const Service = initialisePlugin(path)
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
const Service = await initialisePlugin(path)
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.foo).toBe('barfoo')
|
||||
})
|
||||
|
||||
it('should prefer scoped over unscoped packages', () => {
|
||||
const Service = initialisePlugin('scoped', 'service')
|
||||
const service = new Service({}, {}, {}) as TestService
|
||||
it('should prefer scoped over unscoped packages', async () => {
|
||||
const Service = await initialisePlugin('scoped', 'service')
|
||||
const service = new Service({} as any, {}, {}) as TestService
|
||||
expect(service.isScoped).toBe(true)
|
||||
})
|
||||
|
||||
it('should throw meaningful error message', () => {
|
||||
expect(() => initialisePlugin('lala', 'service'))
|
||||
it('should throw meaningful error message', async () => {
|
||||
await expect(() => initialisePlugin('lala', 'service'))
|
||||
.toThrow(/Please make sure you have it installed!/)
|
||||
expect(() => initialisePlugin('borked', 'framework'))
|
||||
await expect(() => initialisePlugin('borked', 'framework'))
|
||||
.toThrow(/Error: foobar/)
|
||||
expect(() => initialisePlugin('foobar'))
|
||||
await expect(() => initialisePlugin('foobar'))
|
||||
.toThrow(/No plugin type provided/)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
import path from 'path'
|
||||
import { vi, MockedFunction, describe, it, expect, beforeEach } from 'vitest'
|
||||
|
||||
import logger from '@wdio/logger'
|
||||
import type { Capabilities } from 'webdriver'
|
||||
import type { Config, ServiceInstance } from 'webdriverio'
|
||||
import type { Capabilities, Options, Services } from '@wdio/types'
|
||||
|
||||
import { initialiseLauncherService, initialiseWorkerService } from '../src/initialiseServices'
|
||||
|
||||
vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))
|
||||
const log = logger('test')
|
||||
|
||||
interface TestLauncherService extends ServiceInstance {
|
||||
interface TestLauncherService extends Services.ServiceInstance {
|
||||
isLauncher: boolean
|
||||
}
|
||||
|
||||
class CustomService {
|
||||
options: Record<string, any>
|
||||
config: Config
|
||||
caps: Capabilities
|
||||
constructor (options: Record<string, any>, caps: Capabilities, config: Config) {
|
||||
config: Options.Testrunner
|
||||
caps: Capabilities.Capabilities
|
||||
constructor (options: Record<string, any>, caps: Capabilities.Capabilities, config: Options.Testrunner) {
|
||||
this.options = options
|
||||
this.config = config
|
||||
this.caps = caps
|
||||
|
|
@ -24,38 +27,38 @@ class CustomService {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
(log.error as jest.Mock).mockClear()
|
||||
(log.error as MockedFunction<any>).mockClear()
|
||||
})
|
||||
|
||||
describe('initialiseLauncherService', () => {
|
||||
it('should return empty array if no services prop is given', () => {
|
||||
expect(initialiseLauncherService({ services: [] }, {})).toEqual({
|
||||
it('should return empty array if no services prop is given', async () => {
|
||||
expect(await initialiseLauncherService({ services: [] }, {})).toEqual({
|
||||
ignoredWorkerServices: [],
|
||||
launcherServices: []
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to add initialised services', () => {
|
||||
it('should be able to add initialised services', async () => {
|
||||
const service = {
|
||||
before: jest.fn(),
|
||||
afterTest: jest.fn()
|
||||
before: vi.fn(),
|
||||
afterTest: vi.fn()
|
||||
}
|
||||
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService({ services: [service] }, {})
|
||||
} = await initialiseLauncherService({ services: [service] }, {})
|
||||
expect(ignoredWorkerServices).toHaveLength(0)
|
||||
expect(launcherServices).toHaveLength(1)
|
||||
expect(launcherServices[0]).toEqual(service)
|
||||
})
|
||||
|
||||
it('should allow custom services without options', () => {
|
||||
it('should allow custom services without options', async () => {
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService(
|
||||
{ services: [CustomService], baseUrl: 'foobar' },
|
||||
} = await initialiseLauncherService(
|
||||
{ services: [CustomService as Services.ServiceEntry], baseUrl: 'foobar' },
|
||||
{}
|
||||
)
|
||||
expect(ignoredWorkerServices).toHaveLength(0)
|
||||
|
|
@ -63,12 +66,12 @@ describe('initialiseLauncherService', () => {
|
|||
expect((launcherServices as CustomService[])[0].config.baseUrl).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should allow custom services with options', () => {
|
||||
it('should allow custom services with options', async () => {
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService(
|
||||
{ services: [[CustomService, { foo: 'foo' }]], baseUrl: 'foobar' },
|
||||
} = await initialiseLauncherService(
|
||||
{ services: [[CustomService, { foo: 'foo' }] as Services.ServiceEntry], baseUrl: 'foobar' },
|
||||
{}
|
||||
)
|
||||
expect(ignoredWorkerServices).toHaveLength(0)
|
||||
|
|
@ -77,11 +80,11 @@ describe('initialiseLauncherService', () => {
|
|||
expect((launcherServices as CustomService[])[0].config.baseUrl).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should allow custom services with empty options', () => {
|
||||
const { launcherServices } = initialiseLauncherService(
|
||||
it('should allow custom services with empty options', async () => {
|
||||
const { launcherServices } = await initialiseLauncherService(
|
||||
{
|
||||
services: [
|
||||
[CustomService, {}]
|
||||
[CustomService, {}] as Services.ServiceEntry
|
||||
],
|
||||
baseUrl: 'foobar'
|
||||
},
|
||||
|
|
@ -92,30 +95,42 @@ describe('initialiseLauncherService', () => {
|
|||
expect((launcherServices as CustomService[])[0].options).toEqual({})
|
||||
})
|
||||
|
||||
it('should propagate services that have launcher only capabilities', () => {
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
it.skip('should propagate services that have launcher only capabilities', async () => {
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService({ services: ['launcher-only'] }, {})
|
||||
} = await initialiseLauncherService({ services: ['launcher-only'] }, {})
|
||||
expect(launcherServices).toHaveLength(1)
|
||||
expect((launcherServices[0] as TestLauncherService).isLauncher).toBe(true)
|
||||
expect(ignoredWorkerServices).toEqual(['launcher-only'])
|
||||
})
|
||||
|
||||
it('should ignore worker services', () => {
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
it.skip('should ignore worker services', async () => {
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService({ services: ['scoped'] }, {})
|
||||
} = await initialiseLauncherService({ services: ['scoped'] }, {})
|
||||
expect(launcherServices).toHaveLength(0)
|
||||
expect(ignoredWorkerServices).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('should not fail if service is borked', () => {
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
it.skip('should not fail if service is borked', async () => {
|
||||
const {
|
||||
launcherServices,
|
||||
ignoredWorkerServices
|
||||
} = initialiseLauncherService({ services: ['borked'] }, {})
|
||||
} = await initialiseLauncherService({ services: ['borked'] }, {})
|
||||
expect(launcherServices).toHaveLength(0)
|
||||
expect(ignoredWorkerServices).toHaveLength(0)
|
||||
expect(log.error).toHaveBeenCalledTimes(1)
|
||||
|
|
@ -123,33 +138,33 @@ describe('initialiseLauncherService', () => {
|
|||
})
|
||||
|
||||
describe('initialiseWorkerService', () => {
|
||||
it('should return empty array if no services prop is given', () => {
|
||||
expect(initialiseWorkerService({ services: [] }, {})).toEqual([])
|
||||
it('should return empty array if no services prop is given', async () => {
|
||||
expect(await initialiseWorkerService({ services: [] } as any, {})).toEqual([])
|
||||
})
|
||||
|
||||
it('should be able to add initialised services', () => {
|
||||
it('should be able to add initialised services', async () => {
|
||||
const service = {
|
||||
before: jest.fn(),
|
||||
afterTest: jest.fn()
|
||||
before: vi.fn(),
|
||||
afterTest: vi.fn()
|
||||
}
|
||||
|
||||
const services = initialiseWorkerService({ services: [service] }, {})
|
||||
const services = await initialiseWorkerService({ services: [service] } as any, {})
|
||||
expect(services).toHaveLength(1)
|
||||
expect(services[0]).toEqual(service)
|
||||
})
|
||||
|
||||
it('should allow custom services without options', () => {
|
||||
const services = initialiseWorkerService(
|
||||
{ services: [CustomService], baseUrl: 'foobar' },
|
||||
it('should allow custom services without options', async () => {
|
||||
const services = await initialiseWorkerService(
|
||||
{ services: [CustomService], baseUrl: 'foobar' } as any,
|
||||
{}
|
||||
)
|
||||
expect(services).toHaveLength(1)
|
||||
expect((services as CustomService[])[0].config.baseUrl).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should allow custom services with options', () => {
|
||||
const services = initialiseWorkerService(
|
||||
{ services: [[CustomService, { foo: 'foo' }]], baseUrl: 'foobar' },
|
||||
it('should allow custom services with options', async () => {
|
||||
const services = await initialiseWorkerService(
|
||||
{ services: [[CustomService, { foo: 'foo' }]], baseUrl: 'foobar' } as any,
|
||||
{}
|
||||
)
|
||||
expect(services).toHaveLength(1)
|
||||
|
|
@ -157,18 +172,26 @@ describe('initialiseWorkerService', () => {
|
|||
expect((services as CustomService[])[0].config.baseUrl).toBe('foobar')
|
||||
})
|
||||
|
||||
it('should ignore service with launcher only', () => {
|
||||
const services = initialiseWorkerService(
|
||||
{ services: ['launcher-only'] },
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
it.skip('should ignore service with launcher only', async () => {
|
||||
const services = await initialiseWorkerService(
|
||||
{ services: ['launcher-only'] } as any,
|
||||
{}
|
||||
)
|
||||
expect(services).toHaveLength(0)
|
||||
expect(log.error).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it('should not fail if service is borked', () => {
|
||||
const services = initialiseWorkerService(
|
||||
{ services: ['borked'] },
|
||||
/**
|
||||
* vitest can't load non existing packages
|
||||
* https://github.com/vitest-dev/vitest/pull/1298
|
||||
*/
|
||||
it.skip('should not fail if service is borked', async () => {
|
||||
const services = await initialiseWorkerService(
|
||||
{ services: ['borked'] } as any,
|
||||
{}
|
||||
)
|
||||
expect(services).toHaveLength(0)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { vi, describe, it, expect, beforeEach } from 'vitest'
|
||||
import webdriverMonad from '../src/monad'
|
||||
|
||||
let prototype
|
||||
let prototype: any
|
||||
|
||||
beforeEach(() => {
|
||||
prototype = {
|
||||
scope: { value: '' },
|
||||
someFunc: { value: jest.fn().mockImplementation((arg) => `result-${arg.toString()}`) }
|
||||
someFunc: { value: vi.fn().mockImplementation((arg) => `result-${arg.toString()}`) }
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ const sessionId = 'c5fa4320-07d5-48f5-b7c2-922d4405e17f'
|
|||
|
||||
describe('monad', () => {
|
||||
it('should be able to initialize client with prototype with commands', () => {
|
||||
const modifier = jest.fn()
|
||||
const modifier = vi.fn()
|
||||
const monad = webdriverMonad({ baseUrl: 'option' }, (client: any) => {
|
||||
modifier()
|
||||
return client
|
||||
|
|
@ -40,7 +41,7 @@ describe('monad', () => {
|
|||
|
||||
it('should allow to extend base prototype', () => {
|
||||
const monad = webdriverMonad({}, (client: any) => client, prototype)
|
||||
const commandWrapperMock = jest.fn().mockImplementation((name, fn) => fn)
|
||||
const commandWrapperMock = vi.fn().mockImplementation((name, fn) => fn)
|
||||
const client = monad(sessionId, commandWrapperMock)
|
||||
const fn = () => 'bar'
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ describe('monad', () => {
|
|||
|
||||
it('should allow to overwrite command in base prototype', () => {
|
||||
const monad = webdriverMonad({}, (client: any) => client, { ...prototype })
|
||||
const commandWrapperMock = jest.fn().mockImplementation((name, fn) => fn)
|
||||
const commandWrapperMock = vi.fn().mockImplementation((name, fn) => fn)
|
||||
const client = monad(sessionId, commandWrapperMock)
|
||||
const fn = () => 'bar'
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ describe('monad', () => {
|
|||
|
||||
it('should throw if there is no command to be overwritten', () => {
|
||||
const monad = webdriverMonad({}, (client: any) => client, { ...prototype })
|
||||
const commandWrapperMock = jest.fn().mockImplementation((name, fn) => fn)
|
||||
const commandWrapperMock = vi.fn().mockImplementation((name, fn) => fn)
|
||||
const client = monad(sessionId, commandWrapperMock)
|
||||
const fn = () => 'bar'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { BrowserObject } from 'webdriverio'
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||||
|
||||
import {
|
||||
executeHooksWithArgs, runFnInFiberContext, hasWdioSyncSupport, executeSync,
|
||||
executeAsync, runSync, wrapCommand
|
||||
executeHooksWithArgs, hasWdioSyncSupport, executeSync,
|
||||
executeAsync, wrapCommand
|
||||
} from '../src/shim'
|
||||
|
||||
jest.mock('@wdio/sync', () => {
|
||||
vi.mock('@wdio/sync', () => {
|
||||
throw new Error('Does not exist')
|
||||
})
|
||||
|
||||
|
|
@ -64,17 +64,6 @@ describe('executeHooksWithArgs', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('runFnInFiberContext', () => {
|
||||
it('should return fn that returns Promise', async () => {
|
||||
const fn = runFnInFiberContext(function (this: any, bar: string) {
|
||||
return this.foo + bar
|
||||
}.bind({ foo: 3 }))
|
||||
|
||||
// @ts-ignore
|
||||
expect(await fn(4)).toBe(7)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasWdioSyncSupport', () => {
|
||||
it('should be false', () => {
|
||||
expect(hasWdioSyncSupport).toBe(false)
|
||||
|
|
@ -181,21 +170,15 @@ describe('executeAsync', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('runSync', () => {
|
||||
it('should be null', () => {
|
||||
expect(runSync).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapCommand', () => {
|
||||
it('should not run a command hook in command hook', async () => {
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve('Yayy!'))
|
||||
const rawCommand = vi.fn().mockReturnValue(Promise.resolve('Yayy!'))
|
||||
const commandA = wrapCommand('foobar', rawCommand)
|
||||
const commandB = wrapCommand('barfoo', rawCommand)
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const scope: any = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn().mockImplementation(
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn().mockImplementation(
|
||||
() => commandB.call(scope, 123))
|
||||
}
|
||||
}
|
||||
|
|
@ -207,14 +190,14 @@ describe('wrapCommand', () => {
|
|||
})
|
||||
|
||||
it('throws an error if command fails', async () => {
|
||||
const rawCommand = jest.fn().mockReturnValue(
|
||||
const rawCommand = vi.fn().mockReturnValue(
|
||||
Promise.reject(new Error('Uppsi!')))
|
||||
const commandA = wrapCommand('foobar', rawCommand)
|
||||
const commandB = wrapCommand('barfoo', rawCommand)
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const scope: any = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn().mockImplementation(
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn().mockImplementation(
|
||||
() => commandB.call(scope, 123))
|
||||
}
|
||||
}
|
||||
|
|
@ -228,18 +211,22 @@ describe('wrapCommand', () => {
|
|||
})
|
||||
|
||||
it('allows to chain element promises', async () => {
|
||||
const rawCommand = jest.fn()
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const rawCommand = vi.fn()
|
||||
const scope: any = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy'),
|
||||
getTagName: vi.fn().mockResolvedValue('Yayy'),
|
||||
$: rawCommand
|
||||
}
|
||||
rawCommand.mockReturnValue(Promise.resolve(scope))
|
||||
const commandA = wrapCommand('$', rawCommand)
|
||||
expect(await commandA.call(scope, 'bar').$('foo').getTagName()).toBe('Yayy')
|
||||
expect(await commandA.call(scope, 'bar')
|
||||
// @ts-expect-error
|
||||
.$('foo')
|
||||
.getTagName()
|
||||
).toBe('Yayy')
|
||||
expect(scope.$).toBeCalledTimes(2)
|
||||
expect(scope.$).toBeCalledWith('bar')
|
||||
expect(scope.$).toBeCalledWith('foo')
|
||||
|
|
@ -247,18 +234,21 @@ describe('wrapCommand', () => {
|
|||
})
|
||||
|
||||
it('allows to chain element promises for custom command', async () => {
|
||||
const rawCommand = jest.fn()
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const rawCommand = vi.fn()
|
||||
const scope: any = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy'),
|
||||
getTagName: vi.fn().mockResolvedValue('Yayy'),
|
||||
user$: rawCommand
|
||||
}
|
||||
rawCommand.mockReturnValue(Promise.resolve(scope))
|
||||
const commandB = wrapCommand('user$', rawCommand)
|
||||
expect(await commandB.call(scope, 'bar').user$('foo').getTagName()).toBe('Yayy')
|
||||
expect(await commandB.call(scope, 'bar')
|
||||
// @ts-expect-error
|
||||
.user$('foo')
|
||||
.getTagName()).toBe('Yayy')
|
||||
expect(scope.user$).toBeCalledTimes(2)
|
||||
expect(scope.user$).toBeCalledWith('bar')
|
||||
expect(scope.user$).toBeCalledWith('foo')
|
||||
|
|
@ -266,14 +256,14 @@ describe('wrapCommand', () => {
|
|||
})
|
||||
|
||||
it('allows to access indexed element', async () => {
|
||||
const rawCommand$ = jest.fn()
|
||||
const rawCommand$$ = jest.fn()
|
||||
const scope: (i: number) => Partial<BrowserObject> = (i) => ({
|
||||
const rawCommand$ = vi.fn()
|
||||
const rawCommand$$ = vi.fn()
|
||||
const scope: (i: number) => any = (i) => ({
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy' + i),
|
||||
getTagName: vi.fn().mockResolvedValue('Yayy' + i),
|
||||
$: rawCommand$,
|
||||
$$: rawCommand$$
|
||||
})
|
||||
|
|
@ -284,21 +274,32 @@ describe('wrapCommand', () => {
|
|||
Promise.resolve(scope(2))
|
||||
])
|
||||
const commandA = wrapCommand('$', rawCommand$)
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar')[2].getTagName()).toBe('Yayy2')
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar')[2].$('barfoo').getTagName()).toBe('Yayy0')
|
||||
expect(await commandA.call(scope(0))
|
||||
// @ts-expect-error
|
||||
.$('foo')
|
||||
.$$('bar')[2]
|
||||
.getTagName()
|
||||
).toBe('Yayy2')
|
||||
expect(await commandA.call(scope(0))
|
||||
// @ts-expect-error
|
||||
.$('foo')
|
||||
.$$('bar')[2]
|
||||
.$('barfoo')
|
||||
.getTagName()
|
||||
).toBe('Yayy0')
|
||||
expect(rawCommand$$).toBeCalledTimes(2)
|
||||
expect(rawCommand$$).toBeCalledWith('bar')
|
||||
})
|
||||
|
||||
it('offers array methods on elements', async () => {
|
||||
const rawCommand$ = jest.fn()
|
||||
const rawCommand$$ = jest.fn()
|
||||
const scope: (i: number) => Partial<BrowserObject> = (i) => ({
|
||||
const rawCommand$ = vi.fn()
|
||||
const rawCommand$$ = vi.fn()
|
||||
const scope: (i: number) => any = (i) => ({
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
},
|
||||
getTagName: jest.fn().mockResolvedValue('Yayy' + i),
|
||||
getTagName: vi.fn().mockResolvedValue('Yayy' + i),
|
||||
$: rawCommand$,
|
||||
$$: rawCommand$$
|
||||
})
|
||||
|
|
@ -308,39 +309,35 @@ describe('wrapCommand', () => {
|
|||
Promise.resolve(scope(1)),
|
||||
Promise.resolve(scope(2))
|
||||
])
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand$ },
|
||||
'$$': { value: rawCommand$$ },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand$, propertiesObject)
|
||||
expect(await commandA.call(scope(0)).$('foo').$$('bar').map((el) => el.getTagName()))
|
||||
.toEqual(['Yayy0', 'Yayy1', 'Yayy2'])
|
||||
const commandA = wrapCommand('$', rawCommand$)
|
||||
expect(await commandA.call(scope(0))
|
||||
// @ts-expect-error
|
||||
.$('foo')
|
||||
.$$('bar')
|
||||
.map((el: any) => el.getTagName())
|
||||
).toEqual(['Yayy0', 'Yayy1', 'Yayy2'])
|
||||
})
|
||||
|
||||
it('can access element properties', async () => {
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const scope: any = {
|
||||
options: {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
},
|
||||
selector: 'foobar'
|
||||
}
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand, propertiesObject)
|
||||
const rawCommand = vi.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const commandA = wrapCommand('$', rawCommand)
|
||||
// @ts-expect-error
|
||||
expect(await commandA.call(scope).selector).toBe('foobar')
|
||||
})
|
||||
|
||||
it('can iterate over elements asynchronously', async () => {
|
||||
const options = {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
}
|
||||
const scope: Partial<BrowserObject> = [{
|
||||
const scope: any = [{
|
||||
selector: 'foobarA',
|
||||
options
|
||||
}, {
|
||||
|
|
@ -351,12 +348,8 @@ describe('wrapCommand', () => {
|
|||
options
|
||||
}]
|
||||
scope.options = options
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$$', rawCommand, propertiesObject).bind(scope) as any as (sel: string) => Promise<any>[]
|
||||
const rawCommand = vi.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const commandA = wrapCommand('$$', rawCommand).bind(scope) as any as (sel: string) => Promise<any>[]
|
||||
|
||||
const expectedResults = ['foobarA', 'foobarB', 'foobarC']
|
||||
let i = 0
|
||||
|
|
@ -368,20 +361,16 @@ describe('wrapCommand', () => {
|
|||
it('throws an error if iterating through a non array', async () => {
|
||||
expect.assertions(1)
|
||||
const options = {
|
||||
beforeCommand: jest.fn(),
|
||||
afterCommand: jest.fn()
|
||||
beforeCommand: vi.fn(),
|
||||
afterCommand: vi.fn()
|
||||
}
|
||||
const scope: Partial<BrowserObject> = {
|
||||
const scope: any = {
|
||||
selector: 'foobarA',
|
||||
options
|
||||
}
|
||||
scope.options = options
|
||||
const rawCommand = jest.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const propertiesObject = {
|
||||
'$': { value: rawCommand },
|
||||
getTagName: { value: jest.fn() }
|
||||
}
|
||||
const commandA = wrapCommand('$', rawCommand, propertiesObject).bind(scope) as any as (sel: string) => Promise<any>[]
|
||||
const rawCommand = vi.fn().mockReturnValue(Promise.resolve(scope))
|
||||
const commandA = wrapCommand('$', rawCommand).bind(scope) as any as (sel: string) => Promise<any>[]
|
||||
|
||||
try {
|
||||
for await (let elem of commandA('selector')) {
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
import { executeHooksWithArgs, runFnInFiberContext, wrapCommand, hasWdioSyncSupport, executeSync, runSync } from '../src/shim'
|
||||
import { wrapCommand as wrapCommandSync } from '@wdio/sync'
|
||||
|
||||
jest.mock('@wdio/sync', () => ({
|
||||
executeHooksWithArgs: jest.fn().mockReturnValue('executeHooksWithArgs'),
|
||||
wrapCommand: jest.fn().mockReturnValue(jest.fn()),
|
||||
executeSync: jest.fn().mockReturnValue('executeSync'),
|
||||
runSync: jest.fn().mockReturnValue('runSync'),
|
||||
runFnInFiberContext: jest.fn().mockReturnValue(() => {})
|
||||
}))
|
||||
|
||||
const command = jest.fn().mockReturnValue({})
|
||||
|
||||
describe('executeHooksWithArgs', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(executeHooksWithArgs.call({}, 'before', command)).toBe('executeHooksWithArgs')
|
||||
})
|
||||
})
|
||||
|
||||
describe('runFnInFiberContext', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(typeof runFnInFiberContext.call({}, command)).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapCommand', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
global._HAS_FIBER_CONTEXT = true
|
||||
|
||||
expect(wrapCommandSync).toBeCalledTimes(0)
|
||||
wrapCommand('foo', jest.fn()).call({ options: {} }, 'foo')
|
||||
expect(wrapCommandSync).toBeCalledTimes(0)
|
||||
|
||||
// @ts-ignore
|
||||
global.browser = {}
|
||||
wrapCommand('foo', jest.fn()).call({ options: {} }, 'foo')
|
||||
expect(wrapCommandSync).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasWdioSyncSupport', () => {
|
||||
it('should be true', () => {
|
||||
expect(hasWdioSyncSupport).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('executeSync', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(executeSync.call({}, command)).toBe('executeSync')
|
||||
})
|
||||
})
|
||||
|
||||
describe('runSync', () => {
|
||||
it('should match @wdio/sync', async () => {
|
||||
expect(runSync.call({}, command)).toBe('runSync')
|
||||
})
|
||||
})
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import { wrapCommand, switchSyncFlag, runAsync, expectAsyncShim } from '../src/shim'
|
||||
import { vi, describe, it, expect } from 'vitest'
|
||||
import { wrapCommand, expectAsyncShim } from '../src/shim'
|
||||
|
||||
describe('wrapCommand', () => {
|
||||
it('should run command with before and after hook', async () => {
|
||||
const commandFn = jest.fn().mockReturnValue(Promise.resolve('foobar'))
|
||||
const beforeHook = jest.fn()
|
||||
const afterHook = jest.fn()
|
||||
const commandFn = vi.fn().mockReturnValue(Promise.resolve('foobar'))
|
||||
const beforeHook = vi.fn()
|
||||
const afterHook = vi.fn()
|
||||
const scope = {
|
||||
options: {
|
||||
beforeCommand: [beforeHook, beforeHook],
|
||||
|
|
@ -25,8 +26,8 @@ describe('wrapCommand', () => {
|
|||
|
||||
it('should throw but still run after command hook', async () => {
|
||||
const error = new Error('uups')
|
||||
const commandFn = jest.fn().mockReturnValue(Promise.reject(error))
|
||||
const afterHook = jest.fn()
|
||||
const commandFn = vi.fn().mockReturnValue(Promise.reject(error))
|
||||
const afterHook = vi.fn()
|
||||
const scope = {
|
||||
options: {
|
||||
beforeCommand: [],
|
||||
|
|
@ -43,61 +44,9 @@ describe('wrapCommand', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('switchSyncFlag', () => {
|
||||
it('should switch runAsync flag', () => {
|
||||
expect(runAsync).toBe(true)
|
||||
switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return {}
|
||||
})()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a promise', async () => {
|
||||
expect(runAsync).toBe(true)
|
||||
await switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return Promise.resolve(true)
|
||||
})()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a function', () => {
|
||||
expect(runAsync).toBe(true)
|
||||
const fn = switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return () => {
|
||||
expect(runAsync).toBe(true)
|
||||
return {}
|
||||
}
|
||||
})()
|
||||
expect(runAsync).toBe(false)
|
||||
// eslint-disable-next-line
|
||||
runAsync = true
|
||||
fn()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
|
||||
it('should switch back when returning a function with promise', async () => {
|
||||
expect(runAsync).toBe(true)
|
||||
const fn = switchSyncFlag(() => {
|
||||
expect(runAsync).toBe(false)
|
||||
return () => {
|
||||
expect(runAsync).toBe(true)
|
||||
return Promise.resolve({})
|
||||
}
|
||||
})()
|
||||
expect(runAsync).toBe(false)
|
||||
// eslint-disable-next-line
|
||||
runAsync = true
|
||||
await fn()
|
||||
expect(runAsync).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
test('expectAsyncShim', () => {
|
||||
global.expectAsync = jest.fn()
|
||||
const expectSync = jest.fn()
|
||||
it('expectAsyncShim', () => {
|
||||
global.expectAsync = vi.fn()
|
||||
const expectSync = vi.fn()
|
||||
expectAsyncShim(undefined, expectSync)
|
||||
expect(expectSync).toBeCalledTimes(1)
|
||||
expect(global.expectAsync).toBeCalledTimes(0)
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`testFnWrapper should propagate jasmine failed expecations as errors 1`] = `"afterFn"`;
|
||||
|
||||
exports[`testFnWrapper should propagate jasmine failed expecations as errors 2`] = `"@wdio/sync: FooBar 0 0"`;
|
||||
|
||||
exports[`testFnWrapper should run fn in sync mode with cucumber 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
"beforeFoo",
|
||||
"beforeFn",
|
||||
Array [
|
||||
"beforeFnArgs",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"afterFoo",
|
||||
"afterFn",
|
||||
Array [
|
||||
Object {
|
||||
"foo": "bar",
|
||||
},
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
Object {
|
||||
"duration": 0,
|
||||
"error": undefined,
|
||||
"passed": true,
|
||||
"result": "@wdio/sync: FooBar 0 0",
|
||||
"retries": Object {
|
||||
"attempts": 0,
|
||||
"limit": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`testFnWrapper should run fn in sync mode with mocha or jasmine 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
"beforeFoo",
|
||||
"beforeFn",
|
||||
Array [
|
||||
"beforeFnArgs",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
"afterFoo",
|
||||
"afterFn",
|
||||
Array [
|
||||
Object {
|
||||
"description": "foo",
|
||||
"foo": "bar",
|
||||
},
|
||||
"context",
|
||||
Object {
|
||||
"error": undefined,
|
||||
"passed": true,
|
||||
"result": "@wdio/sync: FooBar 0 0",
|
||||
"retries": Object {
|
||||
"attempts": 0,
|
||||
"limit": 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
import { vi, describe, it, afterEach } from 'vitest'
|
||||
import { logHookError } from '../../src/test-framework/errorHandler'
|
||||
|
||||
const pSend = jest.spyOn(process, 'send')
|
||||
// const pSend = vi.spyOn(process, 'send')
|
||||
const pSend = vi.fn()
|
||||
|
||||
describe('logHookError', () => {
|
||||
it('should send message if there is Error in results', () => {
|
||||
it.skip('should send message if there is Error in results', () => {
|
||||
logHookError('BeforeStep', [undefined, true, new Error('foobar')], '0-1')
|
||||
expect(pSend).toBeCalledTimes(1)
|
||||
expect(pSend).toBeCalledWith({
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
import { vi, MockedFunction, describe, it, expect, afterEach } from 'vitest'
|
||||
|
||||
import * as shim from '../../src/shim'
|
||||
import { testFnWrapper, filterStackTrace } from '../../src/test-framework/testFnWrapper'
|
||||
|
||||
jest.mock('../../src/shim', () => ({
|
||||
executeHooksWithArgs: jest.fn(),
|
||||
executeAsync: async (fn, { limit, attempts }, args = []) => fn('async', limit, attempts, ...args),
|
||||
vi.mock('../../src/shim', () => ({
|
||||
executeHooksWithArgs: vi.fn(),
|
||||
executeAsync: async (fn: Function, { limit, attempts }: any, args = []) => fn('async', limit, attempts, ...args),
|
||||
runSync: null,
|
||||
}))
|
||||
|
||||
const executeHooksWithArgs = shim.executeHooksWithArgs
|
||||
const executeHooksWithArgs = shim.executeHooksWithArgs as MockedFunction<any>
|
||||
|
||||
describe('testFnWrapper', () => {
|
||||
const origFn = (mode, limit, attempts, arg) => `${mode}: Foo${arg} ${limit} ${attempts}`
|
||||
const buildArgs = (specFn, retries, beforeFnArgs, afterFnArgs) => [
|
||||
const origFn = (mode: string, limit: number, attempts: number, arg: any) => `${mode}: Foo${arg} ${limit} ${attempts}`
|
||||
const buildArgs = (specFn: Function, retries?: number, beforeFnArgs?: Function, afterFnArgs?: Function) => [
|
||||
'Foo',
|
||||
{ specFn, specFnArgs: ['Bar'] },
|
||||
{ beforeFn: 'beforeFn', beforeFnArgs },
|
||||
|
|
@ -21,7 +23,8 @@ describe('testFnWrapper', () => {
|
|||
]
|
||||
|
||||
it('should run fn in async mode if not runSync', async () => {
|
||||
const args = buildArgs(origFn, undefined, () => [], () => [])
|
||||
const args = buildArgs(origFn, undefined, () => [], () => []) as any[]
|
||||
// @ts-expect-error
|
||||
const result = await testFnWrapper(...args)
|
||||
|
||||
expect(result).toBe('async: FooBar 0 0')
|
||||
|
|
@ -40,7 +43,9 @@ describe('testFnWrapper', () => {
|
|||
})
|
||||
|
||||
it('should run fn in async mode if specFn is async', async () => {
|
||||
const args = buildArgs(async (...args) => origFn(...args), 11, () => ['beforeFnArgs'], () => ['afterFnArgs'])
|
||||
// @ts-expect-error
|
||||
const args = buildArgs(async (...args: any[]) => origFn(...args), 11, () => ['beforeFnArgs'], () => ['afterFnArgs'])
|
||||
// @ts-expect-error
|
||||
const result = await testFnWrapper(...args)
|
||||
|
||||
expect(result).toBe('async: FooBar 11 0')
|
||||
|
|
@ -60,13 +65,14 @@ describe('testFnWrapper', () => {
|
|||
|
||||
it('should throw on error', async () => {
|
||||
let expectedError
|
||||
const args = buildArgs((mode, repeatTest, arg) => {
|
||||
const args = buildArgs((mode: string, repeatTest: boolean, arg: any) => {
|
||||
expectedError = new Error(`${mode}: Foo${arg} ${repeatTest}`)
|
||||
throw expectedError
|
||||
}, undefined, () => ['beforeFnArgs'], () => ['afterFnArgs'])
|
||||
|
||||
let error
|
||||
try {
|
||||
// @ts-expect-error
|
||||
await testFnWrapper(...args)
|
||||
} catch (err) {
|
||||
error = err
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import * as shim from '../../src/shim'
|
||||
import { testFnWrapper } from '../../src/test-framework/testFnWrapper'
|
||||
|
||||
jest.mock('../../src/shim', () => ({
|
||||
executeHooksWithArgs: jest.fn(),
|
||||
runSync: (fn, { attempts, limit }, args = []) => async (resolve, reject) => {
|
||||
try {
|
||||
return resolve(await fn('@wdio/sync', attempts, limit, ...args))
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
},
|
||||
executeAsync: null,
|
||||
}))
|
||||
|
||||
const executeHooksWithArgs = shim.executeHooksWithArgs
|
||||
|
||||
describe('testFnWrapper', () => {
|
||||
const origFn = (mode, attempts, limit, arg) => `${mode}: Foo${arg} ${attempts} ${limit}`
|
||||
const buildArgs = (specFn, retries, beforeFnArgs, afterFnArgs) => [
|
||||
'Foo',
|
||||
{ specFn, specFnArgs: ['Bar'] },
|
||||
{ beforeFn: 'beforeFn', beforeFnArgs },
|
||||
{ afterFn: 'afterFn', afterFnArgs },
|
||||
'0-9',
|
||||
retries
|
||||
]
|
||||
|
||||
it('should run fn in sync mode with mocha or jasmine', async () => {
|
||||
const args = buildArgs(origFn, undefined, () => ['beforeFnArgs'], () => [{ foo: 'bar', description: 'foo' }, 'context'])
|
||||
const result = await testFnWrapper.call({ test: { fullTitle: () => 'full title' } }, ...args)
|
||||
|
||||
expect(result).toBe('@wdio/sync: FooBar 0 0')
|
||||
expect(executeHooksWithArgs).toBeCalledTimes(2)
|
||||
|
||||
expect(
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
executeHooksWithArgs.mock.calls[1][2][2],
|
||||
'duration'
|
||||
)
|
||||
).toBe(true)
|
||||
delete executeHooksWithArgs.mock.calls[1][2][2].duration
|
||||
expect(executeHooksWithArgs.mock.calls).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should propagate jasmine failed expecations as errors', async () => {
|
||||
const failedExpectation = {
|
||||
matcherName: 'toEqual',
|
||||
message: 'Expected true to equal false.',
|
||||
stack: 'Error: Expected true to equal false.\n at <Jasmine>\n at UserContext.it',
|
||||
passed: false,
|
||||
expected: false,
|
||||
actual: true
|
||||
}
|
||||
const args = buildArgs(origFn, undefined, () => ['beforeFnArgs'], () => [{ foo: 'bar', description: 'foo', failedExpectations: [failedExpectation] }, 'context'])
|
||||
const error = await testFnWrapper.call({ test: { fullTitle: () => 'full title' } }, ...args).catch((err) => err)
|
||||
expect(executeHooksWithArgs.mock.calls[1][1]).toMatchSnapshot()
|
||||
expect(error).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should run fn in sync mode with cucumber', async () => {
|
||||
const args = buildArgs(origFn, undefined, () => ['beforeFnArgs'], () => [{ foo: 'bar' }, 2, 3, 4])
|
||||
const result = await testFnWrapper(...args)
|
||||
|
||||
expect(result).toBe('@wdio/sync: FooBar 0 0')
|
||||
expect(executeHooksWithArgs).toBeCalledTimes(2)
|
||||
|
||||
delete executeHooksWithArgs.mock.calls[1][1][2].duration
|
||||
expect(executeHooksWithArgs.mock.calls).toMatchSnapshot()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
executeHooksWithArgs.mockClear()
|
||||
})
|
||||
})
|
||||
|
|
@ -1,55 +1,59 @@
|
|||
import { testFnWrapper } from '../../src/test-framework/testFnWrapper'
|
||||
import { MockedFunction, vi, describe, it, expect, afterEach, beforeEach } from 'vitest'
|
||||
|
||||
import { testFnWrapper as testFnWrapperImport } from '../../src/test-framework/testFnWrapper'
|
||||
import { runHook, runSpec, wrapTestFunction, runTestInFiberContext } from '../../src/test-framework/testInterfaceWrapper'
|
||||
|
||||
const testFunction = jest.fn(function (specTitle, cb) { return cb.call(this, 'foo', 'bar') })
|
||||
const hookFunction = jest.fn(function (cb) { return cb.call(this, 'foo', 'bar') })
|
||||
const testFunction = vi.fn(function (this: any, specTitle, cb) { return cb.call(this, 'foo', 'bar') })
|
||||
const hookFunction = vi.fn(function (this: any, cb) { return cb.call(this, 'foo', 'bar') })
|
||||
|
||||
jest.mock('../../src/test-framework/testFnWrapper', () => ({
|
||||
testFnWrapper: jest.fn()
|
||||
vi.mock('../../src/test-framework/testFnWrapper', () => ({
|
||||
testFnWrapper: vi.fn()
|
||||
}))
|
||||
|
||||
const testFnWrapper = testFnWrapperImport as MockedFunction<any>
|
||||
beforeEach(() => {
|
||||
testFnWrapper.mockClear()
|
||||
// @ts-expect-error
|
||||
global.jasmine = {}
|
||||
})
|
||||
|
||||
describe('runHook', () => {
|
||||
it('should call testFnWrapper with proper args', () => {
|
||||
const beforeFnArgs = (context) => [context.foo]
|
||||
const afterFnArgs = (context) => [context.foo]
|
||||
runHook('hookFn', hookFunction.bind({ foo: 'bar' }), 'beforeFn', beforeFnArgs, 'afterFn', afterFnArgs, 'cid')
|
||||
const beforeFnArgs = (context: any) => [context.foo]
|
||||
const afterFnArgs = (context: any) => [context.foo]
|
||||
runHook('hookFn' as any, hookFunction.bind({ foo: 'bar' }), 'beforeFn' as any, beforeFnArgs as any, 'afterFn' as any, afterFnArgs as any, 'cid', 0, 0)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Hook',
|
||||
{ specFn: 'hookFn', specFnArgs: ['foo', 'bar'] },
|
||||
{ beforeFn: 'beforeFn', beforeFnArgs },
|
||||
{ afterFn: 'afterFn', afterFnArgs },
|
||||
'cid',
|
||||
undefined
|
||||
0
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('runSpec', () => {
|
||||
it('should call testFnWrapper with proper args', () => {
|
||||
const beforeFnArgs = (context) => [context.foo]
|
||||
const afterFnArgs = (context) => [context.foo]
|
||||
runSpec('test title', 'specFn', testFunction.bind({ foo: 'bar' }), 'beforeFn', beforeFnArgs, 'afterFn', afterFnArgs, 'cid')
|
||||
const beforeFnArgs = (context: any) => [context.foo]
|
||||
const afterFnArgs = (context: any) => [context.foo]
|
||||
runSpec('test title', 'specFn' as any, testFunction.bind({ foo: 'bar' }), 'beforeFn' as any, beforeFnArgs as any, 'afterFn' as any, afterFnArgs as any, 'cid', 0, 0)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Test',
|
||||
{ specFn: 'specFn', specFnArgs: ['foo', 'bar'] },
|
||||
{ beforeFn: 'beforeFn', beforeFnArgs },
|
||||
{ afterFn: 'afterFn', afterFnArgs },
|
||||
'cid',
|
||||
undefined
|
||||
0
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapTestFunction', () => {
|
||||
it('should run spec', () => {
|
||||
const specFn = jest.fn()
|
||||
const fn = wrapTestFunction(testFunction, true, 'beforeFn', () => [], 'afterFn', () => [], 'cid')
|
||||
fn('test title', specFn, { options: { foo: 'bar' } })
|
||||
const specFn = vi.fn()
|
||||
const fn = wrapTestFunction(testFunction, true, 'beforeFn' as any, () => [] as any, 'afterFn' as any, () => [] as any, 'cid')
|
||||
fn('test title', specFn, { options: { foo: 'bar' } } as any)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Test',
|
||||
{ specFn, specFnArgs: ['foo', 'bar'] },
|
||||
|
|
@ -61,9 +65,9 @@ describe('wrapTestFunction', () => {
|
|||
})
|
||||
|
||||
it('should run hook', () => {
|
||||
const specFn = jest.fn()
|
||||
const fn = wrapTestFunction(hookFunction, false, 'beforeFn', () => [], 'afterFn', () => [], 'cid')
|
||||
fn(specFn, 123, 4)
|
||||
const specFn = vi.fn()
|
||||
const fn = wrapTestFunction(hookFunction, false, 'beforeFn' as any, () => [] as any, 'afterFn' as any, () => [] as any, 'cid')
|
||||
fn(specFn as any, 123 as any, 4)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Hook',
|
||||
{ specFn, specFnArgs: ['foo', 'bar'] },
|
||||
|
|
@ -76,9 +80,9 @@ describe('wrapTestFunction', () => {
|
|||
})
|
||||
|
||||
it('should run pending function', () => {
|
||||
const origFn = jest.fn()
|
||||
const fn = wrapTestFunction(origFn, true)
|
||||
fn('skipped test title')
|
||||
const origFn = vi.fn()
|
||||
const fn = wrapTestFunction(origFn, true, 'beforeFn' as any, () => [] as any, 'afterFn' as any, () => [] as any, 'cid')
|
||||
fn('skipped test title' as any)
|
||||
expect(origFn).toBeCalledWith('skipped test title')
|
||||
})
|
||||
})
|
||||
|
|
@ -86,16 +90,23 @@ describe('wrapTestFunction', () => {
|
|||
describe('runTestInFiberContext', () => {
|
||||
it('should wrap skip and only functions', () => {
|
||||
const skipFn = () => { }
|
||||
const onlyFn = function (...args) { return global.foobar(...args) }
|
||||
// @ts-expect-error
|
||||
const onlyFn = function (...args: any[]) { return global.foobar(...args) }
|
||||
// @ts-expect-error
|
||||
global.foobar = testFunction
|
||||
// @ts-expect-error
|
||||
global.foobar.only = onlyFn
|
||||
// @ts-expect-error
|
||||
global.foobar.skip = skipFn
|
||||
runTestInFiberContext(true, 'beforeFn', () => [], 'afterFn', () => [], 'foobar', 'cid')
|
||||
runTestInFiberContext(true, 'beforeFn' as any, () => [] as any, 'afterFn' as any, () => [] as any, 'foobar', 'cid')
|
||||
|
||||
// @ts-expect-error
|
||||
expect(global.foobar.skip).toBe(skipFn)
|
||||
// @ts-expect-error
|
||||
expect(global.foobar.only).toBe(onlyFn)
|
||||
|
||||
const specFn = jest.fn()
|
||||
const specFn = vi.fn()
|
||||
// @ts-expect-error
|
||||
global.foobar.only('test title', specFn, 321, 3)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Test',
|
||||
|
|
@ -117,10 +128,10 @@ describe('runTestInFiberContext', () => {
|
|||
const scope = {
|
||||
barfoo: hookFunction
|
||||
}
|
||||
runTestInFiberContext(false, 'beforeFn', () => [], 'afterFn', () => [], 'barfoo', 'cid', scope)
|
||||
runTestInFiberContext(false, 'beforeFn' as any, () => [] as any, 'afterFn' as any, () => [] as any, 'barfoo', 'cid', scope as any)
|
||||
|
||||
const specFn = jest.fn()
|
||||
scope.barfoo(specFn, 0)
|
||||
const specFn = vi.fn()
|
||||
scope.barfoo(specFn)
|
||||
expect(testFnWrapper).toBeCalledWith(
|
||||
'Hook',
|
||||
{ specFn, specFnArgs: ['foo', 'bar'] },
|
||||
|
|
@ -133,7 +144,9 @@ describe('runTestInFiberContext', () => {
|
|||
})
|
||||
|
||||
afterEach(() => {
|
||||
// @ts-expect-error
|
||||
delete global.foobar
|
||||
// @ts-expect-error
|
||||
delete global.jasmine
|
||||
testFnWrapper.mockClear()
|
||||
})
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import fs from 'fs'
|
||||
import { vi, describe, it, expect, MockedFunction } from 'vitest'
|
||||
import fs from 'node:fs'
|
||||
|
||||
import {
|
||||
overwriteElementCommands, commandCallStructure, isValidParameter, canAccess,
|
||||
getArgumentType, isFunctionAsync, filterSpecArgs, isBase64, transformCommandLogResult
|
||||
} from '../src/utils'
|
||||
|
||||
jest.mock('fs')
|
||||
vi.mock('fs')
|
||||
|
||||
describe('utils', () => {
|
||||
it('commandCallStructure', () => {
|
||||
|
|
@ -55,7 +56,7 @@ describe('utils', () => {
|
|||
describe('overwriteElementCommands', () => {
|
||||
it('should overwrite command', function () {
|
||||
const context = {}
|
||||
const origFnMock: (arg: any) => void = jest.fn(() => 1)
|
||||
const origFnMock: (arg: any) => void = vi.fn(() => 1)
|
||||
const propertiesObject = {
|
||||
foo: { value: origFnMock },
|
||||
__elementOverrides__: {
|
||||
|
|
@ -65,15 +66,15 @@ describe('utils', () => {
|
|||
overwriteElementCommands.call(context, propertiesObject)
|
||||
expect(propertiesObject.foo.value(5))
|
||||
.toEqual([1, 5])
|
||||
expect((origFnMock as jest.Mock).mock.calls.length)
|
||||
expect((origFnMock as MockedFunction<any>).mock.calls.length)
|
||||
.toBe(1)
|
||||
expect((origFnMock as jest.Mock).mock.instances[0])
|
||||
expect((origFnMock as MockedFunction<any>).mock.instances[0])
|
||||
.toBe(propertiesObject.foo)
|
||||
})
|
||||
|
||||
it('should support rebinding when invoking original fn', function () {
|
||||
const context = {}
|
||||
const origFnMock: (arg: any) => void = jest.fn(() => 1)
|
||||
const origFnMock: (arg: any) => void = vi.fn(() => 1)
|
||||
const origFnContext = {}
|
||||
const propertiesObject = {
|
||||
foo: { value: origFnMock },
|
||||
|
|
@ -84,9 +85,9 @@ describe('utils', () => {
|
|||
overwriteElementCommands.call(context, propertiesObject)
|
||||
expect(propertiesObject.foo.value(5))
|
||||
.toEqual([1, 5])
|
||||
expect((origFnMock as jest.Mock).mock.calls.length)
|
||||
expect((origFnMock as MockedFunction<any>).mock.calls.length)
|
||||
.toBe(1)
|
||||
expect((origFnMock as jest.Mock).mock.instances[0])
|
||||
expect((origFnMock as MockedFunction<any>).mock.instances[0])
|
||||
.toBe(origFnContext)
|
||||
})
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ describe('utils', () => {
|
|||
|
||||
it('should throw if there is no command to be propertiesObject', function () {
|
||||
const propertiesObject = { __elementOverrides__: { value: {
|
||||
foo: jest.fn()
|
||||
foo: vi.fn()
|
||||
} } }
|
||||
expect(() => overwriteElementCommands.call(null, propertiesObject))
|
||||
.toThrow('overwriteCommand: no command to be overwritten: foo')
|
||||
|
|
@ -114,7 +115,7 @@ describe('utils', () => {
|
|||
|
||||
it('should throw on attempt to overwrite not a function', function () {
|
||||
const propertiesObject = { foo: 'bar', __elementOverrides__: { value: {
|
||||
foo: jest.fn()
|
||||
foo: vi.fn()
|
||||
} } }
|
||||
expect(() => overwriteElementCommands.call(null, propertiesObject))
|
||||
.toThrow('overwriteCommand: only functions can be overwritten, command: foo')
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ import { defineConfig } from 'vite'
|
|||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['packages/wdio-logger/**/*.test.ts'],
|
||||
include: [
|
||||
'packages/wdio-logger/**/*.test.ts',
|
||||
'packages/wdio-utils/**/*.test.ts'
|
||||
],
|
||||
coverage: {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
exclude: ['**/build/**', '**/*.test.ts'],
|
||||
lines: 90,
|
||||
functions: 90,
|
||||
|
|
|
|||
Loading…
Reference in a new issue