Skip to content

Commit

Permalink
fix: add user agent to args automatically (#1026)
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowusr authored and KuznetsovRoman committed Nov 13, 2024
1 parent 049ea92 commit c43323b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 106 deletions.
20 changes: 11 additions & 9 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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, {
Expand Down
14 changes: 8 additions & 6 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,18 +275,20 @@ export type ConfigInput = Partial<CommonConfig> & {
prepareEnvironment?: () => void | null;
};

export interface ConfigParsed extends CommonConfig {
browsers: Record<string, BrowserConfig>;
plugins: Record<string, Record<string, unknown>>;
sets: Record<string, SetsConfigParsed>;
prepareEnvironment?: () => void | null;
}

export interface RuntimeConfig {
extend: (data: unknown) => this;
[key: string]: unknown;
}

declare module "." {
export interface Config extends CommonConfig {
browsers: Record<string, BrowserConfig>;
plugins: Record<string, Record<string, unknown>>;
sets: Record<string, SetsConfigParsed>;
prepareEnvironment?: () => void | null;
}
export interface Config extends ConfigParsed {}
}

declare module "./browser-config" {
Expand Down
86 changes: 0 additions & 86 deletions src/config/utils.js

This file was deleted.

101 changes: 101 additions & 0 deletions src/config/utils.ts
Original file line number Diff line number Diff line change
@@ -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 = <T = unknown>(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;
};
15 changes: 10 additions & 5 deletions test/src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand All @@ -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', () => {
Expand Down

0 comments on commit c43323b

Please sign in to comment.