import type { Options } from '@wdio/types' import { DEFAULT_CONFIGS } from './constants.js' export const validObjectOrArray = (object: object): object is object | Array => (Array.isArray(object) && object.length > 0) || (typeof object === 'object' && Object.keys(object).length > 0) /** * remove line numbers from file path, ex: * `/foo:9` or `c:\bar:14:5` * @param {string} filePath path to spec file * @returns {string} */ export function removeLineNumbers(filePath: string) { const matcher = filePath.match(/:\d+(:\d+$|$)/) if (matcher) { filePath = filePath.substring(0, matcher.index) } return filePath } /** * does spec file path contain Cucumber's line number, ex * `/foo/bar:9` or `c:\bar\foo:14:5` * @param {string|string[]} spec */ export function isCucumberFeatureWithLineNumber(spec: string | string[]) { const specs = Array.isArray(spec) ? spec : [spec] return specs.some((s) => /:\d+(:\d+$|$)/.test(s)) } export function isCloudCapability(caps: WebdriverIO.Capabilities) { return Boolean(caps && (caps['bstack:options'] || caps['sauce:options'] || caps['tb:options'])) } /** * Creates a configuration object while providing types for both TypeScript and Javascript */ export const defineConfig = (options?: Partial | WebdriverIO.Config): WebdriverIO.Config => ({ ...DEFAULT_CONFIGS(), ...options, }) /** * validates configurations based on default values * @param {Object} defaults object describing all allowed properties * @param {Object} options option to check against * @return {Object} validated config enriched with default values */ export function validateConfig(defaults: Options.Definition, options: T, keysToKeep = [] as (keyof T)[]) { const params = {} as T for (const [name, expectedOption] of Object.entries(defaults) as [keyof Options.Definition, NonNullable[keyof Options.Definition]>][]) { /** * check if options is given */ if (typeof options[name] === 'undefined' && !expectedOption.default && expectedOption.required) { throw new Error(`Required option "${name.toString()}" is missing`) } if (typeof options[name] === 'undefined' && expectedOption.default) { params[name] = expectedOption.default } if (typeof options[name] !== 'undefined') { const optValue = options[name] if (typeof optValue !== expectedOption.type) { throw new Error(`Expected option "${name.toString()}" to be type of ${expectedOption.type} but was ${typeof options[name]}`) } if (typeof expectedOption.validate === 'function') { try { expectedOption.validate(optValue) } catch (e: unknown) { throw new Error(`Type check for option "${name.toString()}" failed: ${(e as Error).message}`) } } if (typeof optValue === 'string' && expectedOption.match && !optValue.match(expectedOption.match)) { throw new Error(`Option "${name.toString()}" doesn't match expected values: ${expectedOption.match}`) } params[name] = options[name] } } for (const [name, option] of Object.entries(options as object) as [keyof T, T[keyof T]][]) { /** * keep keys from source object if desired */ if (keysToKeep.includes(name)) { params[name] = option } } return params }