🏡 index : quine-global/webdriverio.git

import path from 'node:path'
import { expect, describe, it, vi, beforeEach } from 'vitest'
import logger from '@wdio/logger'
import type { IosDetailedContext } from '../../../src/index.js'
import { remote } from '../../../src/index.js'

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

describe('switchContext test', () => {
    let browser: WebdriverIO.Browser
    let androidContexts = [] as any
    let iOSContexts = [] as IosDetailedContext[]
    let logSpy

    beforeEach(async () => {
        androidContexts = [
            { id: 'NATIVE_APP' },
            {
                androidWebviewData: {
                    attached: true,
                    empty: false,
                    height: 2589,
                    neverAttached: false,
                    screenX: 0,
                    screenY: 151,
                    visible: true,
                    width: 1344
                },
                id: 'WEBVIEW_com.wdiodemoapp',
                title: 'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
                url: 'https://webdriver.io/',
                packageName: 'com.wdiodemoapp',
                webviewPageId: 'B32A3DC8B0FEA92C435CE26D51F46986',
            },
            {
                androidWebviewData: {
                    attached: true,
                    empty: false,
                    height: 2589,
                    neverAttached: false,
                    screenX: 0,
                    screenY: 151,
                    visible: true,
                    width: 1344
                },
                id: 'WEBVIEW_com.otherApp',
                title: 'Other Apps · I am just another app',
                url: 'https://otherapp.io/',
                packageName: 'com.otherApp',
                webviewPageId: '6751C1E052A63B0CA27F839216AEF4B8',
            },
            {
                androidWebviewData: {
                    attached: true,
                    empty: false,
                    height: 2589,
                    neverAttached: false,
                    screenX: 0,
                    screenY: 151,
                    visible: false,
                    width: 1344
                },
                id: 'WEBVIEW_com.otherNonVisibleApp',
                title: 'Other Apps · I am just another app which is not visible',
                url: 'https://othernotvisibleapp.io/',
                packageName: 'com.otherNonVisibleApp',
                webviewPageId: 'EE01B05A515BF58395772B5EA297BE78',
            },
            {
                androidWebviewData: {
                    attached: false,
                    empty: false,
                    height: 0,
                    neverAttached: false,
                    screenX: 0,
                    screenY: 0,
                    visible: false,
                    width: 0
                },
                id: 'WEBVIEW_chrome',
                title: 'Android | Get more done with Google on Android-phones and devices',
                url: 'https://www.android.com/',
                packageName: 'com.android.chrome',
                webviewPageId: '0',
            },
        ]
        iOSContexts = [
            { id: 'NATIVE_APP' },
            {
                id: 'WEBVIEW_86150.1',
                title: 'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
                url: 'https://webdriver.io/',
                bundleId: 'org.reactjs.native.example.wdiodemoapp'
            },
            {
                id: 'WEBVIEW_86152.1',
                title: 'Apple',
                url: 'https://www.apple.com/',
                bundleId: 'com.apple.mobilesafari'
            }
        ]
        vi.mocked(fetch).mockClear()
    })

    it('should call the default Appium endpoint if the Webview option as a string is provided', async () => {
        logSpy = vi.spyOn(log, 'info')
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
            } as any
        })
        await browser.switchContext('WEBVIEW_29051')
        const { calls } = vi.mocked(fetch).mock

        expect(calls).toHaveLength(2)

        const [[sessionCallUrl], [callUrl]] = calls as any

        expect(sessionCallUrl.pathname).toEqual('/session')
        expect(callUrl.pathname).toEqual('/session/foobar-123/context')
        expect(logSpy).toHaveBeenCalledWith('The standard Appium `context`-method is used. If you want to switch to a webview with a specific title or url, please provide an object with the `title` or `url` property. See https://webdriver.io/docs/api/mobile/switchContext for more information.')
        logSpy.mockRestore()
    })

    it('should find a matching context for iOS and switch to it', async () => {
        logSpy = vi.spyOn(log, 'info')
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'iOS',
            } as any
        })
        const getContextsSpy = vi.spyOn(browser, 'getContexts').mockResolvedValue(iOSContexts)
        const executeSpy = vi.spyOn(browser, 'execute').mockResolvedValue({ bundleId:'com.apple.mobilesafari' })
        const switchAppiumContextSpy = vi.spyOn(browser, 'switchAppiumContext')

        await browser.switchContext({ title: 'Apple' })
        expect(getContextsSpy).toHaveBeenCalledTimes(1)
        expect(getContextsSpy).toHaveBeenCalledWith({
            'filterByCurrentAndroidApp': false,
            'isAndroidWebviewVisible': false,
            'returnAndroidDescriptionData': true,
            'returnDetailedContexts': true,
        })
        expect(executeSpy).toHaveBeenCalledTimes(1)
        expect(executeSpy).toHaveBeenCalledWith('mobile: activeAppInfo')
        expect(logSpy).toHaveBeenCalledWith('WebdriverIO found a matching context:', JSON.stringify(iOSContexts[2], null, 2))
        expect(switchAppiumContextSpy).toHaveBeenCalledWith(iOSContexts[2].id)

        logSpy.mockRestore()
        getContextsSpy.mockRestore()
        executeSpy.mockRestore()
        switchAppiumContextSpy.mockRestore()
    })

    it('should find a matching context for Android and switch to it', async () => {
        logSpy = vi.spyOn(log, 'info')
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'Android',
            } as any
        })
        const getContextsSpy = vi.spyOn(browser, 'getContexts').mockResolvedValue(androidContexts)
        const getCurrentPackageSpy = vi.spyOn(browser, 'getCurrentPackage').mockResolvedValue('com.wdiodemoapp')
        const switchAppiumContextSpy = vi.spyOn(browser, 'switchAppiumContext')
        const switchToWindowSpy = vi.spyOn(browser, 'switchToWindow')

        await browser.switchContext({ title: /.*WebdriverIO.*/, url: /.*webdriver.io/ })
        expect(getContextsSpy).toHaveBeenCalledTimes(1)
        expect(getContextsSpy).toHaveBeenCalledWith({
            'filterByCurrentAndroidApp': false,
            'isAndroidWebviewVisible': false,
            'returnAndroidDescriptionData': true,
            'returnDetailedContexts': true,
        })
        expect(getCurrentPackageSpy).toHaveBeenCalledTimes(1)
        expect(logSpy).toHaveBeenCalledWith('WebdriverIO found a matching context:', JSON.stringify(androidContexts[1], null, 2))
        expect(switchAppiumContextSpy).toHaveBeenCalledWith(androidContexts[1].id)
        expect(switchToWindowSpy).toHaveBeenCalledWith(androidContexts[1].webviewPageId)

        logSpy.mockRestore()
        getContextsSpy.mockRestore()
        getCurrentPackageSpy.mockRestore()
        switchAppiumContextSpy.mockRestore()
        switchToWindowSpy.mockRestore()
    })

    it('should throw an error for non-mobile platforms', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
            } as any
        })
        await expect(browser.switchContext('foo')).rejects.toThrow('The `switchContext` command is only available for mobile platforms.')
    })

    it('should throw an error when no options are provided', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'Android',
            } as any
        })

        // @ts-expect-error
        await expect(browser.switchContext()).rejects.toThrow('You need to provide at least a context name to switch to. See https://webdriver.io/docs/api/mobile/switchContext for more information.')
    })

    it('should throw an error when no title and url is provided', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'Android',
            } as any
        })

        await expect(browser.switchContext({})).rejects.toThrow('You need to provide at least a `title` or `url` property to use full potential of the `switchContext` command. See https://webdriver.io/docs/api/mobile/switchContext for more information.')
    })

    it('should throw an error when no matching context is found for iOS based on the title', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'iOS',
            } as any
        })
        const getContextsSpy = vi.spyOn(browser, 'getContexts').mockResolvedValue(iOSContexts)
        const executeSpy = vi.spyOn(browser, 'execute').mockResolvedValue({ bundleId:'com.foo' })
        const switchAppiumContextSpy = vi.spyOn(browser, 'switchAppiumContext')

        await expect(browser.switchContext({ title: 'No matching Title' })).rejects.toThrowErrorMatchingSnapshot()

        getContextsSpy.mockRestore()
        executeSpy.mockRestore()
        switchAppiumContextSpy.mockRestore()
    })

    it('should throw an error when no matching context is found for Android based on the url', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'Android',
            } as any
        })
        const getContextsSpy = vi.spyOn(browser, 'getContexts').mockResolvedValue(androidContexts)
        const getCurrentPackageSpy = vi.spyOn(browser, 'getCurrentPackage').mockResolvedValue('com.foo')
        const switchAppiumContextSpy = vi.spyOn(browser, 'switchAppiumContext')

        await expect(browser.switchContext({ url: 'https://no-matching-url.io' })).rejects.toThrowErrorMatchingSnapshot()

        getContextsSpy.mockRestore()
        getCurrentPackageSpy.mockRestore()
        switchAppiumContextSpy.mockRestore()
    })

    it('should throw an error when no matching context is found for Android based on the title and url regex', async () => {
        browser = await remote({
            baseUrl: 'http://foobar.com',
            capabilities: {
                browserName: 'foobar',
                mobileMode: true,
                platformName: 'Android',
            } as any
        })
        const getContextsSpy = vi.spyOn(browser, 'getContexts').mockResolvedValue(androidContexts)
        const getCurrentPackageSpy = vi.spyOn(browser, 'getCurrentPackage').mockResolvedValue('com.foo')
        const switchAppiumContextSpy = vi.spyOn(browser, 'switchAppiumContext')

        await expect(browser.switchContext({
            title: /No matching Title/,
            url: /.*io/,
            androidWebviewConnectionRetryTime: 1000,
            androidWebviewConnectTimeout: 1000,
        })).rejects.toThrowErrorMatchingSnapshot()

        getContextsSpy.mockRestore()
        getCurrentPackageSpy.mockRestore()
        switchAppiumContextSpy.mockRestore()
    })
})