taiko
Version:
Taiko is a Node.js library for automating Chromium based browsers
199 lines (184 loc) • 5.88 kB
JavaScript
const runFile = require("./runFile");
const fs = require("node:fs");
const Command = require("commander").Command;
const repl = require("../lib/repl/repl");
const { isTaikoRunner } = require("../lib/util");
const devices = require("../lib/data/devices").default;
const NETWORK_TYPES = Object.keys(require("../lib/data/networkConditions"));
const { getExecutablePlugins } = require("../lib/plugins");
const processArgv = process.argv;
let repl_mode = false;
let taiko;
function printVersion() {
try {
const packageJson = require("../package.json");
let hash = "RELEASE";
if (packageJson._resolved?.includes("#")) {
hash = packageJson._resolved.split("#")[1];
}
const taikoVersion = packageJson.version;
const browserVersion = packageJson.taiko.browser.version;
return `Version: ${taikoVersion} (Chromium: ${browserVersion}) ${hash}`;
} catch (error) {
return "Could not find the package.json file to read version information";
}
}
async function exitOnUnhandledFailures(e) {
if (!repl_mode) {
console.error(e);
if (taiko && (await taiko.client())) {
try {
await taiko.closeBrowser();
} catch (error) {
console.error(error);
process.exit(1);
}
}
process.exit(1);
}
}
process.on("unhandledRejection", exitOnUnhandledFailures);
process.on("uncaughtException", exitOnUnhandledFailures);
function validate(file) {
if (!file.endsWith(".js")) {
console.log("Invalid file extension. Only javascript files are accepted.");
process.exit(1);
}
if (!fs.existsSync(file)) {
console.log("File does not exist.");
process.exit(1);
}
}
function setupEmulateDevice(device) {
if (Object.prototype.hasOwnProperty.call(devices, device)) {
process.env.TAIKO_EMULATE_DEVICE = device;
} else {
console.log(`Invalid value ${device} for --emulate-device`);
console.log(`Available devices: ${Object.keys(devices).join(", ")}`);
process.exit(1);
}
}
function setPluginNameInEnv(pluginName) {
process.env.TAIKO_PLUGIN = pluginName;
return pluginName;
}
function setEmulatedNetwork(networkType) {
if (!NETWORK_TYPES.includes(networkType)) {
console.log(`Invalid value ${networkType} for --emulate-network`);
console.log(`Available options: ${NETWORK_TYPES.join(", ")}`);
process.exit(1);
}
process.env.TAIKO_EMULATE_NETWORK = networkType;
}
function setDisableLogout() {
process.env.TAIKO_ENABLE_ACTION_OUTPUT = "false";
}
function seekingForHelp(args) {
return ["-h", "--help"].some((arg) => args.includes(arg));
}
function registerSubcommandForPlugins(program, plugins) {
for (const pluginName of Object.keys(plugins)) {
program
.command(`${pluginName} [options...]`)
.allowUnknownOption(true)
.action((options, cmd) => {
const plugin = require(plugins[cmd.name()]);
plugin.exec(options);
});
}
}
function isCLICommand() {
return require.main === module;
}
if (isTaikoRunner(processArgv[1])) {
process.env.TAIKO_ENABLE_ACTION_OUTPUT =
process.env.TAIKO_ENABLE_ACTION_OUTPUT || true;
const plugins = getExecutablePlugins();
if (
isCLICommand() &&
!(
seekingForHelp(processArgv) ||
Object.prototype.hasOwnProperty.call(plugins, processArgv[2])
)
) {
// append taiko sub-command as if the user has executed <taiko taiko script.js or just taiko taiko>
processArgv.splice(2, 0, "taiko");
}
const program = new Command("taiko");
program
.arguments("<cmd> [fileName]")
.version(printVersion(), "-v, --version")
.usage(
`[options]
taiko <file> [options]`,
)
.option(
"-o, --observe",
`enables headful mode and runs script with 3000ms delay by default.
\t\t\tpass --wait-time option to override the default 3000ms\n`,
)
.option(
"-l, --load",
"run the given file and start the repl to record further steps.\n",
)
.option(
"-w, --wait-time <time in ms>",
"runs script with provided delay\n",
Number.parseInt,
)
.option(
"--emulate-device <device>",
"Allows to simulate device viewport. Visit https://docs.taiko.dev/devices for all the available devices\n",
setupEmulateDevice,
)
.option(
"--emulate-network <networkType>",
`Allow to simulate network. Available options are ${NETWORK_TYPES.join(", ")}`,
setEmulatedNetwork,
)
.option(
"--plugin <plugin1,plugin2...>",
"Load the taiko plugin.",
setPluginNameInEnv,
)
.option("--no-log", "Disable log output of taiko", setDisableLogout)
.action((_, fileName, cmd) => {
taiko = require("../lib/taiko");
if (fileName) {
validate(fileName);
const observe = Boolean(cmd.observe || cmd.slowMod);
if (cmd.load) {
runFile(taiko, fileName, true, cmd.waitTime, (fileName) => {
return new Promise((resolve) => {
repl_mode = true;
repl.initialize(taiko, fileName, true).then((r) => {
const listeners = r.listeners("exit");
r.removeAllListeners("exit");
r.on("exit", () => {
for (const l of listeners) {
r.addListener("exit", l);
}
resolve();
});
});
});
});
} else {
runFile(taiko, fileName, observe, cmd.waitTime);
}
} else {
repl_mode = true;
repl.initialize(taiko);
}
});
registerSubcommandForPlugins(program, plugins);
program.unknownOption = (option) => {
console.error("error: unknown option `%s", option);
program.outputHelp();
process.exit(1);
};
program.parse(processArgv);
} else {
module.exports = require("../lib/taiko");
}