🏑 index : quine-global/webdriverio.git

import path from 'node:path'
import { EventEmitter } from 'node:events'
import { expect, test, vi, beforeEach } from 'vitest'
import type { CDPSession } from 'puppeteer-core/lib/esm/puppeteer/api/CDPSession.js'
import type { Page } from 'puppeteer-core/lib/esm/puppeteer/api/Page.js'
import Auditor from '../src/auditor.js'

import CommandHandler from '../src/commands.js'

vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))

vi.mock('../src/utils', () => ({
    readIOStream: vi.fn().mockReturnValue('foobar'),
    sumByKey: vi.fn().mockReturnValue('foobar')
}))

vi.mock('../src/auditor', () => {
    const updateCommandsMock = vi.fn()
    return {
        default: class {
            traceEvents: any
            logs: any
            updateCommands = updateCommandsMock

            constructor (traceEvents: any, logs: any) {
                this.traceEvents = traceEvents
                this.logs = logs
            }
        }
    }
})

vi.mock('../src/gatherer/coverage', () => {
    const instances: any[] = []
    return {
        default: class {
            getCoverageReport = vi.fn()
            init = vi.fn()

            constructor () {
                instances.push(this)
            }
        }
    }
})

const pageMock = {
    setCacheEnabled: vi.fn(),
    emulate: vi.fn(),
    on: vi.fn(),
    tracing: {
        start: vi.fn(),
        stop: vi.fn()
    }
}

const sessionMock = {
    on: vi.fn(),
    emit: vi.fn(),
    send: vi.fn()
}

const driverMock = {}

const options = {}

const browser: any = {
    addCommand: vi.fn(),
    emit: vi.fn()
}

beforeEach(() => {
    pageMock.on.mockClear()
    pageMock.tracing.start.mockClear()
    pageMock.tracing.stop.mockClear()
    sessionMock.on.mockClear()
    // sessionMock.emit.mockClear()
    sessionMock.send.mockClear()
    browser.addCommand.mockReset()
})

test('initialization', async () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        {
            coverageReporter: {
                enable: true
            }
        },
        browser as any
    )
    await handler._initCommand()

    expect(browser.addCommand.mock.calls).toHaveLength(8)
    expect(sessionMock.on).toBeCalled()
    expect(pageMock.on).toBeCalled()

    expect(handler['_session']?.send).toBeCalledWith('Network.enable')
    expect(handler['_session']?.send).toBeCalledWith('Runtime.enable')
    expect(handler['_session']?.send).toBeCalledWith('Page.enable')

    handler['_devtoolsGatherer'] = { onMessage: vi.fn() } as any
    handler['_propagateWSEvents']({ method: 'foo', params: 'bar' })
    expect(handler['_devtoolsGatherer']?.onMessage).toBeCalledTimes(1)
    expect(handler['_devtoolsGatherer']?.onMessage).toBeCalledWith({ method:'foo', params: 'bar' })
    expect((handler['_browser'] as any).emit).toBeCalledTimes(1)
    expect((handler['_browser'] as any).emit).toBeCalledWith('foo', 'bar')
})

test('getTraceLogs', () => {
    const commander = new CommandHandler(
        sessionMock as unknown as CDPSession,
        pageMock as unknown as Page,
        driverMock as any,
        options as any,
        browser
    )
    commander['_traceEvents'] = [{ foo: 'bar' }] as any
    expect(commander.getTraceLogs()).toEqual([{ foo: 'bar' }])
})

test('startTracing', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler.startTracing()

    expect(handler['_isTracing']).toBe(true)
    expect(handler.startTracing.bind(handler)).toThrow()
    expect(pageMock.tracing.start).toBeCalledTimes(1)
})

test('endTracing', async () => {
    pageMock.tracing.stop.mockResolvedValue(Buffer.from('{ "traceEvents": "foobar" }'))
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_isTracing'] = true

    const traceEvents = await handler.endTracing()
    expect(pageMock.tracing.stop).toBeCalledTimes(1)
    expect(traceEvents).toEqual({ traceEvents: 'foobar' })
    expect(handler['_isTracing']).toBe(false)
})

test('endTracing throws if not tracing', async () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    const err = await handler.endTracing().catch((err) => err)
    expect(err.message).toContain('No tracing was initiated')
})

test('endTracing throws if parsing of trace events fails', async () => {
    pageMock.tracing.stop.mockResolvedValue(Buffer.from('{ "traceEven'))
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_isTracing'] = true
    const err = await handler.endTracing().catch((err) => err)
    expect(err.message).toContain("Couldn't parse trace events")
})

test('getPageWeight', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_networkHandler'].requestTypes = {
        Document: { size: 23343, encoded: 7674, count: 1 },
        Image: { size: 53479, encoded: 53479, count: 6 },
        Other: { size: 0, encoded: 0, count: 1 }
    }

    const { pageWeight, transferred, requestCount, details } = handler.getPageWeight()
    expect(pageWeight).toBe('foobar')
    expect(transferred).toBe('foobar')
    expect(requestCount).toBe('foobar')
    expect(details).toEqual(handler['_networkHandler'].requestTypes)
})

test('beforeCmd', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_traceGatherer'] = { startTracing: vi.fn() } as any
    handler.setThrottlingProfile = vi.fn()

    handler['_networkThrottling'] = 'offline'
    handler['_cpuThrottling'] = 2
    handler['_cacheEnabled'] = true

    // @ts-ignore test without paramater
    handler._beforeCmd()
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(0)

    handler['_shouldRunPerformanceAudits'] = true
    // @ts-ignore test without paramater
    handler._beforeCmd()
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(0)

    // @ts-ignore test with only one paramater
    handler._beforeCmd('foobar')
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(0)

    handler._beforeCmd('navigateTo', ['some page'])
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(1)
    expect(handler['_traceGatherer']?.startTracing).toBeCalledWith('some page')
    expect(handler.setThrottlingProfile).toBeCalledWith('offline', 2, true)

    handler._beforeCmd('url', ['next page'])
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(2)
    expect(handler['_traceGatherer']?.startTracing).toBeCalledWith('next page')
    expect(handler.setThrottlingProfile).toBeCalledWith('offline', 2, true)

    handler._beforeCmd('click', ['some other page'])
    expect(handler['_traceGatherer']?.startTracing).toBeCalledTimes(3)
    expect(handler['_traceGatherer']?.startTracing).toBeCalledWith('click transition')
})

test('afterCmd', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_traceGatherer'] = { once: vi.fn() } as any

    // @ts-ignore test without paramater
    handler._afterCmd()
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(0)

    // @ts-ignore access mock
    handler['_traceGatherer']['isTracing'] = true
    // @ts-ignore test without paramater
    handler._afterCmd()
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(0)

    handler._afterCmd('foobar')
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(0)

    handler._afterCmd('navigateTo')
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(3)

    handler._afterCmd('url')
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(6)

    handler._afterCmd('click')
    expect(handler['_traceGatherer']?.once).toBeCalledTimes(9)
})

test('afterCmd: should create a new auditor instance and should update the browser commands', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_traceGatherer'] = new EventEmitter() as any

    // @ts-ignore access mock
    handler['_traceGatherer']['isTracing'] = true
    handler['_devtoolsGatherer'] = { getLogs: vi.fn() } as any
    handler['_browser'] = 'some browser' as any
    handler._afterCmd('url')
    handler['_traceGatherer']?.emit('tracingComplete', { some: 'events' })

    const auditor = new Auditor()
    expect(auditor.updateCommands).toBeCalledWith('some browser')
})

test('afterCmd: should update browser commands even if failed', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_traceGatherer'] = new EventEmitter() as any

    // @ts-ignore access mock
    handler['_traceGatherer']['isTracing'] = true
    handler['_devtoolsGatherer'] = { getLogs: vi.fn() } as any
    handler['_browser'] = 'some browser' as any
    handler._afterCmd('url')
    handler['_traceGatherer']?.emit('tracingError', new Error('boom'))

    const auditor = new Auditor()
    expect(auditor.updateCommands).toBeCalledWith('some browser', expect.any(Function))
})

test('afterCmd: should continue with command after tracingFinished was emitted', async () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler['_traceGatherer'] = new EventEmitter() as any

    // @ts-ignore access mock
    handler['_traceGatherer']['isTracing'] = true
    handler.setThrottlingProfile = vi.fn()

    const start = Date.now()
    setTimeout(() => handler['_traceGatherer']?.emit('tracingFinished'), 100)
    await handler._afterCmd('navigateTo')

    expect(Date.now() - start).toBeGreaterThan(98)
    expect(handler.setThrottlingProfile).toBeCalledWith('online', 0, true)
})

test('enablePerformanceAudits: applies some default values', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler.enablePerformanceAudits()

    expect(handler['_networkThrottling']).toBe('online')
    expect(handler['_cpuThrottling']).toBe(0)
    expect(handler['_cacheEnabled']).toBe(false)
    expect(handler['_formFactor']).toBe('desktop')
})

test('enablePerformanceAudits: applies some custom values', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler.enablePerformanceAudits({
        networkThrottling: 'Regular 2G',
        cpuThrottling: 42,
        cacheEnabled: true,
        formFactor: 'mobile'
    })

    expect(handler['_networkThrottling']).toBe('Regular 2G')
    expect(handler['_cpuThrottling']).toBe(42)
    expect(handler['_cacheEnabled']).toBe(true)
    expect(handler['_formFactor']).toBe('mobile')
})

test('disablePerformanceAudits', () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )
    handler.enablePerformanceAudits({
        networkThrottling: 'Regular 2G',
        cpuThrottling: 42,
        cacheEnabled: true,
        formFactor: 'mobile'
    })
    handler.disablePerformanceAudits()
    expect(handler['_shouldRunPerformanceAudits']).toBe(false)
})

test('setThrottlingProfile', async () => {
    const handler = new CommandHandler(
        sessionMock as any,
        pageMock as any,
        driverMock as any,
        options as any,
        browser as any
    )

    await handler.setThrottlingProfile('GPRS', 42, true)
    expect(pageMock.setCacheEnabled).toBeCalledWith(true)
    expect(sessionMock.send).toBeCalledWith('Emulation.setCPUThrottlingRate', { rate: 42 })
    expect(sessionMock.send).toBeCalledWith('Network.emulateNetworkConditions', {
        downloadThroughput: 6400,
        latency: 500,
        offline: false,
        uploadThroughput: 2560
    })

    pageMock.setCacheEnabled.mockClear()
    sessionMock.send.mockClear()
    await handler.setThrottlingProfile()
    expect(pageMock.setCacheEnabled).toBeCalledWith(false)
    expect(sessionMock.send).toBeCalledWith('Emulation.setCPUThrottlingRate', { rate: 0 })
    expect(sessionMock.send).toBeCalledWith('Network.emulateNetworkConditions', {
        downloadThroughput: -1,
        latency: 0,
        offline: false,
        uploadThroughput: -1
    })
})