🏡 index : quine-global/webdriverio.git

import type { Capabilities, Options } from '@wdio/types'

const DEFAULT_HOSTNAME = '127.0.0.1'
const DEFAULT_PORT = 4444
const DEFAULT_PROTOCOL = 'http'
const DEFAULT_PATH = '/'
const LEGACY_PATH = '/wd/hub'

const REGION_MAPPING = {
    'us': 'us-west-1.', // default endpoint
    'eu': 'eu-central-1.',
    'eu-central-1': 'eu-central-1.',
    'us-east-4': 'us-east-4.',
}

interface BackendConfigurations {
    port?: number
    hostname?: string
    user?: string
    key?: string
    protocol?: string
    region?: Options.SauceRegions
    path?: string
    capabilities?: Capabilities.RequestedStandaloneCapabilities
}

function getSauceEndpoint (
    region: keyof typeof REGION_MAPPING,
    { isRDC, isVisual }: { isRDC?: boolean, isVisual?: boolean } = {}
) {
    const shortRegion = REGION_MAPPING[region] ? region : 'us'
    if (isRDC) {
        return `${shortRegion}1.appium.testobject.com`
    } else if (isVisual) {
        return 'hub.screener.io'
    }

    return `ondemand.${REGION_MAPPING[shortRegion]}saucelabs.com`
}

/**
 * helper to detect the Selenium backend according to given capabilities
 */
export default function detectBackend(options: BackendConfigurations = {}) {
    const { port, hostname, user, key, protocol, region, path, capabilities } = options

    /**
     * browserstack
     * e.g. zHcv9sZ39ip8ZPsxBVJ2
     */
    if (typeof user === 'string' && typeof key === 'string' && key.length === 20) {
        return {
            protocol: protocol || 'https',
            hostname: hostname || 'hub-cloud.browserstack.com',
            port: port || 443,
            path: path || LEGACY_PATH
        }
    }

    /**
     * testingbot
     * e.g. ec337d7b677720a4dde7bd72be0bfc67
     */
    if (typeof user === 'string' && typeof key === 'string' && key.length === 32) {
        return {
            protocol: protocol || 'https',
            hostname: hostname || 'hub.testingbot.com',
            port: port || 443,
            path: path || LEGACY_PATH
        }
    }

    /**
     * Sauce Labs
     * e.g. 50aa152c-1932-B2f0-9707-18z46q2n1mb0
     *
     * For Sauce Labs Legacy RDC we only need to determine if the sauce option has a `testobject_api_key`.
     * Same for Sauce Visual where an apiKey can be passed in through the capabilities (soon to be legacy too).
     */
    const isVisual = Boolean(!Array.isArray(capabilities) && capabilities && (capabilities as WebdriverIO.Capabilities)['sauce:visual']?.apiKey)
    if ((typeof user === 'string' && typeof key === 'string' && key.length === 36) ||
        // Or only RDC or visual
        isVisual
    ) {
        const sauceRegion = region as keyof typeof REGION_MAPPING

        return {
            protocol: protocol || 'https',
            hostname: hostname || getSauceEndpoint(sauceRegion, { isVisual }),
            port: port || 443,
            path: path || LEGACY_PATH
        }
    }

    /**
     * Lambdatest
     * e.g. cYAjKrqGwyPjPQv41ICDF4C5OjlxzA9epZsnugVJJxqOReWRWU
     */
    if (typeof user === 'string' && typeof key === 'string' && key.length === 50) {
        return {
            protocol: protocol || DEFAULT_PROTOCOL,
            hostname: hostname || 'hub.lambdatest.com',
            port: port || 80,
            path: path || LEGACY_PATH
        }
    }

    if (
        /**
         * user and key are set in config
         */
        (typeof user === 'string' || typeof key === 'string') &&
        /**
         * but no custom WebDriver endpoint was configured
         */
        !hostname
    ) {
        throw new Error(
            'A "user" or "key" was provided but could not be connected to a ' +
            'known cloud service (Sauce Labs, Browerstack, Testingbot or Lambdatest). ' +
            'Please check if given user and key properties are correct!'
        )
    }

    /**
     * default values if on of the WebDriver criticial options is set
     */
    if (hostname || port || protocol || path) {
        return {
            hostname: hostname || DEFAULT_HOSTNAME,
            port: port || DEFAULT_PORT,
            protocol: protocol || DEFAULT_PROTOCOL,
            path: path || DEFAULT_PATH
        }
    }

    /**
     * no cloud provider detected, pass on provided params and eventually
     * fallback to DevTools protocol
     */
    return { hostname, port, protocol, path }
}