Implements Phases 1-8 of the TFTSR implementation plan. Rust backend (Tauri 2.x, src-tauri/): - Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama - PII detection engine: 11 regex patterns with overlap resolution - SQLCipher AES-256 encrypted database with 10 versioned migrations - 28 Tauri IPC commands for triage, analysis, document, and system ops - Ollama: hardware probe, model recommendations, pull/delete with events - RCA and blameless post-mortem Markdown document generators - PDF export via printpdf - Audit log: SHA-256 hash of every external data send - Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2) Frontend (React 18 + TypeScript + Vite, src/): - 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings - 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives - 3 Zustand stores: session, settings (persisted), history - Type-safe tauriCommands.ts matching Rust backend types exactly - 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs) DevOps: - .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push - .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload Verified: - cargo check: zero errors - tsc --noEmit: zero errors - vitest run: 13/13 unit tests passing Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
852 lines
32 KiB
JavaScript
852 lines
32 KiB
JavaScript
// src/utils.ts
|
|
import url from "node:url";
|
|
import path from "node:path";
|
|
import util, { promisify } from "node:util";
|
|
import fs from "node:fs/promises";
|
|
import { execSync } from "node:child_process";
|
|
import readDir from "recursive-readdir";
|
|
import { $ } from "execa";
|
|
import { readPackageUp } from "read-pkg-up";
|
|
import inquirer from "inquirer";
|
|
import ejs from "ejs";
|
|
import spawn from "cross-spawn";
|
|
import chalk from "chalk";
|
|
|
|
// src/install.ts
|
|
import { execa } from "execa";
|
|
|
|
// src/utils.ts
|
|
var NPM_COMMAND = /^win/.test(process.platform) ? "npm.cmd" : "npm";
|
|
var __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
process.on("SIGINT", () => printAndExit(void 0, "SIGINT"));
|
|
var TEMPLATE_ROOT_DIR = process.env.WDIO_UNIT_TESTS ? path.join(__dirname, "templates", "exampleFiles") : path.join(__dirname, "..", "templates", "exampleFiles");
|
|
function printAndExit(error, signal) {
|
|
if (signal === "SIGINT") {
|
|
console.log("\n\nGoodbye \u{1F44B}");
|
|
} else {
|
|
console.log(`
|
|
|
|
\u26A0\uFE0F Ups, something went wrong${error ? `: ${error}` : ""}!`);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
function convertPackageHashToObject(pkg, hash = "$--$") {
|
|
const [p, short, purpose] = pkg.split(hash);
|
|
return { package: p, short, purpose };
|
|
}
|
|
async function getProjectProps(cwd = process.cwd()) {
|
|
try {
|
|
const { packageJson, path: packageJsonPath } = await readPackageUp({ cwd }) || {};
|
|
if (!packageJson || !packageJsonPath) {
|
|
return void 0;
|
|
}
|
|
return {
|
|
esmSupported: packageJson.type === "module" || typeof packageJson.module === "string",
|
|
packageJson,
|
|
path: path.dirname(packageJsonPath)
|
|
};
|
|
} catch {
|
|
return void 0;
|
|
}
|
|
}
|
|
async function getProjectRoot(parsedAnswers) {
|
|
if (parsedAnswers?.createPackageJSON && parsedAnswers.projectRoot) {
|
|
return parsedAnswers.projectRoot;
|
|
}
|
|
const root = (await getProjectProps())?.path;
|
|
if (!root) {
|
|
throw new Error("Could not find project root directory with a package.json");
|
|
}
|
|
return !parsedAnswers || parsedAnswers.projectRootCorrect ? root : parsedAnswers.projectRoot || process.cwd();
|
|
}
|
|
var renderFile = promisify(ejs.renderFile);
|
|
function detectPackageManager() {
|
|
if (!process.env.npm_config_user_agent) {
|
|
return "npm";
|
|
}
|
|
const detectedPM = process.env.npm_config_user_agent.split("/")[0].toLowerCase();
|
|
const matchedPM = SUPPORTED_PACKAGE_MANAGERS.find((pm) => pm.toLowerCase() === detectedPM);
|
|
return matchedPM || "npm";
|
|
}
|
|
async function getDefaultFiles(answers, pattern) {
|
|
const rootDir = await getProjectRoot(answers);
|
|
const presetPackage = convertPackageHashToObject(answers.preset || "");
|
|
const isJSX = TSX_BASED_FRAMEWORKS.includes(presetPackage.short || "");
|
|
const val = pattern.endsWith(".feature") ? path.join(rootDir, pattern) : answers?.isUsingTypeScript ? `${path.join(rootDir, pattern)}.ts${isJSX ? "x" : ""}` : `${path.join(rootDir, pattern)}.js${isJSX ? "x" : ""}`;
|
|
return val;
|
|
}
|
|
var TSX_BASED_FRAMEWORKS = ["react", "preact", "solid", "stencil"];
|
|
async function detectCompiler(answers) {
|
|
if (answers.createPackageJSON) {
|
|
return false;
|
|
}
|
|
const root = await getProjectRoot(answers);
|
|
const hasRootTSConfig = await fs.access(path.resolve(root, "tsconfig.json")).then(() => true, () => false);
|
|
return hasRootTSConfig;
|
|
}
|
|
|
|
// package.json
|
|
var package_default = {
|
|
name: "create-wdio",
|
|
version: "9.21.0",
|
|
description: "Install and setup a WebdriverIO project with all its dependencies in a single run",
|
|
author: "WebdriverIO Team <mail@webdriver.io>",
|
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/create-wdio",
|
|
license: "MIT",
|
|
type: "module",
|
|
exports: {
|
|
".": {
|
|
types: "./build/index.d.ts",
|
|
import: "./build/index.js",
|
|
importSource: "./src/index.ts"
|
|
},
|
|
"./config/cli": {
|
|
types: "./build/config/cli.d.ts",
|
|
import: "./build/config/cli.js",
|
|
importSource: "./src/config/cli.ts"
|
|
},
|
|
"./install/cli": {
|
|
types: "./build/install/cli.d.ts",
|
|
import: "./build/install/cli.js",
|
|
importSource: "./src/install/cli.ts"
|
|
},
|
|
"./utils": {
|
|
types: "./build/utils/index.d.ts",
|
|
import: "./build/utils/index.js",
|
|
importSource: "./src/utils/index.ts"
|
|
}
|
|
},
|
|
types: "./build/index.d.ts",
|
|
typeScriptVersion: "3.8.3",
|
|
engines: {
|
|
node: ">=12.0.0"
|
|
},
|
|
repository: {
|
|
type: "git",
|
|
url: "git+https://github.com/webdriverio/webdriverio.git",
|
|
directory: "packages/create-wdio"
|
|
},
|
|
keywords: [
|
|
"webdriverio",
|
|
"create-wdio",
|
|
"wdio",
|
|
"installer",
|
|
"e2e"
|
|
],
|
|
bugs: {
|
|
url: "https://github.com/webdriverio/webdriverio/issues"
|
|
},
|
|
bin: {
|
|
"create-wdio": "./bin/wdio.js"
|
|
},
|
|
dependencies: {
|
|
chalk: "^5.3.0",
|
|
commander: "^14.0.0",
|
|
"cross-spawn": "^7.0.3",
|
|
ejs: "^3.1.10",
|
|
execa: "^9.6.0",
|
|
"import-meta-resolve": "^4.1.0",
|
|
inquirer: "^12.7.0",
|
|
"normalize-package-data": "^7.0.0",
|
|
"read-pkg-up": "^10.1.0",
|
|
"recursive-readdir": "^2.2.3",
|
|
semver: "^7.6.3",
|
|
"type-fest": "^4.41.0",
|
|
yargs: "^17.7.2"
|
|
},
|
|
devDependencies: {
|
|
"@types/cross-spawn": "^6.0.6",
|
|
"@types/ejs": "^3.1.5",
|
|
"@types/fs-extra": "^11.0.4",
|
|
"@types/normalize-package-data": "^2.4.4",
|
|
"@types/semver": "^7.5.8",
|
|
"@types/yargs": "^17.0.33"
|
|
},
|
|
publishConfig: {
|
|
access: "public"
|
|
}
|
|
};
|
|
|
|
// src/constants.ts
|
|
import path2 from "node:path";
|
|
import fs2 from "node:fs";
|
|
import chalk2 from "chalk";
|
|
var colorItBold = chalk2.bold.rgb(234, 89, 6);
|
|
var colorIt = chalk2.rgb(234, 89, 6);
|
|
var PROGRAM_TITLE = `
|
|
${colorItBold("Webdriver.IO")}
|
|
${colorIt("Next-gen browser and mobile automation")}
|
|
${colorIt("test framework for Node.js")}
|
|
`;
|
|
var UNSUPPORTED_NODE_VERSION = `\u26A0\uFE0F Unsupported Node.js Version Error \u26A0\uFE0F
|
|
You are using Node.js ${process.version} which is too old to be used with WebdriverIO.
|
|
Please update to Node.js v20 to continue.
|
|
`;
|
|
var INSTALL_COMMAND = {
|
|
npm: "install",
|
|
pnpm: "add",
|
|
yarn: "add",
|
|
bun: "install"
|
|
};
|
|
var SUPPORTED_PACKAGE_MANAGERS = Object.keys(INSTALL_COMMAND);
|
|
var CLI_EPILOGUE = `Documentation: https://webdriver.io
|
|
@wdio/cli (v${package_default.version})`;
|
|
var SUPPORTED_PACKAGES = {
|
|
runner: [
|
|
{ name: "E2E Testing - of Web or Mobile Applications", value: "@wdio/local-runner$--$local$--$e2e" },
|
|
{ name: "Component or Unit Testing - in the browser\n > https://webdriver.io/docs/component-testing", value: "@wdio/browser-runner$--$browser$--$component" },
|
|
{ name: "Desktop Testing - of Electron Applications\n > https://webdriver.io/docs/desktop-testing/electron", value: "@wdio/local-runner$--$local$--$electron" },
|
|
{ name: "Desktop Testing - of MacOS Applications\n > https://webdriver.io/docs/desktop-testing/macos", value: "@wdio/local-runner$--$local$--$macos" },
|
|
{ name: "VS Code Extension Testing\n > https://webdriver.io/docs/vscode-extension-testing", value: "@wdio/local-runner$--$local$--$vscode" },
|
|
{ name: "Roku Testing - of OTT apps running on RokuOS\n > https://webdriver.io/docs/wdio-roku-service", value: "@wdio/local-runner$--$local$--$roku" }
|
|
],
|
|
framework: [
|
|
{ name: "Mocha (https://mochajs.org/)", value: "@wdio/mocha-framework$--$mocha" },
|
|
{ name: "Mocha with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$mocha" },
|
|
{ name: "Jasmine (https://jasmine.github.io/)", value: "@wdio/jasmine-framework$--$jasmine" },
|
|
{ name: "Jasmine with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$jasmine" },
|
|
{ name: "Cucumber (https://cucumber.io/)", value: "@wdio/cucumber-framework$--$cucumber" },
|
|
{ name: "Cucumber with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$cucumber" }
|
|
],
|
|
reporter: [
|
|
{ name: "spec", value: "@wdio/spec-reporter$--$spec", checked: true },
|
|
{ name: "dot", value: "@wdio/dot-reporter$--$dot" },
|
|
{ name: "junit", value: "@wdio/junit-reporter$--$junit" },
|
|
{ name: "allure", value: "@wdio/allure-reporter$--$allure" },
|
|
{ name: "sumologic", value: "@wdio/sumologic-reporter$--$sumologic" },
|
|
{ name: "concise", value: "@wdio/concise-reporter$--$concise" },
|
|
{ name: "json", value: "@wdio/json-reporter$--$json" },
|
|
// external
|
|
{ name: "reportportal", value: "wdio-reportportal-reporter$--$reportportal" },
|
|
{ name: "video", value: "wdio-video-reporter$--$video" },
|
|
{ name: "cucumber-json", value: "wdio-cucumberjs-json-reporter$--$cucumberjs-json" },
|
|
{ name: "mochawesome", value: "wdio-mochawesome-reporter$--$mochawesome" },
|
|
{ name: "timeline", value: "wdio-timeline-reporter$--$timeline" },
|
|
{ name: "html-nice", value: "wdio-html-nice-reporter$--$html-nice" },
|
|
{ name: "slack", value: "@moroo/wdio-slack-reporter$--$slack" },
|
|
{ name: "teamcity", value: "wdio-teamcity-reporter$--$teamcity" },
|
|
{ name: "delta", value: "@delta-reporter/wdio-delta-reporter-service$--$delta" },
|
|
{ name: "testrail", value: "@wdio/testrail-reporter$--$testrail" },
|
|
{ name: "light", value: "wdio-light-reporter$--$light" },
|
|
{ name: "wdio-json-html-reporter", value: "wdio-json-html-reporter$--$jsonhtml" }
|
|
],
|
|
plugin: [
|
|
{ name: "wait-for: utilities that provide functionalities to wait for certain conditions till a defined task is complete.\n > https://www.npmjs.com/package/wdio-wait-for", value: "wdio-wait-for$--$wait-for" },
|
|
{ name: "angular-component-harnesses: support for Angular component test harnesses\n > https://www.npmjs.com/package/@badisi/wdio-harness", value: "@badisi/wdio-harness$--$harness" },
|
|
{ name: "Testing Library: utilities that encourage good testing practices laid down by dom-testing-library.\n > https://testing-library.com/docs/webdriverio-testing-library/intro", value: "@testing-library/webdriverio$--$testing-library" }
|
|
],
|
|
service: [
|
|
// internal or community driver services
|
|
{ name: "visual", value: "@wdio/visual-service$--$visual" },
|
|
{ name: "vite", value: "wdio-vite-service$--$vite" },
|
|
{ name: "nuxt", value: "wdio-nuxt-service$--$nuxt" },
|
|
{ name: "firefox-profile", value: "@wdio/firefox-profile-service$--$firefox-profile" },
|
|
{ name: "gmail", value: "wdio-gmail-service$--$gmail" },
|
|
{ name: "sauce", value: "@wdio/sauce-service$--$sauce" },
|
|
{ name: "testingbot", value: "@wdio/testingbot-service$--$testingbot" },
|
|
{ name: "browserstack", value: "@wdio/browserstack-service$--$browserstack" },
|
|
{ name: "lighthouse", value: "@wdio/lighthouse-service$--$lighthouse" },
|
|
{ name: "vscode", value: "wdio-vscode-service$--$vscode" },
|
|
{ name: "electron", value: "wdio-electron-service$--$electron" },
|
|
{ name: "appium", value: "@wdio/appium-service$--$appium" },
|
|
// external
|
|
{ name: "camera", value: "wdio-camera-service$--$camera" },
|
|
{ name: "eslinter-service", value: "wdio-eslinter-service$--$eslinter" },
|
|
{ name: "lambdatest", value: "wdio-lambdatest-service$--$lambdatest" },
|
|
{ name: "tvlabs", value: "@tvlabs/wdio-service$--$tvlabs" },
|
|
{ name: "zafira-listener", value: "wdio-zafira-listener-service$--$zafira-listener" },
|
|
{ name: "reportportal", value: "wdio-reportportal-service$--$reportportal" },
|
|
{ name: "docker", value: "wdio-docker-service$--$docker" },
|
|
{ name: "ui5", value: "wdio-ui5-service$--$ui5" },
|
|
{ name: "wiremock", value: "wdio-wiremock-service$--$wiremock" },
|
|
{ name: "ng-apimock", value: "wdio-ng-apimock-service$--$ng-apimock" },
|
|
{ name: "slack", value: "wdio-slack-service$--$slack" },
|
|
{ name: "cucumber-viewport-logger", value: "wdio-cucumber-viewport-logger-service$--$cucumber-viewport-logger" },
|
|
{ name: "intercept", value: "wdio-intercept-service$--$intercept" },
|
|
{ name: "docker", value: "wdio-docker-service$--$docker" },
|
|
{ name: "novus-visual-regression", value: "wdio-novus-visual-regression-service$--$novus-visual-regression" },
|
|
{ name: "rerun", value: "wdio-rerun-service$--$rerun" },
|
|
{ name: "winappdriver", value: "wdio-winappdriver-service$--$winappdriver" },
|
|
{ name: "ywinappdriver", value: "wdio-ywinappdriver-service$--$ywinappdriver" },
|
|
{ name: "performancetotal", value: "wdio-performancetotal-service$--$performancetotal" },
|
|
{ name: "cleanuptotal", value: "wdio-cleanuptotal-service$--$cleanuptotal" },
|
|
{ name: "aws-device-farm", value: "wdio-aws-device-farm-service$--$aws-device-farm" },
|
|
{ name: "ms-teams", value: "wdio-ms-teams-service$--$ms-teams" },
|
|
{ name: "tesults", value: "wdio-tesults-service$--$tesults" },
|
|
{ name: "azure-devops", value: "@gmangiapelo/wdio-azure-devops-service$--$azure-devops" },
|
|
{ name: "google-Chat", value: "wdio-google-chat-service$--$google-chat" },
|
|
{ name: "qmate-service", value: "@sap_oss/wdio-qmate-service$--$qmate-service" },
|
|
{ name: "robonut", value: "wdio-robonut-service$--$robonut" },
|
|
{ name: "qunit", value: "wdio-qunit-service$--$qunit" },
|
|
{ name: "roku", value: "wdio-roku-service$--$roku" },
|
|
{ name: "obsidian", value: "wdio-obsidian-service$--$obsidian" }
|
|
]
|
|
};
|
|
var isNuxtProject = [
|
|
path2.join(process.cwd(), "nuxt.config.js"),
|
|
path2.join(process.cwd(), "nuxt.config.ts"),
|
|
path2.join(process.cwd(), "nuxt.config.mjs"),
|
|
path2.join(process.cwd(), "nuxt.config.mts")
|
|
].map((p) => {
|
|
try {
|
|
fs2.accessSync(p);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}).some(Boolean);
|
|
function usesSerenity(answers) {
|
|
return answers.framework.includes("serenity-js");
|
|
}
|
|
var ProtocolOptions = /* @__PURE__ */ ((ProtocolOptions2) => {
|
|
ProtocolOptions2["HTTPS"] = "https";
|
|
ProtocolOptions2["HTTP"] = "http";
|
|
return ProtocolOptions2;
|
|
})(ProtocolOptions || {});
|
|
var BackendChoice = /* @__PURE__ */ ((BackendChoice2) => {
|
|
BackendChoice2["Local"] = "On my local machine";
|
|
BackendChoice2["Experitest"] = "In the cloud using Experitest";
|
|
BackendChoice2["Saucelabs"] = "In the cloud using Sauce Labs";
|
|
BackendChoice2["Browserstack"] = "In the cloud using BrowserStack";
|
|
BackendChoice2["OtherVendors"] = "In the cloud using Testingbot or LambdaTest or a different service";
|
|
BackendChoice2["Grid"] = "I have my own Selenium cloud";
|
|
return BackendChoice2;
|
|
})(BackendChoice || {});
|
|
var RegionOptions = /* @__PURE__ */ ((RegionOptions2) => {
|
|
RegionOptions2["US"] = "us";
|
|
RegionOptions2["EU"] = "eu";
|
|
return RegionOptions2;
|
|
})(RegionOptions || {});
|
|
var ElectronBuildToolChoice = /* @__PURE__ */ ((ElectronBuildToolChoice2) => {
|
|
ElectronBuildToolChoice2["ElectronForge"] = "Electron Forge (https://www.electronforge.io/)";
|
|
ElectronBuildToolChoice2["ElectronBuilder"] = "electron-builder (https://www.electron.build/)";
|
|
ElectronBuildToolChoice2["SomethingElse"] = "Something else";
|
|
return ElectronBuildToolChoice2;
|
|
})(ElectronBuildToolChoice || {});
|
|
var SUPPORTED_BROWSER_RUNNER_PRESETS = [
|
|
{ name: "Lit (https://lit.dev/)", value: "$--$" },
|
|
{ name: "Vue.js (https://vuejs.org/)", value: "@vitejs/plugin-vue$--$vue" },
|
|
{ name: "Svelte (https://svelte.dev/)", value: "@sveltejs/vite-plugin-svelte$--$svelte" },
|
|
{ name: "SolidJS (https://www.solidjs.com/)", value: "vite-plugin-solid$--$solid" },
|
|
{ name: "StencilJS (https://stenciljs.com/)", value: "$--$stencil" },
|
|
{ name: "React (https://reactjs.org/)", value: "@vitejs/plugin-react$--$react" },
|
|
{ name: "Preact (https://preactjs.com/)", value: "@preact/preset-vite$--$preact" },
|
|
{ name: "Other", value: null }
|
|
];
|
|
function isBrowserRunner(answers) {
|
|
return answers.runner === SUPPORTED_PACKAGES.runner[1].value;
|
|
}
|
|
function getTestingPurpose(answers) {
|
|
return convertPackageHashToObject(answers.runner).purpose;
|
|
}
|
|
var TESTING_LIBRARY_PACKAGES = {
|
|
react: "@testing-library/react",
|
|
preact: "@testing-library/preact",
|
|
vue: "@testing-library/vue",
|
|
svelte: "@testing-library/svelte",
|
|
solid: "solid-testing-library"
|
|
};
|
|
var E2E_ENVIRONMENTS = [
|
|
{ name: "Web - web applications in the browser", value: "web" },
|
|
{ name: "Mobile - native, hybrid and mobile web apps, on Android or iOS", value: "mobile" }
|
|
];
|
|
var MOBILE_ENVIRONMENTS = [
|
|
{ name: "Android - native, hybrid and mobile web apps, tested on emulators and real devices\n > using UiAutomator2 (https://www.npmjs.com/package/appium-uiautomator2-driver)", value: "android" },
|
|
{ name: "iOS - applications on iOS, iPadOS, and tvOS\n > using XCTest (https://appium.github.io/appium-xcuitest-driver)", value: "ios" }
|
|
];
|
|
var BROWSER_ENVIRONMENTS = [
|
|
{ name: "Chrome", value: "chrome", checked: true },
|
|
{ name: "Firefox", value: "firefox" },
|
|
{ name: "Safari", value: "safari" },
|
|
{ name: "Microsoft Edge", value: "MicrosoftEdge" }
|
|
];
|
|
function selectDefaultService(serviceNames) {
|
|
serviceNames = Array.isArray(serviceNames) ? serviceNames : [serviceNames];
|
|
return SUPPORTED_PACKAGES.service.filter(({ name }) => serviceNames.includes(name)).map(({ value }) => value);
|
|
}
|
|
function prioServiceOrderFor(serviceNamesParam) {
|
|
const serviceNames = Array.isArray(serviceNamesParam) ? serviceNamesParam : [serviceNamesParam];
|
|
let services = SUPPORTED_PACKAGES.service;
|
|
for (const serviceName of serviceNames) {
|
|
const index = services.findIndex(({ name }) => name === serviceName);
|
|
services = [services[index], ...services.slice(0, index), ...services.slice(index + 1)];
|
|
}
|
|
return services;
|
|
}
|
|
var QUESTIONNAIRE = [{
|
|
type: "list",
|
|
name: "runner",
|
|
message: "What type of testing would you like to do?",
|
|
choices: SUPPORTED_PACKAGES.runner
|
|
}, {
|
|
type: "list",
|
|
name: "preset",
|
|
message: "Which framework do you use for building components?",
|
|
choices: SUPPORTED_BROWSER_RUNNER_PRESETS,
|
|
// only ask if there are more than 1 runner to pick from
|
|
when: (
|
|
/* istanbul ignore next */
|
|
isBrowserRunner
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "installTestingLibrary",
|
|
message: "Do you like to use Testing Library (https://testing-library.com/) as test utility?",
|
|
default: true,
|
|
// only ask if there are more than 1 runner to pick from
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => isBrowserRunner(answers) && /**
|
|
* Only show if Testing Library has an add-on for framework
|
|
*/
|
|
answers.preset && TESTING_LIBRARY_PACKAGES[convertPackageHashToObject(answers.preset).short]
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "electronBuildTool",
|
|
message: "Which tool are you using to build your Electron app?",
|
|
choices: Object.values(ElectronBuildToolChoice),
|
|
when: (
|
|
/* instanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "electron"
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "electronAppBinaryPath",
|
|
message: "What is the path to the binary of your built Electron app?",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "electron" && answers.electronBuildTool === "Something else" /* SomethingElse */
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "backend",
|
|
message: "Where is your automation backend located?",
|
|
choices: Object.values(BackendChoice),
|
|
when: (
|
|
/* instanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "e2e"
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "e2eEnvironment",
|
|
message: "Which environment you would like to automate?",
|
|
choices: E2E_ENVIRONMENTS,
|
|
default: "web",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "e2e"
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "mobileEnvironment",
|
|
message: "Which mobile environment you'd like to automate?",
|
|
choices: MOBILE_ENVIRONMENTS,
|
|
when: (
|
|
/* instanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "e2e" && answers.e2eEnvironment === "mobile"
|
|
)
|
|
}, {
|
|
type: "checkbox",
|
|
name: "browserEnvironment",
|
|
message: "With which browser should we start?",
|
|
choices: BROWSER_ENVIRONMENTS,
|
|
when: (
|
|
/* instanbul ignore next */
|
|
(answers) => getTestingPurpose(answers) === "e2e" && answers.e2eEnvironment === "web"
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "hostname",
|
|
message: "What is the host address of that cloud service?",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.indexOf("different service") > -1
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "port",
|
|
message: "What is the port on which that service is running?",
|
|
default: "80",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.indexOf("different service") > -1
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "expEnvAccessKey",
|
|
message: "Access key from Experitest Cloud",
|
|
default: "EXPERITEST_ACCESS_KEY",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "expEnvHostname",
|
|
message: "Environment variable for cloud url",
|
|
default: "example.experitest.com",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "expEnvPort",
|
|
message: "Environment variable for port",
|
|
default: "443",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "expEnvProtocol",
|
|
message: "Choose a protocol for environment variable",
|
|
default: "https" /* HTTPS */,
|
|
choices: Object.values(ProtocolOptions),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Experitest" /* Experitest */ && answers.expEnvPort !== "80" && answers.expEnvPort !== "443"
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_user",
|
|
message: "Environment variable for username",
|
|
default: "LT_USERNAME",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.indexOf("LambdaTest") > -1 && (answers.hostname === "lambdatest.com" || answers.hostname?.endsWith(".lambdatest.com"))
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_key",
|
|
message: "Environment variable for access key",
|
|
default: "LT_ACCESS_KEY",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.indexOf("LambdaTest") > -1 && (answers.hostname === "lambdatest.com" || answers.hostname?.endsWith(".lambdatest.com"))
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_user",
|
|
message: "Environment variable for username",
|
|
default: "BROWSERSTACK_USERNAME",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using BrowserStack" /* Browserstack */
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_key",
|
|
message: "Environment variable for access key",
|
|
default: "BROWSERSTACK_ACCESS_KEY",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using BrowserStack" /* Browserstack */
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_user",
|
|
message: "Environment variable for username",
|
|
default: "SAUCE_USERNAME",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "env_key",
|
|
message: "Environment variable for access key",
|
|
default: "SAUCE_ACCESS_KEY",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "region",
|
|
message: "In which region do you want to run your Sauce Labs tests in?",
|
|
choices: Object.values(RegionOptions),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "useSauceConnect",
|
|
message: "Are you testing a local application and need Sauce Connect to be set-up?\nRead more on Sauce Connect at: https://docs.saucelabs.com/secure-connections/#sauce-connect-proxy",
|
|
default: isNuxtProject,
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */ && !isNuxtProject
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "hostname",
|
|
message: "What is the IP or URI to your Selenium standalone or grid server?",
|
|
default: "localhost",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "port",
|
|
message: "What is the port which your Selenium standalone or grid server is running on?",
|
|
default: "4444",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "path",
|
|
message: "What is the path to your browser driver or grid server?",
|
|
default: "/",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
|
|
)
|
|
}, {
|
|
type: "list",
|
|
name: "framework",
|
|
message: "Which framework do you want to use?",
|
|
choices: (
|
|
/* instanbul ignore next */
|
|
(answers) => {
|
|
if (isBrowserRunner(answers)) {
|
|
return SUPPORTED_PACKAGES.framework.slice(0, 1);
|
|
}
|
|
if (getTestingPurpose(answers) === "electron") {
|
|
return SUPPORTED_PACKAGES.framework.filter(
|
|
({ value }) => !value.startsWith("@serenity-js")
|
|
);
|
|
}
|
|
return SUPPORTED_PACKAGES.framework;
|
|
}
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "isUsingTypeScript",
|
|
message: "Do you want to use Typescript to write tests?",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => {
|
|
if (answers.preset?.includes("stencil")) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
),
|
|
default: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.preset?.includes("stencil") || detectCompiler(answers)
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "generateTestFiles",
|
|
message: "Do you want WebdriverIO to autogenerate some test files?",
|
|
default: true,
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => {
|
|
if (["vscode", "electron", "macos"].includes(getTestingPurpose(answers)) && answers.framework.includes("cucumber")) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "specs",
|
|
message: "What should be the location of your spec files?",
|
|
default: (
|
|
/* istanbul ignore next */
|
|
(answers) => {
|
|
const pattern = isBrowserRunner(answers) ? "src/**/*.test" : "test/specs/**/*";
|
|
return getDefaultFiles(answers, pattern);
|
|
}
|
|
),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && /(mocha|jasmine)/.test(answers.framework)
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "specs",
|
|
message: "What should be the location of your feature files?",
|
|
default: (answers) => getDefaultFiles(answers, "features/**/*.feature"),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && answers.framework.includes("cucumber")
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "stepDefinitions",
|
|
message: "What should be the location of your step definitions?",
|
|
default: (answers) => getDefaultFiles(answers, "features/step-definitions/steps"),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && answers.framework.includes("cucumber")
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "usePageObjects",
|
|
message: "Do you want to use page objects (https://martinfowler.com/bliki/PageObject.html)?",
|
|
default: true,
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && /**
|
|
* page objects aren't common for component testing
|
|
*/
|
|
!isBrowserRunner(answers) && /**
|
|
* and also not needed when running VS Code tests since the service comes with
|
|
* its own page object implementation, nor when running Electron or MacOS tests
|
|
*/
|
|
!["vscode", "electron", "macos"].includes(getTestingPurpose(answers)) && /**
|
|
* Serenity/JS generates Lean Page Objects by default, so there's no need to ask about it
|
|
* See https://serenity-js.org/handbook/web-testing/page-objects-pattern/
|
|
*/
|
|
!usesSerenity(answers)
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "pages",
|
|
message: "Where are your page objects located?",
|
|
default: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.framework.match(/(mocha|jasmine)/) ? getDefaultFiles(answers, "test/pageobjects/**/*") : getDefaultFiles(answers, "features/pageobjects/**/*")
|
|
),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && answers.usePageObjects
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "serenityLibPath",
|
|
message: "What should be the location of your Serenity/JS Screenplay Pattern library?",
|
|
default: (
|
|
/* istanbul ignore next */
|
|
async (answers) => {
|
|
const projectRootDir = await getProjectRoot(answers);
|
|
const specsDir = path2.resolve(projectRootDir, path2.dirname(answers.specs || "").replace(/\*\*$/, ""));
|
|
return path2.resolve(specsDir, "..", "serenity");
|
|
}
|
|
),
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.generateTestFiles && usesSerenity(answers)
|
|
)
|
|
}, {
|
|
type: "checkbox",
|
|
name: "reporters",
|
|
message: "Which reporter do you want to use?",
|
|
choices: SUPPORTED_PACKAGES.reporter
|
|
}, {
|
|
type: "checkbox",
|
|
name: "plugins",
|
|
message: "Do you want to add a plugin to your test setup?",
|
|
choices: SUPPORTED_PACKAGES.plugin,
|
|
default: []
|
|
}, {
|
|
type: "confirm",
|
|
name: "includeVisualTesting",
|
|
message: "Would you like to include Visual Testing to your setup? For more information see https://webdriver.io/docs/visual-testing!",
|
|
default: false,
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => {
|
|
return ["e2e", "component"].includes(getTestingPurpose(answers));
|
|
}
|
|
)
|
|
}, {
|
|
type: "checkbox",
|
|
name: "services",
|
|
message: "Do you want to add a service to your test setup?",
|
|
choices: (answers) => {
|
|
const services = [];
|
|
if (answers.backend === "In the cloud using BrowserStack" /* Browserstack */) {
|
|
services.push("browserstack");
|
|
} else if (answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */) {
|
|
services.push("sauce");
|
|
}
|
|
if (answers.e2eEnvironment === "mobile") {
|
|
services.push("appium");
|
|
}
|
|
if (getTestingPurpose(answers) === "e2e" && isNuxtProject) {
|
|
services.push("nuxt");
|
|
}
|
|
if (getTestingPurpose(answers) === "vscode") {
|
|
return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "vscode")];
|
|
} else if (getTestingPurpose(answers) === "electron") {
|
|
return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "electron")];
|
|
} else if (getTestingPurpose(answers) === "macos") {
|
|
return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "appium")];
|
|
} else if (getTestingPurpose(answers) === "roku") {
|
|
return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "roku")];
|
|
}
|
|
return prioServiceOrderFor(services);
|
|
},
|
|
default: (answers) => {
|
|
const defaultServices = [];
|
|
if (answers.backend === "In the cloud using BrowserStack" /* Browserstack */) {
|
|
defaultServices.push("browserstack");
|
|
} else if (answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */) {
|
|
defaultServices.push("sauce");
|
|
}
|
|
if (answers.e2eEnvironment === "mobile" || getTestingPurpose(answers) === "macos") {
|
|
defaultServices.push("appium");
|
|
}
|
|
if (getTestingPurpose(answers) === "vscode") {
|
|
defaultServices.push("vscode");
|
|
} else if (getTestingPurpose(answers) === "electron") {
|
|
defaultServices.push("electron");
|
|
} else if (getTestingPurpose(answers) === "roku") {
|
|
defaultServices.push("roku");
|
|
}
|
|
if (isNuxtProject) {
|
|
defaultServices.push("nuxt");
|
|
}
|
|
if (answers.includeVisualTesting) {
|
|
defaultServices.push("visual");
|
|
}
|
|
return selectDefaultService(defaultServices);
|
|
}
|
|
}, {
|
|
type: "input",
|
|
name: "outputDir",
|
|
message: "In which directory should the xunit reports get stored?",
|
|
default: "./",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.reporters.includes("junit")
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "outputDir",
|
|
message: "In which directory should the json reports get stored?",
|
|
default: "./",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.reporters.includes("json")
|
|
)
|
|
}, {
|
|
type: "input",
|
|
name: "outputDir",
|
|
message: "In which directory should the mochawesome json reports get stored?",
|
|
default: "./",
|
|
when: (
|
|
/* istanbul ignore next */
|
|
(answers) => answers.reporters.includes("mochawesome")
|
|
)
|
|
}, {
|
|
type: "confirm",
|
|
name: "npmInstall",
|
|
message: () => `Do you want me to run \`${detectPackageManager()} install\``,
|
|
default: true
|
|
}];
|
|
export {
|
|
SUPPORTED_PACKAGE_MANAGERS
|
|
};
|