webdriverio/packages/wdio-cli/tests/launcher.test.ts
Christian Bromann a2ae35332f
chore: upgrade to Eslint v9 (#13867)
* chore: upgrade to Eslint v9

* fix remaining issues

* more type fixes

* resolve remaining issues

* use wdio eslint package

* PR feedback
2024-12-18 11:55:43 -08:00

789 lines
32 KiB
TypeScript

import path from 'node:path'
import { vi, describe, it, expect, afterEach, beforeEach } from 'vitest'
import logger from '@wdio/logger'
import { sleep, enableFileLogging } from '@wdio/utils'
import Launcher from '../src/launcher.js'
const caps: WebdriverIO.Capabilities = {
'wdio:maxInstances': 1,
browserName: 'chrome'
}
vi.mock('node:fs/promises', () => ({
default: {
access: vi.fn().mockRejectedValue(new Error('ENOENT')),
mkdir: vi.fn()
}
}))
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/config/node', () => import(path.join(process.cwd(), '__mocks__', '@wdio/config/node')))
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()
logHookError = vi.fn()
finalise = vi.fn()
}
}))
const shard = { current: 1, total: 1 }
describe('launcher', () => {
const emitSpy = vi.spyOn(process, 'emit')
let launcher: Launcher
beforeEach(() => {
global.console.log = vi.fn()
emitSpy.mockClear()
launcher = new Launcher('./')
launcher.interface = {
onMessage: vi.fn(),
finalise: vi.fn(),
emit: vi.fn(),
sigintTrigger: vi.fn()
} as any
})
it('should have default for the argv parameter', () => {
expect(launcher['_args']).toEqual({})
})
describe('capabilities', () => {
it('should NOT fail when capabilities are passed', async () => {
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
const exitCode = await launcher['_runMode']({ specs: ['./'] } as any, [caps])
expect(launcher['_runSpecs']).toBeCalled()
expect(exitCode).toEqual(0)
expect(logger('').error).not.toBeCalled()
})
it('should fail if no specs were found', async () => {
launcher['_runSpecs'] = vi.fn()
launcher.configParser.getSpecs = vi.fn().mockReturnValue([])
const exitCode = await launcher['_runMode']({ specs: ['./'], shard } as any, [caps])
expect(launcher['_runSpecs']).toBeCalledTimes(0)
expect(exitCode).toBe(1)
})
it('should fail when no capabilities are passed', async () => {
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
// @ts-ignore test invalid parameter
const exitCode = await launcher['_runMode']({ specs: ['./'] as any, shard })
expect(exitCode).toEqual(1)
expect(logger('').error).toBeCalledWith('Missing capabilities, exiting with failure')
})
it('should fail when no capabilities are set', async () => {
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
const exitCode = await launcher['_runMode']({ specs: ['./'], shard } as any)
expect(exitCode).toEqual(1)
expect(logger('').error).toBeCalledWith('Missing capabilities, exiting with failure')
})
it('should fail when no capabilities are set (empty capabilities array)', async () => {
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
const exitCode = await launcher['_runMode']({ specs: ['./'], shard } as any, [])
expect(exitCode).toEqual(1)
expect(logger('').error).toBeCalledWith('Missing capabilities, exiting with failure')
})
it('should fail when no capabilities are set (empty capabilities array)', async () => {
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
const exitCode = await launcher['_runMode']({ specs: ['./'], shard } as any, {})
expect(exitCode).toEqual(1)
expect(logger('').error).toBeCalledWith('Missing capabilities, exiting with failure')
})
it('should start instance in multiremote', () => {
launcher['_runSpecs'] = vi.fn()
launcher.isMultiremote = true
launcher['_runMode'](
{ specs: ['./'], specFileRetries: 2 } as any,
{ foo: { capabilities: { browserName: 'chrome' } } }
)
expect(launcher['_schedule']).toHaveLength(1)
expect(launcher['_schedule'][0].specs[0].retries).toBe(2)
expect(typeof launcher['_resolve']).toBe('function')
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should start instances with parallel multiremote', () => {
launcher['_runSpecs'] = vi.fn()
launcher.isMultiremote = true
launcher.isParallelMultiremote = true
launcher['_runMode'](
{ specs: ['./'], specFileRetries: 2 } as any,
[
{ foo: { capabilities: { browserName: 'chrome' } } },
{ foo: { capabilities: { browserName: 'chrome' } } }
]
)
expect(launcher['_schedule']).toHaveLength(2)
expect(launcher['_schedule'][0].specs[0].retries).toBe(2)
expect(launcher['_schedule'][1].specs[0].retries).toBe(2)
expect(typeof launcher['_resolve']).toBe('function')
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should start instance with grouped specs', () => {
launcher['_runSpecs'] = vi.fn()
launcher.isMultiremote = false
launcher['_runMode'](
{ specs: [['/a.js', '/b.js']], specFileRetries: 2 } as any,
[caps]
)
expect(launcher['_schedule']).toHaveLength(1)
expect(launcher['_schedule'][0].specs[0].retries).toBe(2)
expect(typeof launcher['_resolve']).toBe('function')
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should start instance in multiremote with grouped specs', () => {
launcher['_runSpecs'] = vi.fn()
launcher.isMultiremote = true
launcher['_runMode'](
{ specs: [['/a.js', '/b.js']], specFileRetries: 2 } as any,
{ foo: { capabilities: { browserName: 'chrome' } } }
)
expect(launcher['_schedule']).toHaveLength(1)
expect(launcher['_schedule'][0].specs[0].retries).toBe(2)
expect(typeof launcher['_resolve']).toBe('function')
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should ignore specFileRetries in watch mode', () => {
launcher['_runSpecs'] = vi.fn()
launcher['_isWatchMode'] = true
launcher['_runMode']({ specs: ['./'], specFileRetries: 2 } as any, [caps, caps])
expect(launcher['_schedule']).toHaveLength(2)
expect(launcher['_schedule'][0].specs[0].retries).toBe(0)
expect(launcher['_schedule'][1].specs[0].retries).toBe(0)
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should apply maxInstancesPerCapability if maxInstances is not passed', () => {
launcher['_runSpecs'] = vi.fn()
launcher['_runMode'](
{ specs: ['./'], specFileRetries: 3, maxInstancesPerCapability: 4 } as any,
[{ browserName: 'chrome' }]
)
expect(launcher['_schedule']).toHaveLength(1)
expect(launcher['_schedule'][0].specs[0].retries).toBe(3)
expect(launcher['_schedule'][0].availableInstances).toBe(4)
expect(launcher['_runSpecs']).toBeCalledTimes(1)
})
it('should mixin wdio:maxInstances per capability + precedence over maxInstancesPerCapability', () => {
const wdioPrefixMaxInstances = [
{
browserName: 'chrome',
'wdio:maxInstances': 11
},
{
browserName: 'safari',
'wdio:maxInstances': 22
}
]
launcher['_runSpecs'] = vi.fn()
launcher['_runMode'](
{ specs: ['./'], maxInstancesPerCapability: 3 } as any,
wdioPrefixMaxInstances
)
expect(launcher['_schedule']).toHaveLength(2)
expect(launcher['_schedule'][0].availableInstances).toBe(11)
expect(launcher['_schedule'][1].availableInstances).toBe(22)
})
})
describe('hasTriggeredExitRoutine', () => {
it('should return false if there are specs left of running when hasTriggeredExitRoutine is false', () => {
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'] = vi.fn().mockReturnValue(1)
launcher['_hasTriggeredExitRoutine'] = true
const returnValue = launcher['_runSpecs']()
expect(returnValue).toEqual(true)
})
})
describe('endHandler', () => {
const config = { onWorkerEnd: vi.fn() }
beforeEach(() => {
launcher['_launcher'] = []
launcher.configParser.getConfig = vi.fn().mockReturnValue(config)
})
it('should emit and resolve failed status', async () => {
launcher['_getNumberOfRunningInstances'] = vi.fn().mockReturnValue(1)
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }]
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)
expect(config.onWorkerEnd).toBeCalledWith('0-1', 1, [], 0)
})
it('should emit and resolve passed status', async () => {
launcher['_getNumberOfRunningInstances'] = vi.fn().mockReturnValue(1)
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }]
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'] = vi.fn().mockReturnValue(1)
launcher['_runSpecs'] = vi.fn().mockReturnValue(0)
launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }]
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.interface?.finalise).toBeCalledTimes(0)
expect(launcher['_resolve']).toBeCalledTimes(0)
})
it('should do nothing if watch mode is still running', async () => {
launcher['_getNumberOfRunningInstances'] = vi.fn().mockReturnValue(1)
launcher['_isWatchMode'] = true
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }]
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.interface?.finalise).toBeCalledTimes(1)
expect(launcher['_resolve']).toBeCalledTimes(0)
})
it('should resolve and not emit on watch mode stop', async () => {
launcher['_getNumberOfRunningInstances'] = vi.fn().mockReturnValue(1)
launcher['_isWatchMode'] = true
launcher['_hasTriggeredExitRoutine'] = true
launcher['_runSpecs'] = vi.fn().mockReturnValue(1)
launcher['_schedule'] = [{ cid: 1 } as any, { cid: 2 }]
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)
})
it('should reschedule when runner failed and retries remain', async () => {
launcher['_schedule'] = [{ cid: 0, specs: [] }] 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 }] }])
})
it('should requeue retried specfiles at beginning of queue', async () => {
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'] }] }])
})
it('should requeue retried specfiles at end of queue', async () => {
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: [{ files: ['b.js'] }, { rid: '0-5', files: ['a.js'], retries: 0 }] }])
})
})
describe('exitHandler', () => {
it('should do nothing if no callback is given', () => {
launcher['_hasTriggeredExitRoutine'] = false
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)
})
it('should do nothing if shutdown was called before', () => {
launcher['_hasTriggeredExitRoutine'] = true
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)
})
it('should shutdown', () => {
launcher['_hasTriggeredExitRoutine'] = false
launcher.runner = { shutdown: vi.fn().mockReturnValue(Promise.resolve()) } as any
launcher['_exitHandler'](vi.fn())
expect(launcher['_hasTriggeredExitRoutine']).toBe(true)
expect(launcher.interface!.sigintTrigger).toBeCalledTimes(1)
expect(launcher.runner?.shutdown).toBeCalledTimes(1)
})
})
describe('getRunnerId', () => {
it('should return increasing cid numbers', () => {
expect(launcher['_getRunnerId'](0)).toBe('0-0')
expect(launcher['_getRunnerId'](0)).toBe('0-1')
expect(launcher['_getRunnerId'](0)).toBe('0-2')
expect(launcher['_getRunnerId'](2)).toBe('2-0')
expect(launcher['_getRunnerId'](0)).toBe('0-3')
expect(launcher['_getRunnerId'](2)).toBe('2-1')
})
})
describe('getNumberOfSpecsLeft', () => {
it('should return number of spec', () => {
launcher['_schedule'] = [{ specs: [1, 2, [3, 4, 5]] }, { specs: [1, 2] }] as any
expect(launcher['_getNumberOfSpecsLeft']()).toBe(5)
})
})
describe('getNumberOfRunningInstances', () => {
it('should return number of spec', () => {
launcher['_schedule'] = [{ runningInstances: 3 }, { runningInstances: 2 }] as any
expect(launcher['_getNumberOfRunningInstances']()).toBe(5)
})
})
describe('formatSpecs', () => {
it('should return correctly formatted specs', () => {
// Define a capabilities that is sent to formatSpecs
// - only used in function call
const capabilities = { specs: ['/a.js', ['/b.js', '/c.js', '/d.js'], '/e.js'] }
const specFileRetries = 17
// Define the golden result
const expected = [
{ 'files': ['/a.js'], 'retries': 17 },
{ 'files': ['/b.js', '/c.js', '/d.js'], 'retries': 17 },
{ 'files': ['/e.js'], 'retries': 17 },
]
// Mock the return value of getSpecs so we are not doing cross
// module testing
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)
})
})
describe('_runSpecs', () => {
beforeEach(() => {
launcher['_startInstance'] = vi.fn()
})
it('should not start running anything if exit routine is triggered', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100
}) } as any
launcher['_hasTriggeredExitRoutine'] = true
expect(launcher['_runSpecs']()).toBe(true)
expect(launcher['_startInstance']).toBeCalledTimes(0)
})
it('should run all specs', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: ['/a.js', 'b.js', 'c.js'],
availableInstances: 50,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js', 'c.js', 'd.js'],
availableInstances: 60,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js'],
availableInstances: 70,
runningInstances: 0,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(false)
expect(launcher['_getNumberOfRunningInstances']()).toBe(9)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(0)
expect(launcher['_schedule'][0].runningInstances).toBe(3)
expect(launcher['_schedule'][0].availableInstances).toBe(47)
expect(launcher['_schedule'][1].runningInstances).toBe(4)
expect(launcher['_schedule'][1].availableInstances).toBe(56)
expect(launcher['_schedule'][2].runningInstances).toBe(2)
expect(launcher['_schedule'][2].availableInstances).toBe(68)
})
it('should run arrayed specs in a single instance', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: ['/a.js', ['b.js', 'c.js']],
availableInstances: 50,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: [['/a.js', 'b.js', 'c.js', 'd.js']],
availableInstances: 60,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: [['/a.js'], 'b.js'],
availableInstances: 70,
runningInstances: 0,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(false)
expect(launcher['_getNumberOfRunningInstances']()).toBe(5)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(0)
expect(launcher['_schedule'][0].runningInstances).toBe(2)
expect(launcher['_schedule'][0].availableInstances).toBe(48)
expect(launcher['_schedule'][1].runningInstances).toBe(1)
expect(launcher['_schedule'][1].availableInstances).toBe(59)
expect(launcher['_schedule'][2].runningInstances).toBe(2)
expect(launcher['_schedule'][2].availableInstances).toBe(68)
})
it('should not run anything if runner failed', () => {
launcher['_runnerFailed'] = 2
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100,
bail: 1
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: ['/a.js', 'b.js', 'c.js'],
availableInstances: 50,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js', 'c.js', 'd.js'],
availableInstances: 60,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js'],
availableInstances: 70,
runningInstances: 0,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(true)
expect(launcher['_getNumberOfRunningInstances']()).toBe(0)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(0)
})
it('should run as much as maxInstances allows', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 5
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: ['/a.js', 'b.js', 'c.js'],
availableInstances: 50,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js', 'c.js', 'd.js'],
availableInstances: 60,
runningInstances: 0,
seleniumServer: {}
}, {
cid: 2,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js'],
availableInstances: 70,
runningInstances: 0,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(false)
expect(launcher['_getNumberOfRunningInstances']()).toBe(5)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(4)
})
it('should not allow to schedule more runner if no instances are available', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: ['/a.js', 'b.js', 'c.js'],
availableInstances: 1,
runningInstances: 10,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js', 'c.js', 'd.js'],
availableInstances: 2,
runningInstances: 4,
seleniumServer: {}
}, {
cid: 1,
caps: { browserName: 'chrome2' },
specs: ['/a.js', 'b.js'],
availableInstances: 0,
runningInstances: 2,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(false)
expect(launcher['_getNumberOfRunningInstances']()).toBe(19)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(6)
expect(launcher['_schedule'][0].runningInstances).toBe(11)
expect(launcher['_schedule'][0].availableInstances).toBe(0)
expect(launcher['_schedule'][1].runningInstances).toBe(6)
expect(launcher['_schedule'][1].availableInstances).toBe(0)
expect(launcher['_schedule'][2].runningInstances).toBe(2)
expect(launcher['_schedule'][2].availableInstances).toBe(0)
})
it('should not run if all specs were executed', () => {
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
maxInstances: 100
}) } as any
launcher['_schedule'] = [{
cid: 0,
caps: { browserName: 'chrome' },
specs: [],
availableInstances: 10,
runningInstances: 0,
seleniumServer: {}
}] as any
expect(launcher['_runSpecs']()).toBe(true)
expect(launcher['_getNumberOfRunningInstances']()).toBe(0)
expect(launcher['_getNumberOfSpecsLeft']()).toBe(0)
})
})
describe('startInstance', () => {
beforeEach(() => {
// 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 = vi.fn()
const caps = {
browserName: 'chrome'
}
launcher.configParser.getConfig = () => ({ onWorkerStart: onWorkerStartMock }) as any
launcher['_args'].hostname = '127.0.0.2'
expect(launcher['_runnerStarted']).toBe(0)
await launcher['_startInstance'](
['/foo.test.js'],
caps,
0,
'0-5',
0
)
expect(sleep).not.toHaveBeenCalled()
expect(launcher['_runnerStarted']).toBe(1)
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
expect(vi.mocked(launcher.runner?.run!).mock.calls[0][0]).toHaveProperty('cid', '0-5')
expect(launcher['_getRunnerId'](0)).toBe('0-0')
expect(onWorkerStartMock).toHaveBeenCalledWith(
'0-5',
caps,
['/foo.test.js'],
{ hostname: '127.0.0.2' },
[]
)
})
it('should wait before starting an instance on retry', async () => {
const onWorkerStartMock = vi.fn()
const caps = {
browserName: 'chrome'
}
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
onWorkerStart: onWorkerStartMock,
specFileRetries: 2,
specFileRetriesDelay: 0.01
}) } as any
launcher['_args'].hostname = '127.0.0.3'
await launcher['_startInstance'](
['/foo.test.js'],
caps,
0,
'0-6',
3
)
expect(sleep).toHaveBeenCalledTimes(1)
expect(sleep).toHaveBeenCalledWith(10)
expect(onWorkerStartMock).toHaveBeenCalledWith(
'0-6',
caps,
['/foo.test.js'],
{ hostname: '127.0.0.3' },
[]
)
})
it('should not wait before starting an instance on the first run', async () => {
const onWorkerStartMock = vi.fn()
const caps = {
browserName: 'chrome'
}
launcher.configParser = { getConfig: vi.fn().mockReturnValue({
onWorkerStart: onWorkerStartMock,
specFileRetries: 4,
specFileRetriesDelay: 0.01
}) } as any
launcher['_args'].hostname = '127.0.0.4'
await launcher['_startInstance'](
['/foo.test.js'],
caps,
0,
'0-7',
4
)
expect(sleep).not.toHaveBeenCalled()
expect(onWorkerStartMock).toHaveBeenCalledWith(
'0-7',
caps,
['/foo.test.js'],
{ hostname: '127.0.0.4' },
[]
)
})
})
describe('run', () => {
let config: WebdriverIO.Config = { capabilities: [{}] }
beforeEach(() => {
global.console.error = vi.fn()
config = {
// ConfigParser.addFileConfig() will return onPrepare and onComplete as arrays of functions
onPrepare: [vi.fn()],
onComplete: [vi.fn()],
capabilities: [{}],
runner: 'local',
runnerEnv: {},
outputDir: 'tempDir',
shard
}
launcher.configParser = {
getCapabilities: vi.fn().mockReturnValue({}),
getConfig: vi.fn().mockReturnValue(config),
initialize: vi.fn()
} as any
launcher.runner = { initialize: vi.fn(), shutdown: vi.fn() } as any
launcher['_runMode'] = vi.fn().mockImplementation(() => 0)
})
it('exit code 0', async () => {
expect(await launcher.run()).toEqual(0)
expect(launcher['configParser'].initialize).toBeCalledTimes(1)
expect(launcher.runner!.shutdown).toBeCalled()
expect(enableFileLogging).toHaveBeenCalledWith('tempDir')
expect(launcher.configParser.getCapabilities).toBeCalledTimes(2)
expect(launcher.configParser.getConfig).toBeCalledTimes(1)
expect(launcher.runner!.initialize).toBeCalledTimes(1)
// @ts-ignore
expect(config.onPrepare![0]).toBeCalledTimes(1)
expect(launcher['_runMode']).toBeCalledTimes(1)
// @ts-ignore
expect(config.onPrepare![0]).toBeCalledTimes(1)
expect(launcher.interface!.finalise).toBeCalledTimes(1)
})
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()
})
it('onComplete error', async () => {
// ConfigParser.addFileConfig() will return onComplete as an array of functions
config.onComplete = [() => { throw new Error() }]
expect(await launcher.run()).toEqual(1)
expect(launcher.runner!.shutdown).toBeCalled()
})
it('should shutdown runner on error', async () => {
logger.waitForBuffer = () => Promise.reject(new Error('ups'))
let error
try {
await launcher.run()
} catch (err: any) {
error = err
}
expect(launcher.runner!.shutdown).toBeCalled()
expect(error).toBeInstanceOf(Error)
})
afterEach(() => {
vi.mocked(global.console.error).mockRestore()
})
})
afterEach(() => {
vi.mocked(global.console.log).mockRestore()
vi.mocked(sleep).mockClear()
})
})