From c2af168042218deecc33bb1b2cd4cbb16502ee49 Mon Sep 17 00:00:00 2001 From: shadowusr <58862284+shadowusr@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:17:28 +0300 Subject: [PATCH] fix: add user agent to args automatically (#1026) --- src/config/index.ts | 20 ++++---- src/config/types.ts | 14 +++--- src/config/utils.js | 86 --------------------------------- src/config/utils.ts | 101 +++++++++++++++++++++++++++++++++++++++ test/src/config/index.js | 15 ++++-- 5 files changed, 130 insertions(+), 106 deletions(-) delete mode 100644 src/config/utils.js create mode 100644 src/config/utils.ts diff --git a/src/config/index.ts b/src/config/index.ts index 3edd1fdcc..50734394e 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -4,7 +4,8 @@ import defaults from "./defaults"; import { BrowserConfig } from "./browser-config"; import parseOptions from "./options"; import logger from "../utils/logger"; -import { ConfigInput } from "./types"; +import { ConfigInput, ConfigParsed } from "./types"; +import { addUserAgentToArgs } from "./utils"; export class Config { configPath!: string; @@ -59,14 +60,15 @@ export class Config { options.prepareEnvironment(); } - _.extend( - this, - parseOptions({ - options, - env: process.env, - argv: process.argv, - }), - ); + const parsedOptions = parseOptions({ + options, + env: process.env, + argv: process.argv, + }) as ConfigParsed; + + addUserAgentToArgs(parsedOptions); + + _.extend(this, parsedOptions); this.browsers = _.mapValues(this.browsers, (browser, id) => { const browserOptions = _.extend({}, browser, { diff --git a/src/config/types.ts b/src/config/types.ts index f3a265968..248ef1173 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -275,18 +275,20 @@ export type ConfigInput = Partial & { prepareEnvironment?: () => void | null; }; +export interface ConfigParsed extends CommonConfig { + browsers: Record; + plugins: Record>; + sets: Record; + prepareEnvironment?: () => void | null; +} + export interface RuntimeConfig { extend: (data: unknown) => this; [key: string]: unknown; } declare module "." { - export interface Config extends CommonConfig { - browsers: Record; - plugins: Record>; - sets: Record; - prepareEnvironment?: () => void | null; - } + export interface Config extends ConfigParsed {} } declare module "./browser-config" { diff --git a/src/config/utils.js b/src/config/utils.js deleted file mode 100644 index ac70f9a74..000000000 --- a/src/config/utils.js +++ /dev/null @@ -1,86 +0,0 @@ -"use strict"; - -const path = require("path"); -const _ = require("lodash"); - -const is = (type, name) => { - return value => { - if (typeof value !== type) { - throw new Error(`"${name}" must be a ${type}`); - } - }; -}; - -exports.is = is; - -exports.assertNonNegativeNumber = (value, name) => { - is("number", name)(value); - if (value < 0) { - throw new Error(`"${name}" must be non-negative`); - } -}; - -exports.assertOptionalObject = (value, name) => { - if (!_.isNull(value) && !_.isPlainObject(value)) { - throw new Error(`"${name}" must be an object`); - } -}; - -exports.assertOptionalArray = (value, name) => { - if (!_.isArray(value)) { - throw new Error(`"${name}" must be an array`); - } -}; - -exports.assertNonNegativeInteger = (value, name) => { - if (!Number.isInteger(value) || value < 0) { - throw new Error(`"${name}" must be a non-negative integer`); - } -}; - -exports.assertEnum = (enumValues, value, name) => { - is("string", name)(value); - - if (!_.includes(enumValues, value)) { - throw new Error(`"${name}" must be one of: ${enumValues.join(", ")}`); - } -}; - -const isPositiveInteger = value => Number.isInteger(value) && value > 0; - -exports.assertPositiveInteger = (value, name) => { - if (!isPositiveInteger(value)) { - throw new Error(`"${name}" must be a positive integer`); - } -}; - -exports.assertPositiveIntegerOrInfinity = (value, name) => { - if (!isPositiveInteger(value) && value !== Infinity) { - throw new Error(`"${name}" must be a positive integer or Infinity`); - } -}; - -exports.parseBoolean = exports.parseBoolean = (value, name) => { - switch (value.toLowerCase()) { - case "1": - case "yes": - case "true": - return true; - case "0": - case "no": - case "false": - return false; - default: - throw new Error(`Unexpected value for boolean option "${name}"`); - } -}; - -exports.parsePrimitive = exports.parsePrimitive = str => { - try { - return JSON.parse(str); - } catch (error) { - throw new Error("a value must be a primitive type"); - } -}; - -exports.resolveWithProjectDir = value => (value ? path.resolve(process.cwd(), value) : value); diff --git a/src/config/utils.ts b/src/config/utils.ts new file mode 100644 index 000000000..e0f455d85 --- /dev/null +++ b/src/config/utils.ts @@ -0,0 +1,101 @@ +import _ from "lodash"; +import { ConfigParsed } from "./types"; + +type ValueType = "string" | "number" | "boolean" | "object" | "undefined" | "function"; + +export const is = (type: ValueType, name: string) => { + return (value: unknown): void => { + if (typeof value !== type) { + throw new Error(`"${name}" must be a ${type}`); + } + }; +}; + +export const assertNonNegativeNumber = (value: number, name: string): void => { + is("number", name)(value); + if (value < 0) { + throw new Error(`"${name}" must be non-negative`); + } +}; + +export const assertOptionalObject = (value: unknown, name: string): void => { + if (!_.isNull(value) && !_.isPlainObject(value)) { + throw new Error(`"${name}" must be an object`); + } +}; + +export const assertOptionalArray = (value: unknown, name: string): void => { + if (!_.isArray(value)) { + throw new Error(`"${name}" must be an array`); + } +}; + +export const assertNonNegativeInteger = (value: number, name: string): void => { + if (!Number.isInteger(value) || value < 0) { + throw new Error(`"${name}" must be a non-negative integer`); + } +}; + +export const assertEnum = (enumValues: string[], value: string, name: string): void => { + is("string", name)(value); + + if (!_.includes(enumValues, value)) { + throw new Error(`"${name}" must be one of: ${enumValues.join(", ")}`); + } +}; + +const isPositiveInteger = (value: number): boolean => Number.isInteger(value) && value > 0; + +export const assertPositiveInteger = (value: number, name: string): void => { + if (!isPositiveInteger(value)) { + throw new Error(`"${name}" must be a positive integer`); + } +}; + +export const assertPositiveIntegerOrInfinity = (value: number, name: string): void => { + if (!isPositiveInteger(value) && value !== Infinity) { + throw new Error(`"${name}" must be a positive integer or Infinity`); + } +}; + +export const parseBoolean = (value: string, name: string): boolean => { + switch (value.toLowerCase()) { + case "1": + case "yes": + case "true": + return true; + case "0": + case "no": + case "false": + return false; + default: + throw new Error(`Unexpected value for boolean option "${name}"`); + } +}; + +export const parsePrimitive = (str: string): T => { + try { + return JSON.parse(str); + } catch (error) { + throw new Error("a value must be a primitive type"); + } +}; + +export const addUserAgentToArgs = (config: ConfigParsed): ConfigParsed => { + for (const browserKey in config.browsers) { + const browserConfig = config.browsers[browserKey]; + const chromeOptions = browserConfig.desiredCapabilities?.["goog:chromeOptions"]; + + if (chromeOptions?.mobileEmulation?.userAgent) { + const userAgent = chromeOptions.mobileEmulation.userAgent; + chromeOptions.args = chromeOptions.args || []; + + const userAgentArg = `user-agent=${userAgent}`; + if (!chromeOptions.args.find(arg => arg.startsWith("user-agent="))) { + chromeOptions.args.push(userAgentArg); + } + } + } + + return config; +}; diff --git a/test/src/config/index.js b/test/src/config/index.js index 55a39d652..3f0951850 100644 --- a/test/src/config/index.js +++ b/test/src/config/index.js @@ -32,25 +32,28 @@ describe("config", () => { describe("constructor", () => { it("should parse options", () => { - initConfig(); + initConfig({ configParserReturns: {} }); assert.calledOnce(parseOptions); }); it("should parse config from file", () => { - initConfig({ requireConfigReturns: "some-options" }); + initConfig({ configParserReturns: {}, requireConfigReturns: "some-options" }); assert.calledWithMatch(parseOptions, { options: "some-options", env: process.env, argv: process.argv }); }); it("should support default export", () => { - initConfig({ requireConfigReturns: { __esModule: true, default: { foo: "bar" } } }); + initConfig({ + configParserReturns: {}, + requireConfigReturns: { __esModule: true, default: { foo: "bar" } }, + }); assert.calledWithMatch(parseOptions, { options: { foo: "bar" }, env: process.env, argv: process.argv }); }); it("should parse config from object", () => { - initConfig({ config: { someOption: "some-value" } }); + initConfig({ configParserReturns: {}, config: { someOption: "some-value" } }); assert.calledWithMatch(parseOptions, { options: { someOption: "some-value" }, @@ -64,7 +67,9 @@ describe("config", () => { }); it("should extend config with a config path", () => { - assert.include(initConfig({ config: "config-path" }), { configPath: "config-path" }); + assert.include(initConfig({ configParserReturns: {}, config: "config-path" }), { + configPath: "config-path", + }); }); it('should wrap browser config with "BrowserConfig" instance', () => {