webdriverio/packages/webdriver/tests/command.test.ts
2022-07-20 07:51:06 +00:00

337 lines
11 KiB
TypeScript

import path from 'node:path'
import { EventEmitter } from 'node:events'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import logger from '@wdio/logger'
import type Protocols from '@wdio/protocols'
import type { Options } from '@wdio/types'
// @ts-expect-error mock feature
import RequestMock, { thenMock } from '../src/request/node'
import commandWrapper from '../src/command'
import { BaseClient } from '../src/types'
vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))
vi.mock('got')
const log = logger('webdriver')
const commandPath = '/session/:sessionId/element/:elementId/element'
const commandMethod = 'POST'
const commandEndpoint: Protocols.CommandEndpoint = {
command: 'findElementFromElement',
ref: 'https://w3c.github.io/webdriver/webdriver-spec.html#dfn-find-element-from-element',
description: '',
variables: [{
name: 'elementId',
description: 'the id of an element returned in a previous call to Find Element(s)'
}],
parameters: [{
name: 'using',
type: 'string',
description: 'a valid element location strategy',
required: true
}, {
name: 'value',
type: 'string',
description: 'the actual selector that will be used to find an element',
required: true
}, {
name: 'customParam',
type: 'number',
description: 'a random not required param',
required: false
}, {
name: 'customArrayParam',
type: '(string|object|number|boolean|undefined)[]',
description: 'a random not required param',
required: false
}]
}
vi.mock('../src/request/node', () => {
const thenMock = vi.fn().mockResolvedValue({ value: 15 })
return {
thenMock,
default: vi.fn().mockReturnValue({
makeRequest: () => ({
then: thenMock,
catch: vi.fn()
}),
on: vi.fn()
})
}
})
class FakeClient extends EventEmitter {
isW3C = false
isChrome = false
isAndroid = false
isMobile = false
isIOS = false
isSauce = false
isFirefox = false
isDevTools =false
isSeleniumStandalone = false
sessionId = '123'
capabilities = {}
requestedCapabilities = {}
options = {
logLevel: 'warn' as Options.WebDriverLogTypes
} as any
}
const scope: BaseClient = new FakeClient()
type mockResponse = (...args: any[]) => any
describe('command wrapper', () => {
it('should fail if wrong arguments are passed in', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await expect(commandFn)
.rejects
.toThrow(/Wrong parameters applied for findElementFromElement/)
})
it('should fail if arguments are malformed', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await expect(() => commandFn.call(scope, '123', 123, '123'))
.rejects
.toThrow(/Malformed type for "using" parameter of command/)
})
it('should fail if not required param has wrong type', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await expect(() => commandFn.call(scope, '123', '123', '123', 'foobar'))
.rejects
.toThrow(/Malformed type for "customParam" parameter of command/)
})
it('should throw if param type within array is not met', async () => {
expect.assertions(1)
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await expect(commandFn.call(scope, '123', '123', '123', 234, () => {}))
.rejects
.toThrow(/Actual: \(function\)\[\]/)
})
it('should do a proper request', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await commandFn.call(scope, '123', 'css selector', '#body', undefined) as unknown as mockResponse
const callback = thenMock.mock.calls[0][0]
expect(callback({ value: 14 })).toBe(14)
vi.mocked(thenMock).mockClear()
expect(RequestMock).toHaveBeenCalledWith(
'POST',
'/session/:sessionId/element/123/element',
{
using: 'css selector',
value: '#body'
},
false
)
vi.mocked(RequestMock).mockClear()
})
it('should do a proper request with non required params', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await commandFn.call(scope, '123', 'css selector', '#body', 123) as unknown as mockResponse
expect(RequestMock).toHaveBeenCalledWith(
'POST',
'/session/:sessionId/element/123/element',
{
using: 'css selector',
value: '#body',
customParam: 123
},
false
)
vi.mocked(RequestMock).mockClear()
})
it('should encode uri parameters', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint)
await commandFn.call(scope, '/path', 'css selector', '#body', 123)
expect(RequestMock).toHaveBeenCalledWith(
'POST',
'/session/:sessionId/element/%2Fpath/element',
expect.anything(),
false
)
vi.mocked(RequestMock).mockClear()
})
it('should double encode uri parameters if using selenium', async () => {
const commandFn = commandWrapper(commandMethod, commandPath, commandEndpoint, true)
await commandFn.call(scope, '/path', 'css selector', '#body', 123)
expect(RequestMock).toHaveBeenCalledWith(
'POST',
'/session/:sessionId/element/%252Fpath/element',
expect.anything(),
false
)
vi.mocked(RequestMock).mockClear()
})
})
describe('command wrapper result log', () => {
async function getRequestCallback (method: string, path: string, endpoint: Protocols.CommandEndpoint) {
const commandFn = commandWrapper(method, path, endpoint)
await commandFn.call(scope)
expect(RequestMock).toHaveBeenCalledTimes(1)
expect(RequestMock).toHaveBeenCalledWith(method, path, expect.any(Object), false)
const callback = thenMock.mock.calls[0][0]
vi.mocked(RequestMock).mockClear()
vi.mocked(thenMock).mockClear()
vi.mocked(log.info).mockClear()
return callback
}
const takeScreenshotCmd = {
path: '/foobar',
method: 'GET',
endpoint: {
command: 'takeScreenshot',
ref: 'https://foobar.com',
parameters: [],
description: ''
}
}
const deleteSessionCmd = {
path: '/foobar',
method: 'POST',
endpoint: {
command: 'deleteSession',
ref: 'https://foobar.com',
parameters: [],
description: ''
}
}
const scenarios = [{
title: 'truncate long string value',
command: { ...takeScreenshotCmd },
value: 'f'.repeat(65),
log: 'f'.repeat(61) + '...'
}, {
title: 'truncate long string value',
command: {
...takeScreenshotCmd,
endpoint: {
...takeScreenshotCmd.endpoint,
command: 'takeElementScreenshot'
}
},
value: 'f'.repeat(123),
log: 'f'.repeat(61) + '...'
}, {
title: 'truncate long string value',
command: {
...takeScreenshotCmd,
endpoint: {
...takeScreenshotCmd.endpoint,
command: 'startRecordingScreen'
}
},
value: 'f'.repeat(123),
log: 'f'.repeat(61) + '...'
}, {
title: 'truncate long string value',
command: {
...takeScreenshotCmd,
endpoint: {
...takeScreenshotCmd.endpoint,
command: 'stopRecordingScreen'
}
},
value: 'f'.repeat(123),
log: 'f'.repeat(61) + '...'
}, {
title: 'do nothing to values with length less then 65',
command: { ...takeScreenshotCmd },
value: 'f'.repeat(64),
get log () { return this.value }
}, {
title: 'not truncate long string value',
command: {
...takeScreenshotCmd,
endpoint: {
...takeScreenshotCmd.endpoint,
command: 'any-other-command'
}
},
value: 'f'.repeat(66),
get log () { return this.value }
}, {
title: 'do nothing to non string value: array',
command: { ...takeScreenshotCmd },
value: [],
get log () { return this.value }
}, {
title: 'do nothing to non string value: object',
command: { ...takeScreenshotCmd },
value: {},
get log () { return this.value }
}, {
title: 'do nothing to non string value: number',
command: { ...takeScreenshotCmd },
value: 3,
get log () { return this.value }
}, {
title: 'do nothing to non string value: boolean',
command: { ...takeScreenshotCmd },
value: false,
get log () { return this.value }
}]
logger.clearLogger = vi.fn()
const clearLoggerSpy = vi.spyOn(logger, 'clearLogger')
beforeEach(() => {
delete process.env.WDIO_WORKER_ID
vi.clearAllMocks()
})
for (const scenario of scenarios) {
it(`should ${scenario.title} for ${scenario.command.endpoint.command}`, async () => {
const resultFunction = await getRequestCallback(
scenario.command.method,
scenario.command.path,
scenario.command.endpoint
) as unknown as mockResponse
resultFunction({ value: scenario.value })
expect(vi.mocked(log.info).mock.calls[0][1]).toBe(scenario.log)
expect(clearLoggerSpy).not.toHaveBeenCalled()
})
}
it('should be no result in log if there is value in response', async () => {
process.env.WDIO_WORKER_ID = '0-0'
const resultFunction = await getRequestCallback(
deleteSessionCmd.method,
takeScreenshotCmd.path,
takeScreenshotCmd.endpoint
) as unknown as mockResponse
resultFunction({})
expect(vi.mocked(log.info).mock.calls).toHaveLength(0)
expect(clearLoggerSpy).not.toHaveBeenCalled()
})
it('should call clearLogger on deleteSession cmd', async () => {
const resultFunction = await getRequestCallback(
deleteSessionCmd.method,
deleteSessionCmd.path,
deleteSessionCmd.endpoint
) as unknown as mockResponse
resultFunction({})
expect(vi.mocked(log.info).mock.calls).toHaveLength(0)
expect(clearLoggerSpy).toHaveBeenCalledTimes(1)
})
})