🏡 index : quine-global/webdriverio.git

import path from 'node:path'

import { expect, describe, it, vi, beforeEach, beforeAll } from 'vitest'
import puppeteer from 'puppeteer-core'

import { remote } from '../../../src/index.js'

vi.mock('fetch')
vi.mock('puppeteer-core')
/**
 * Given that Puppeteer is not a direct dependency of this package, we can't mock
 * it and dynamically import it. Instead, we mock the "userImport" helper and make
 * it resolve to the mocked Puppeteer.
 */
vi.mock('@wdio/utils', async (origMod) => {
    const orig = await origMod() as any
    // resolve the mocked puppeteer-core
    const puppeteer = await import('puppeteer-core')
    return {
        ...orig,
        userImport: vi.fn().mockResolvedValue(puppeteer.default)
    }
})
vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))

const puppeteerConnect = vi.mocked(puppeteer.connect)

describe('attach Puppeteer', () => {
    let browser: WebdriverIO.Browser

    beforeEach(() => {
        puppeteerConnect.mockClear()
    })

    beforeAll(async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar'
            },
            headers: {
                Authorization: 'OAuth token'
            }
        })
    })

    it('should pass for Chrome', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'chrome',
                'goog:chromeOptions': {
                    debuggerAddress: 'localhost:1234'
                }
            },
            requestedCapabilities: {}
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should pass for Firefox', async () => {
        const pprt = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'firefox',
                browserVersion: '79.0b'
            },
            requestedCapabilities: {
                'moz:firefoxOptions': {
                    args: ['foo', 'bar', '-remote-debugging-port', 4321, 'barfoo']
                } as any
            }
        } as WebdriverIO.Browser)
        expect(typeof pprt).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should pass for Firefox (DevTools)', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'firefox',
                browserVersion: '79.0b',
                'moz:firefoxOptions': {
                    debuggerAddress: 'localhost:1234'
                }
            },
            requestedCapabilities: {
                'moz:firefoxOptions': {
                    args: []
                } as any
            }
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should pass for Firefox and moz:debuggerAddress flag enabled', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'firefox',
                browserVersion: '79.0b',
                'moz:debuggerAddress': 'localhost:1234'
            },
            requestedCapabilities: {
                'moz:firefoxOptions': {
                    args: []
                } as any
            }
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should fail for Firefox if no info is given', async () => {
        await expect(browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'firefox',
                browserVersion: '79.0b'
            },
            requestedCapabilities: {
                'moz:firefoxOptions': {
                    args: []
                } as any
            }
        } as WebdriverIO.Browser)).rejects.toThrow('Could\'t find a websocket url')
    })

    it('should pass for Edge', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'edge',
                'ms:edgeOptions': {
                    debuggerAddress: 'localhost:1234'
                }
            },
            requestedCapabilities: {}
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should fail for old Firefox version', async () => {
        const err = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'firefox',
                browserVersion: '78.0b'
            },
            requestedCapabilities: {
                'moz:firefoxOptions': {
                    args: ['foo', 'bar', '-remote-debugging-port', 4321, 'barfoo']
                } as any
            }
        // @ts-ignore uses sync command
        } as WebdriverIO.Browser).catch(err => err)

        expect(err.message).toContain('Using DevTools capabilities is not supported for this session.')
    })

    it('should not re-attach if connection was already established', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                browserName: 'chrome',
                'goog:chromeOptions': {
                    debuggerAddress: 'localhost:1234'
                }
            },
            puppeteer: {
                connected: true
            } as any
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect).toHaveBeenCalledTimes(0)
    })

    it('should pass for Selenium CDP', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: {
                'se:cdp': 'http://my.grid:1234/session/mytestsession/se/cdp'
            }
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should pass for Aerokube Selenoid CDP', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            capabilities: {
                'selenoid:options': {
                    foo: 'bar'
                } as any
            },
            requestedCapabilities: {
                'selenoid:options': {
                    foo: 'bar'
                } as any
            },
            options: {
                hostname: 'my.grid',
                port: 4444,
                path: '/wd/hub',
                headers: {
                    Authorization: 'OAuth token'
                }
            } as any
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should pass for Aerokube Moon CDP', async () => {
        const pptr = await browser.getPuppeteer.call({
            ...browser,
            capabilities: {
                'moon:options': {
                    foo: 'bar'
                } as any
            },
            requestedCapabilities: {
                'moon:options': {
                    foo: 'bar'
                } as any
            },
            options: {
                hostname: 'my.grid',
                port: 4444,
                path: '/wd/hub',
                headers: {
                    Authorization: 'OAuth token'
                }
            } as any
        } as WebdriverIO.Browser)
        expect(typeof pptr).toBe('object')
        expect(puppeteerConnect.mock.calls).toMatchSnapshot()
    })

    it('should throw an error if Puppeteer is not supported', async () => {
        // @ts-expect-error
        globalThis.wdio = true
        await expect(browser.getPuppeteer.call({
            ...browser,
            options: browser.options,
            capabilities: { browserName: 'chrome' }
        } as WebdriverIO.Browser)).rejects.toThrow('Puppeteer is not supported in browser runner')
    })
})