Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add user agent to args automatically #1026

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading