import path from 'node:path'
import type { ChildProcess } from 'node:child_process'
import { WritableStreamBuffer } from 'stream-buffers'
import { describe, expect, it, vi } from 'vitest'
import logger from '@wdio/logger'
import type { Workers } from '@wdio/types'
import Worker from '../src/worker.js'
const workerConfig = {
cid: '0-3',
configFile: '/foobar',
caps: {},
specs: ['/some/spec'],
execArgv: [],
retries: 0
}
vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))
const mockXvfbManager = {
init: vi.fn().mockResolvedValue(true)
}
describe('handleMessage', () => {
it('should emit payload with cid', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.emit = vi.fn()
worker['_handleMessage']({ foo: 'bar' } as unknown as Workers.WorkerMessage)
expect(worker.emit).toBeCalledWith('message', {
foo: 'bar',
cid: '0-3'
})
})
it('should un mark worker as busy if command is finished', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.isBusy = true
worker['_handleMessage']({ name: 'finishedCommand' } as unknown as Workers.WorkerMessage)
expect(worker.isBusy).toBe(false)
})
it('should mark worker as ready if ready message was received', async () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker['_handleMessage']({ name: 'ready' } as unknown as Workers.WorkerMessage)
expect(await worker.isReady).toBe(true)
})
it('stores sessionId and connection data to worker instance', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.emit = vi.fn()
const payload = {
name: 'sessionStarted',
content: {
sessionId: 'abc123',
bar: 'foo'
}
}
worker['_handleMessage'](payload as unknown as Workers.WorkerMessage)
expect(worker.sessionId).toEqual('abc123')
})
it('stores instances to worker instance in Multiremote mode', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
const payload = {
name: 'sessionStarted',
content: {
instances: { foo: { sessionId: 'abc123' } },
isMultiremote: true
}
}
worker['_handleMessage'](payload as unknown as Workers.WorkerMessage)
expect(worker.instances).toEqual({ foo: { sessionId: 'abc123' } })
expect(worker.isMultiremote).toEqual(true)
})
})
describe('handleError', () => {
it('should emit error', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.emit = vi.fn()
worker['_handleError']({ foo: 'bar' } as unknown as Error)
expect(worker.emit).toBeCalledWith('error', {
cid: '0-3',
foo: 'bar'
})
})
})
describe('handleExit', () => {
it('should handle it', () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
const childProcess = { kill: vi.fn() }
worker.childProcess = childProcess as unknown as ChildProcess
worker.isBusy = true
worker.emit = vi.fn()
worker['_handleExit'](42)
expect(worker.childProcess).toBe(undefined)
expect(worker.isBusy).toBe(false)
expect(worker.emit).toBeCalledWith('exit', {
cid: '0-3',
exitCode: 42,
retries: 0,
specs: ['/some/spec']
})
})
})
describe('postMessage', () => {
it('should log if the cid is busy and exit', async () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
const log = logger('webdriver')
vi.spyOn(log, 'info').mockImplementation((string) => string)
worker.isBusy = true
await worker.postMessage('test-message', {})
expect(log.info)
.toHaveBeenCalledWith('worker with cid 0-3 already busy and can\'t take new commands')
})
it('should create a process if it does not have one', async () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.isReady = Promise.resolve(true)
worker.childProcess = undefined
vi.spyOn(worker, 'startProcess').mockImplementation(
async () => ({ send: vi.fn() }) as unknown as ChildProcess)
await worker.postMessage('test-message', {})
expect(worker.startProcess).toHaveBeenCalled()
expect(worker.isBusy).toBeTruthy()
vi.mocked(worker.startProcess).mockRestore()
})
it('should wait sending the command until worker is ready', async () => {
const worker = new Worker({} as any, workerConfig, new WritableStreamBuffer(), new WritableStreamBuffer(), mockXvfbManager as any)
worker.childProcess = { send: vi.fn() } as any
await worker.postMessage('test-message', {})
expect(worker.childProcess!.send).toBeCalledTimes(0)
worker.isReadyResolver(true)
await worker.isReady
expect(worker.childProcess!.send).toBeCalledTimes(1)
})
})