create-wdio 8.4.10 → 9.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +3 -3
  2. package/assets/demo.gif +0 -0
  3. package/build/cli/config.d.ts +51 -0
  4. package/build/cli/config.d.ts.map +1 -0
  5. package/build/cli/install.d.ts +12 -0
  6. package/build/cli/install.d.ts.map +1 -0
  7. package/build/cli/utils.d.ts +18 -0
  8. package/build/cli/utils.d.ts.map +1 -0
  9. package/build/config/cli.d.ts +2 -0
  10. package/build/config/cli.d.ts.map +1 -0
  11. package/build/config/cli.js +1632 -0
  12. package/build/constants.d.ts +248 -0
  13. package/build/constants.d.ts.map +1 -0
  14. package/build/index.d.ts +4 -0
  15. package/build/index.d.ts.map +1 -0
  16. package/build/index.js +1014 -105
  17. package/build/install/cli.d.ts +2 -0
  18. package/build/install/cli.d.ts.map +1 -0
  19. package/build/install/cli.js +1719 -0
  20. package/build/install.d.ts +4 -0
  21. package/build/install.d.ts.map +1 -0
  22. package/build/templates/EjsHelpers.d.ts +18 -0
  23. package/build/templates/EjsHelpers.d.ts.map +1 -0
  24. package/build/templates/exampleFiles/browser/Component.css.ejs +121 -0
  25. package/build/templates/exampleFiles/browser/Component.lit.ejs +154 -0
  26. package/build/templates/exampleFiles/browser/Component.lit.test.ejs +24 -0
  27. package/build/templates/exampleFiles/browser/Component.preact.ejs +28 -0
  28. package/build/templates/exampleFiles/browser/Component.preact.test.ejs +59 -0
  29. package/build/templates/exampleFiles/browser/Component.react.ejs +29 -0
  30. package/build/templates/exampleFiles/browser/Component.react.test.ejs +58 -0
  31. package/build/templates/exampleFiles/browser/Component.solid.ejs +28 -0
  32. package/build/templates/exampleFiles/browser/Component.solid.test.ejs +58 -0
  33. package/build/templates/exampleFiles/browser/Component.stencil.ejs +43 -0
  34. package/build/templates/exampleFiles/browser/Component.stencil.test.ejs +45 -0
  35. package/build/templates/exampleFiles/browser/Component.svelte.ejs +47 -0
  36. package/build/templates/exampleFiles/browser/Component.svelte.test.ejs +58 -0
  37. package/build/templates/exampleFiles/browser/Component.vue.ejs +34 -0
  38. package/build/templates/exampleFiles/browser/Component.vue.test.ejs +62 -0
  39. package/build/templates/exampleFiles/browser/standalone.test.ejs +13 -0
  40. package/build/templates/exampleFiles/cucumber/features/login.feature +12 -0
  41. package/build/templates/exampleFiles/cucumber/step_definitions/steps.js.ejs +55 -0
  42. package/build/templates/exampleFiles/mochaJasmine/test.e2e.js.ejs +11 -0
  43. package/build/templates/exampleFiles/pageobjects/login.page.js.ejs +45 -0
  44. package/build/templates/exampleFiles/pageobjects/page.js.ejs +17 -0
  45. package/build/templates/exampleFiles/pageobjects/secure.page.js.ejs +20 -0
  46. package/build/templates/exampleFiles/serenity-js/common/config/serenity.properties.ejs +1 -0
  47. package/build/templates/exampleFiles/serenity-js/common/serenity/github-api/GitHubStatus.ts.ejs +41 -0
  48. package/build/templates/exampleFiles/serenity-js/common/serenity/todo-list-app/TodoList.ts.ejs +100 -0
  49. package/build/templates/exampleFiles/serenity-js/common/serenity/todo-list-app/TodoListItem.ts.ejs +36 -0
  50. package/build/templates/exampleFiles/serenity-js/cucumber/step-definitions/steps.ts.ejs +37 -0
  51. package/build/templates/exampleFiles/serenity-js/cucumber/support/parameter.config.ts.ejs +18 -0
  52. package/build/templates/exampleFiles/serenity-js/cucumber/todo-list/completing_items.feature.ejs +23 -0
  53. package/build/templates/exampleFiles/serenity-js/cucumber/todo-list/narrative.md.ejs +17 -0
  54. package/build/templates/exampleFiles/serenity-js/jasmine/example.spec.ts.ejs +86 -0
  55. package/build/templates/exampleFiles/serenity-js/mocha/example.spec.ts.ejs +88 -0
  56. package/build/templates/snippets/afterTest.ejs +20 -0
  57. package/build/templates/snippets/capabilities.ejs +57 -0
  58. package/build/templates/snippets/cucumber.ejs +50 -0
  59. package/build/templates/snippets/electronTest.js.ejs +7 -0
  60. package/build/templates/snippets/jasmine.ejs +20 -0
  61. package/build/templates/snippets/macosTest.js.ejs +11 -0
  62. package/build/templates/snippets/mocha.ejs +14 -0
  63. package/build/templates/snippets/reporters.ejs +14 -0
  64. package/build/templates/snippets/serenity.ejs +18 -0
  65. package/build/templates/snippets/services.ejs +18 -0
  66. package/build/templates/snippets/testWithPO.js.ejs +22 -0
  67. package/build/templates/snippets/testWithoutPO.js.ejs +19 -0
  68. package/build/templates/snippets/vscodeTest.js.ejs +9 -0
  69. package/build/templates/wdio.conf.tpl.ejs +412 -0
  70. package/build/types.d.ts +99 -0
  71. package/build/types.d.ts.map +1 -0
  72. package/build/utils/index.d.ts +2 -0
  73. package/build/utils/index.d.ts.map +1 -0
  74. package/build/utils/index.js +849 -0
  75. package/build/utils.d.ts +78 -0
  76. package/build/utils.d.ts.map +1 -0
  77. package/package.json +58 -41
  78. package/__mocks__/commander.ts +0 -19
  79. package/build/constants.js +0 -76
  80. package/build/types.js +0 -1
  81. package/build/utils.js +0 -44
  82. package/vitest.config.ts +0 -23
@@ -0,0 +1,1632 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/cli/config.ts
8
+ var config_exports = {};
9
+ __export(config_exports, {
10
+ builder: () => builder,
11
+ canAccessConfigPath: () => canAccessConfigPath,
12
+ cmdArgs: () => cmdArgs,
13
+ command: () => command,
14
+ desc: () => desc,
15
+ formatConfigFilePaths: () => formatConfigFilePaths,
16
+ handler: () => handler,
17
+ missingConfigurationPrompt: () => missingConfigurationPrompt
18
+ });
19
+ import fss from "node:fs";
20
+
21
+ // src/utils.ts
22
+ import url from "node:url";
23
+ import path from "node:path";
24
+ import util, { promisify } from "node:util";
25
+ import fs from "node:fs/promises";
26
+ import { execSync } from "node:child_process";
27
+ import readDir from "recursive-readdir";
28
+ import { $ } from "execa";
29
+ import { readPackageUp } from "read-pkg-up";
30
+
31
+ // src/templates/EjsHelpers.ts
32
+ var EjsHelpers = class {
33
+ useTypeScript;
34
+ useEsm;
35
+ constructor(config) {
36
+ this.useTypeScript = config.useTypeScript ?? false;
37
+ this.useEsm = config.useEsm ?? false;
38
+ }
39
+ if(condition, trueValue, falseValue = "") {
40
+ return condition ? trueValue : falseValue;
41
+ }
42
+ ifTs = (trueValue, falseValue = "") => this.if(this.useTypeScript, trueValue, falseValue);
43
+ ifEsm = (trueValue, falseValue = "") => this.if(this.useEsm, trueValue, falseValue);
44
+ param(name, type) {
45
+ return this.useTypeScript ? `${name}: ${type}` : name;
46
+ }
47
+ returns(type) {
48
+ return this.useTypeScript ? `: ${type}` : "";
49
+ }
50
+ import(exports, moduleId) {
51
+ const individualExports = exports.split(",").map((id) => id.trim());
52
+ const imports = this.useTypeScript ? individualExports : individualExports.filter((id) => !id.startsWith("type "));
53
+ if (!imports.length) {
54
+ return "";
55
+ }
56
+ const modulePath = this.modulePathFrom(moduleId);
57
+ return this.useEsm || this.useTypeScript ? `import { ${imports.join(", ")} } from '${modulePath}'` : `const { ${imports.join(", ")} } = require('${modulePath}')`;
58
+ }
59
+ modulePathFrom(moduleId) {
60
+ if (!(moduleId.startsWith(".") && this.useEsm)) {
61
+ return moduleId;
62
+ }
63
+ if (moduleId.endsWith("/") && this.useEsm) {
64
+ return moduleId + "index.js";
65
+ }
66
+ return moduleId + ".js";
67
+ }
68
+ export(keyword, name) {
69
+ if (this.useTypeScript) {
70
+ return `export ${keyword} ${name}`;
71
+ }
72
+ if (this.useEsm) {
73
+ return `export ${keyword} ${name}`;
74
+ }
75
+ if (["class", "function"].includes(keyword)) {
76
+ return `module.exports.${name} = ${keyword} ${name}`;
77
+ }
78
+ return `module.exports.${name}`;
79
+ }
80
+ };
81
+
82
+ // src/utils.ts
83
+ import inquirer from "inquirer";
84
+ import ejs from "ejs";
85
+ import spawn from "cross-spawn";
86
+ import chalk from "chalk";
87
+
88
+ // src/install.ts
89
+ import { execa } from "execa";
90
+ var installCommand = {
91
+ npm: "install",
92
+ pnpm: "add",
93
+ yarn: "add",
94
+ bun: "install"
95
+ };
96
+ var devFlag = {
97
+ npm: "--save-dev",
98
+ pnpm: "--save-dev",
99
+ yarn: "--dev",
100
+ bun: "--dev"
101
+ };
102
+ async function installPackages(cwd, packages, dev) {
103
+ const pm = detectPackageManager();
104
+ const devParam = dev ? devFlag[pm] : "";
105
+ console.log("\n");
106
+ const p = execa(pm, [installCommand[pm], ...packages, devParam], {
107
+ cwd,
108
+ stdout: process.stdout,
109
+ stderr: process.stderr
110
+ });
111
+ const { stdout, stderr, exitCode } = await p;
112
+ if (exitCode !== 0) {
113
+ const cmd = getInstallCommand(pm, packages, dev);
114
+ const customError = `\u26A0\uFE0F An unknown error happened! Please retry installing dependencies via "${cmd}"
115
+
116
+ Error: ${stderr || stdout || "unknown"}`;
117
+ console.error(customError);
118
+ return false;
119
+ }
120
+ return true;
121
+ }
122
+ function getInstallCommand(pm, packages, dev) {
123
+ const devParam = dev ? devFlag[pm] : "";
124
+ return `${pm} ${installCommand[pm]} ${packages.join(" ")} ${devParam}`;
125
+ }
126
+
127
+ // src/utils.ts
128
+ var NPM_COMMAND = /^win/.test(process.platform) ? "npm.cmd" : "npm";
129
+ var __dirname = path.dirname(url.fileURLToPath(import.meta.url));
130
+ process.on("SIGINT", () => printAndExit(void 0, "SIGINT"));
131
+ var TEMPLATE_ROOT_DIR = path.join(__dirname, "templates", "exampleFiles");
132
+ function runProgram(command2, args, options) {
133
+ const child = spawn(command2, args, { stdio: "inherit", ...options });
134
+ return new Promise((resolve, rejects) => {
135
+ let error;
136
+ child.on("error", (e) => error = e);
137
+ child.on("close", (code, signal) => {
138
+ if (code !== 0) {
139
+ const errorMessage = error && error.message || `Error calling: ${command2} ${args.join(" ")}`;
140
+ printAndExit(errorMessage, signal);
141
+ return rejects(errorMessage);
142
+ }
143
+ resolve();
144
+ });
145
+ });
146
+ }
147
+ function printAndExit(error, signal) {
148
+ if (signal === "SIGINT") {
149
+ console.log("\n\nGoodbye \u{1F44B}");
150
+ } else {
151
+ console.log(`
152
+
153
+ \u26A0\uFE0F Ups, something went wrong${error ? `: ${error}` : ""}!`);
154
+ }
155
+ process.exit(1);
156
+ }
157
+ function convertPackageHashToObject(pkg, hash = "$--$") {
158
+ const [p, short, purpose] = pkg.split(hash);
159
+ return { package: p, short, purpose };
160
+ }
161
+ async function getAnswers(yes) {
162
+ if (yes) {
163
+ const ignoredQuestions = ["e2eEnvironment"];
164
+ const filteredQuestionaire = QUESTIONNAIRE.filter((question) => !ignoredQuestions.includes(question.name));
165
+ const answers2 = {};
166
+ for (const question of filteredQuestionaire) {
167
+ if (question.when && !question.when(answers2)) {
168
+ continue;
169
+ }
170
+ Object.assign(answers2, {
171
+ [question.name]: typeof question.default !== "undefined" ? typeof question.default === "function" ? await question.default(answers2) : question.default : question.choices && question.choices.length ? typeof question.choices === "function" ? question.choices(answers2)[0].value ? question.choices(answers2)[0].value : question.choices(answers2)[0] : question.choices[0].value ? question.type === "checkbox" ? [question.choices[0].value] : question.choices[0].value : question.choices[0] : {}
172
+ });
173
+ }
174
+ answers2.isUsingTypeScript = await answers2.isUsingTypeScript;
175
+ answers2.specs = await answers2.specs;
176
+ answers2.pages = await answers2.pages;
177
+ return answers2;
178
+ }
179
+ const projectProps = await getProjectProps(process.cwd());
180
+ const isProjectExisting = Boolean(projectProps);
181
+ const nameInPackageJsonIsNotCreateWdioDefault = projectProps?.packageJson?.name !== "my-new-project";
182
+ const projectName = projectProps?.packageJson?.name ? ` named "${projectProps.packageJson.name}"` : "";
183
+ const projectRootQuestions = [
184
+ {
185
+ type: "confirm",
186
+ name: "createPackageJSON",
187
+ default: true,
188
+ message: `Couldn't find a package.json in "${process.cwd()}" or any of the parent directories, do you want to create one?`,
189
+ when: !isProjectExisting
190
+ },
191
+ {
192
+ type: "confirm",
193
+ name: "projectRootCorrect",
194
+ default: true,
195
+ message: `A project${projectName} was detected at "${projectProps?.path}", correct?`,
196
+ when: isProjectExisting && nameInPackageJsonIsNotCreateWdioDefault
197
+ },
198
+ {
199
+ type: "input",
200
+ name: "projectRoot",
201
+ message: "What is the project root for your test project?",
202
+ default: projectProps?.path,
203
+ // only ask if there are more than 1 runner to pick from
204
+ when: (
205
+ /* istanbul ignore next */
206
+ (answers2) => isProjectExisting && nameInPackageJsonIsNotCreateWdioDefault && !answers2.projectRootCorrect
207
+ )
208
+ }
209
+ ];
210
+ const answers = await inquirer.prompt(projectRootQuestions);
211
+ if (answers.createPackageJSON) {
212
+ answers.projectRoot = process.cwd();
213
+ }
214
+ return inquirer.prompt(QUESTIONNAIRE, answers);
215
+ }
216
+ async function getProjectProps(cwd = process.cwd()) {
217
+ try {
218
+ const { packageJson, path: packageJsonPath } = await readPackageUp({ cwd }) || {};
219
+ if (!packageJson || !packageJsonPath) {
220
+ return void 0;
221
+ }
222
+ return {
223
+ esmSupported: packageJson.type === "module" || typeof packageJson.module === "string",
224
+ packageJson,
225
+ path: path.dirname(packageJsonPath)
226
+ };
227
+ } catch {
228
+ return void 0;
229
+ }
230
+ }
231
+ function getPathForFileGeneration(answers, projectRootDir) {
232
+ const specAnswer = answers.specs || "";
233
+ const stepDefinitionAnswer = answers.stepDefinitions || "";
234
+ const pageObjectAnswer = answers.pages || "";
235
+ const destSpecRootPath = generatePathFromAnswer(specAnswer, projectRootDir).replace(/\*\*$/, "");
236
+ const destStepRootPath = generatePathFromAnswer(stepDefinitionAnswer, projectRootDir);
237
+ const destPageObjectRootPath = answers.usePageObjects ? generatePathFromAnswer(pageObjectAnswer, projectRootDir).replace(/\*\*$/, "") : "";
238
+ const destSerenityLibRootPath = usesSerenity(answers) ? path.resolve(projectRootDir, answers.serenityLibPath || "serenity") : "";
239
+ const relativePath = answers.generateTestFiles && answers.usePageObjects ? !(convertPackageHashToObject(answers.framework).short === "cucumber") ? path.relative(destSpecRootPath, destPageObjectRootPath) : path.relative(destStepRootPath, destPageObjectRootPath) : "";
240
+ return {
241
+ destSpecRootPath,
242
+ destStepRootPath,
243
+ destPageObjectRootPath,
244
+ destSerenityLibRootPath,
245
+ relativePath: relativePath.replaceAll(path.sep, "/")
246
+ };
247
+ }
248
+ function generatePathFromAnswer(answers, projectRootDir) {
249
+ return path.resolve(
250
+ projectRootDir,
251
+ path.dirname(answers) === "." ? path.resolve(answers) : path.dirname(answers)
252
+ );
253
+ }
254
+ async function getProjectRoot(parsedAnswers) {
255
+ if (parsedAnswers?.createPackageJSON && parsedAnswers.projectRoot) {
256
+ return parsedAnswers.projectRoot;
257
+ }
258
+ const root = (await getProjectProps())?.path;
259
+ if (!root) {
260
+ throw new Error("Could not find project root directory with a package.json");
261
+ }
262
+ return !parsedAnswers || parsedAnswers.projectRootCorrect ? root : parsedAnswers.projectRoot || process.cwd();
263
+ }
264
+ async function createPackageJSON(parsedAnswers) {
265
+ const packageJsonExists = await fs.access(path.resolve(process.cwd(), "package.json")).then(() => true, () => false);
266
+ if (packageJsonExists) {
267
+ return;
268
+ }
269
+ if (parsedAnswers.createPackageJSON === false) {
270
+ if (!packageJsonExists) {
271
+ console.log(`No WebdriverIO configuration found in "${parsedAnswers.wdioConfigPath}"`);
272
+ return !process.env.WDIO_UNIT_TESTS && process.exit(0);
273
+ }
274
+ return;
275
+ }
276
+ if (parsedAnswers.createPackageJSON) {
277
+ console.log(`Creating a ${chalk.bold("package.json")} for the directory...`);
278
+ await fs.writeFile(path.resolve(process.cwd(), "package.json"), JSON.stringify({
279
+ name: "webdriverio-tests",
280
+ version: "0.0.0",
281
+ private: true,
282
+ license: "ISC",
283
+ type: "module",
284
+ dependencies: {},
285
+ devDependencies: {}
286
+ }, null, 2));
287
+ console.log(chalk.green(chalk.bold("\u2714 Success!\n")));
288
+ }
289
+ }
290
+ async function setupTypeScript(parsedAnswers) {
291
+ if (!parsedAnswers.isUsingTypeScript) {
292
+ return;
293
+ }
294
+ if (parsedAnswers.hasRootTSConfig) {
295
+ return;
296
+ }
297
+ console.log("Setting up TypeScript...");
298
+ const frameworkPackage = convertPackageHashToObject(parsedAnswers.rawAnswers.framework);
299
+ const servicePackages = parsedAnswers.rawAnswers.services.map((service) => convertPackageHashToObject(service));
300
+ const serenityTypes = parsedAnswers.serenityAdapter === "jasmine" ? ["jasmine"] : [];
301
+ const types = [
302
+ "node",
303
+ ...parsedAnswers.framework === "jasmine" ? ["jasmine"] : [],
304
+ "@wdio/globals/types",
305
+ ...parsedAnswers.framework === "jasmine" ? ["expect-webdriverio/jasmine"] : ["expect-webdriverio"],
306
+ ...parsedAnswers.serenityAdapter ? serenityTypes : [frameworkPackage.package],
307
+ ...parsedAnswers.runner === "browser" ? ["@wdio/browser-runner"] : [],
308
+ ...servicePackages.filter((service) => (
309
+ /**
310
+ * given that we know that all "official" services have
311
+ * typescript support we only include them
312
+ */
313
+ service.package.startsWith("@wdio") || /**
314
+ * also include community maintained packages with known
315
+ * support for TypeScript
316
+ */
317
+ COMMUNITY_PACKAGES_WITH_TS_SUPPORT.includes(service.package)
318
+ )).map((service) => service.package)
319
+ ];
320
+ const preset = getPreset(parsedAnswers);
321
+ const config = {
322
+ compilerOptions: {
323
+ // compiler
324
+ moduleResolution: "node",
325
+ module: !parsedAnswers.esmSupport ? "commonjs" : "ESNext",
326
+ target: "es2022",
327
+ lib: ["es2022", "dom"],
328
+ types,
329
+ skipLibCheck: true,
330
+ // bundler
331
+ noEmit: true,
332
+ allowImportingTsExtensions: true,
333
+ resolveJsonModule: true,
334
+ isolatedModules: true,
335
+ // linting
336
+ strict: true,
337
+ noUnusedLocals: true,
338
+ noUnusedParameters: true,
339
+ noFallthroughCasesInSwitch: true,
340
+ ...Object.assign(
341
+ preset === "lit" ? {
342
+ experimentalDecorators: true,
343
+ useDefineForClassFields: false
344
+ } : {},
345
+ preset === "react" ? {
346
+ jsx: "react-jsx"
347
+ } : {},
348
+ preset === "preact" ? {
349
+ jsx: "react-jsx",
350
+ jsxImportSource: "preact"
351
+ } : {},
352
+ preset === "solid" ? {
353
+ jsx: "preserve",
354
+ jsxImportSource: "solid-js"
355
+ } : {},
356
+ preset === "stencil" ? {
357
+ experimentalDecorators: true,
358
+ jsx: "react",
359
+ jsxFactory: "h",
360
+ jsxFragmentFactory: "Fragment"
361
+ } : {}
362
+ )
363
+ },
364
+ include: preset === "svelte" ? ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] : preset === "vue" ? ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] : ["test", "wdio.conf.ts"]
365
+ };
366
+ if (parsedAnswers.framework === "cucumber") {
367
+ config.include.push("features");
368
+ }
369
+ await fs.mkdir(path.dirname(parsedAnswers.tsConfigFilePath), { recursive: true });
370
+ await fs.writeFile(
371
+ parsedAnswers.tsConfigFilePath,
372
+ JSON.stringify(config, null, 4)
373
+ );
374
+ console.log(chalk.green(chalk.bold("\u2714 Success!\n")));
375
+ }
376
+ var SEP = "\n- ";
377
+ async function npmInstall(parsedAnswers, npmTag) {
378
+ const servicePackages = parsedAnswers.rawAnswers.services.map((service) => convertPackageHashToObject(service));
379
+ const presetPackage = convertPackageHashToObject(parsedAnswers.rawAnswers.preset || "");
380
+ if (parsedAnswers.installTestingLibrary && TESTING_LIBRARY_PACKAGES[presetPackage.short]) {
381
+ parsedAnswers.packagesToInstall.push(
382
+ TESTING_LIBRARY_PACKAGES[presetPackage.short],
383
+ "@testing-library/jest-dom"
384
+ );
385
+ }
386
+ if (presetPackage.short === "solid") {
387
+ parsedAnswers.packagesToInstall.push("solid-js");
388
+ }
389
+ if (parsedAnswers.includeVisualTesting) {
390
+ parsedAnswers.packagesToInstall.push("@wdio/visual-service");
391
+ }
392
+ const preset = getPreset(parsedAnswers);
393
+ if (preset === "lit") {
394
+ parsedAnswers.packagesToInstall.push("lit");
395
+ }
396
+ if (preset === "stencil") {
397
+ parsedAnswers.packagesToInstall.push("@stencil/core");
398
+ }
399
+ if (presetPackage.short === "react") {
400
+ parsedAnswers.packagesToInstall.push("react");
401
+ if (!parsedAnswers.installTestingLibrary) {
402
+ parsedAnswers.packagesToInstall.push("react-dom");
403
+ }
404
+ }
405
+ if (parsedAnswers.framework === "jasmine" && parsedAnswers.isUsingTypeScript) {
406
+ parsedAnswers.packagesToInstall.push("@types/jasmine");
407
+ }
408
+ if (parsedAnswers.purpose === "macos") {
409
+ parsedAnswers.packagesToInstall.push("appium-mac2-driver");
410
+ }
411
+ if (parsedAnswers.mobileEnvironment === "android") {
412
+ parsedAnswers.packagesToInstall.push("appium-uiautomator2-driver");
413
+ }
414
+ if (parsedAnswers.mobileEnvironment === "ios") {
415
+ parsedAnswers.packagesToInstall.push("appium-xcuitest-driver");
416
+ }
417
+ addServiceDeps(servicePackages, parsedAnswers.packagesToInstall);
418
+ parsedAnswers.packagesToInstall = specifyVersionIfNeeded(parsedAnswers.packagesToInstall, package_default.version, npmTag);
419
+ const cwd = await getProjectRoot(parsedAnswers);
420
+ const pm = detectPackageManager();
421
+ if (parsedAnswers.npmInstall) {
422
+ console.log(`Installing packages using ${pm}:${SEP}${parsedAnswers.packagesToInstall.join(SEP)}`);
423
+ const success = await installPackages(cwd, parsedAnswers.packagesToInstall, true);
424
+ if (success) {
425
+ console.log(chalk.green(chalk.bold("\u2714 Success!\n")));
426
+ }
427
+ } else {
428
+ const installationCommand = getInstallCommand(pm, parsedAnswers.packagesToInstall, true);
429
+ console.log(util.format(DEPENDENCIES_INSTALLATION_MESSAGE, installationCommand));
430
+ }
431
+ }
432
+ function getPreset(parsedAnswers) {
433
+ const isUsingFramework = typeof parsedAnswers.preset === "string";
434
+ return isUsingFramework ? parsedAnswers.preset || "lit" : "";
435
+ }
436
+ function addServiceDeps(names, packages, update = false) {
437
+ if (names.some(({ short }) => short === "appium")) {
438
+ const result = execSync("appium --version || echo APPIUM_MISSING", { stdio: "pipe" }).toString().trim();
439
+ if (result === "APPIUM_MISSING") {
440
+ packages.push("appium");
441
+ } else if (update) {
442
+ console.log(
443
+ "\n=======",
444
+ "\nUsing globally installed appium",
445
+ result,
446
+ "\nPlease add the following to your wdio.conf.js:",
447
+ "\nappium: { command: 'appium' }",
448
+ "\n=======\n"
449
+ );
450
+ }
451
+ }
452
+ }
453
+ async function createWDIOConfig(parsedAnswers) {
454
+ try {
455
+ console.log("Creating a WebdriverIO config file...");
456
+ const tplPath = path.resolve(__dirname, "templates", "wdio.conf.tpl.ejs");
457
+ const renderedTpl = await renderFile(tplPath, {
458
+ answers: parsedAnswers,
459
+ _: new EjsHelpers({ useEsm: parsedAnswers.esmSupport, useTypeScript: parsedAnswers.isUsingTypeScript })
460
+ });
461
+ await fs.writeFile(parsedAnswers.wdioConfigPath, renderedTpl);
462
+ console.log(chalk.green(chalk.bold("\u2714 Success!\n")));
463
+ if (parsedAnswers.generateTestFiles) {
464
+ console.log("Autogenerate test files...");
465
+ await generateTestFiles(parsedAnswers);
466
+ console.log(chalk.green(chalk.bold("\u2714 Success!\n")));
467
+ }
468
+ } catch (err) {
469
+ throw new Error(`\u26A0\uFE0F Couldn't write config file: ${err.stack}`);
470
+ }
471
+ }
472
+ var renderFile = promisify(ejs.renderFile);
473
+ async function createWDIOScript(parsedAnswers) {
474
+ const rootDir = await getProjectRoot(parsedAnswers);
475
+ const pathToWdioConfig = `./${path.join(".", parsedAnswers.wdioConfigPath.replace(rootDir, ""))}`;
476
+ const wdioScripts = {
477
+ "wdio": `wdio run ${pathToWdioConfig}`
478
+ };
479
+ const serenityScripts = {
480
+ "serenity": "failsafe serenity:clean wdio serenity:report",
481
+ "serenity:clean": "rimraf target",
482
+ "wdio": `wdio run ${pathToWdioConfig}`,
483
+ "serenity:report": "serenity-bdd run"
484
+ };
485
+ const scripts = parsedAnswers.serenityAdapter ? serenityScripts : wdioScripts;
486
+ for (const [script, command2] of Object.entries(scripts)) {
487
+ const args = ["pkg", "set", `scripts.${script}=${command2}`];
488
+ try {
489
+ console.log(`Adding ${chalk.bold(`"${script}"`)} script to package.json`);
490
+ await runProgram(NPM_COMMAND, args, { cwd: parsedAnswers.projectRootDir });
491
+ } catch (err) {
492
+ const [preArgs, scriptPath] = args.join(" ").split("=");
493
+ console.error(
494
+ `\u26A0\uFE0F Couldn't add script to package.json: "${err.message}", you can add it manually by running:
495
+
496
+ ${NPM_COMMAND} ${preArgs}="${scriptPath}"`
497
+ );
498
+ return false;
499
+ }
500
+ }
501
+ console.log(chalk.green(chalk.bold("\u2714 Success!")));
502
+ return true;
503
+ }
504
+ async function runAppiumInstaller(parsedAnswers) {
505
+ if (parsedAnswers.e2eEnvironment !== "mobile") {
506
+ return;
507
+ }
508
+ const answer = await inquirer.prompt({
509
+ name: "continueWithAppiumSetup",
510
+ message: "Continue with Appium setup using appium-installer (https://github.com/AppiumTestDistribution/appium-installer)?",
511
+ type: "confirm",
512
+ default: true
513
+ });
514
+ if (!answer.continueWithAppiumSetup) {
515
+ return console.log(
516
+ "Ok! You can learn more about setting up mobile environments in the Appium docs at https://appium.io/docs/en/latest/quickstart/"
517
+ );
518
+ }
519
+ return $({ stdio: "inherit" })`npx appium-installer`;
520
+ }
521
+ function getSerenityPackages(answers) {
522
+ const framework = convertPackageHashToObject(answers.framework);
523
+ if (framework.package !== "@serenity-js/webdriverio") {
524
+ return [];
525
+ }
526
+ const packages = {
527
+ cucumber: [
528
+ "@cucumber/cucumber",
529
+ "@serenity-js/cucumber"
530
+ ],
531
+ mocha: [
532
+ "@serenity-js/mocha",
533
+ "mocha"
534
+ ],
535
+ jasmine: [
536
+ "@serenity-js/jasmine",
537
+ "jasmine"
538
+ ],
539
+ common: [
540
+ "@serenity-js/assertions",
541
+ "@serenity-js/console-reporter",
542
+ "@serenity-js/core",
543
+ "@serenity-js/rest",
544
+ "@serenity-js/serenity-bdd",
545
+ "@serenity-js/web",
546
+ "npm-failsafe",
547
+ "rimraf"
548
+ ]
549
+ };
550
+ if (answers.isUsingTypeScript) {
551
+ packages.mocha.push("@types/mocha");
552
+ packages.jasmine.push("@types/jasmine");
553
+ packages.common.push("@types/node");
554
+ }
555
+ return [
556
+ ...packages[framework.purpose],
557
+ ...packages.common
558
+ ].filter(Boolean).sort();
559
+ }
560
+ function detectPackageManager() {
561
+ if (!process.env.npm_config_user_agent) {
562
+ return "npm";
563
+ }
564
+ const detectedPM = process.env.npm_config_user_agent.split("/")[0].toLowerCase();
565
+ const matchedPM = SUPPORTED_PACKAGE_MANAGERS.find((pm) => pm.toLowerCase() === detectedPM);
566
+ return matchedPM || "npm";
567
+ }
568
+ async function getDefaultFiles(answers, pattern) {
569
+ const rootDir = await getProjectRoot(answers);
570
+ const presetPackage = convertPackageHashToObject(answers.preset || "");
571
+ const isJSX = TSX_BASED_FRAMEWORKS.includes(presetPackage.short || "");
572
+ 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" : ""}`;
573
+ return val;
574
+ }
575
+ var TSX_BASED_FRAMEWORKS = ["react", "preact", "solid", "stencil"];
576
+ async function detectCompiler(answers) {
577
+ if (answers.createPackageJSON) {
578
+ return false;
579
+ }
580
+ const root = await getProjectRoot(answers);
581
+ const hasRootTSConfig = await fs.access(path.resolve(root, "tsconfig.json")).then(() => true, () => false);
582
+ return hasRootTSConfig;
583
+ }
584
+ var VERSION_REGEXP = /(\d+)\.(\d+)\.(\d+)-(alpha|beta|)\.(\d+)\+(.+)/g;
585
+ function specifyVersionIfNeeded(packagesToInstall, version, npmTag) {
586
+ const { value } = version.matchAll(VERSION_REGEXP).next();
587
+ const [major, minor, patch, tagName, build] = (value || []).slice(1, -1);
588
+ return packagesToInstall.map((p) => {
589
+ if (p.startsWith("@wdio") && p !== "@wdio/visual-service" || ["webdriver", "webdriverio"].includes(p)) {
590
+ const tag = major && npmTag === "latest" ? `^${major}.${minor}.${patch}-${tagName}.${build}` : npmTag;
591
+ return `${p}@${tag}`;
592
+ }
593
+ return p;
594
+ });
595
+ }
596
+ async function generateTestFiles(answers) {
597
+ if (answers.serenityAdapter) {
598
+ return generateSerenityExamples(answers);
599
+ }
600
+ if (answers.runner === "local") {
601
+ return generateLocalRunnerTestFiles(answers);
602
+ }
603
+ return generateBrowserRunnerTestFiles(answers);
604
+ }
605
+ async function generateSerenityExamples(answers) {
606
+ const templateDirectories = Object.entries({
607
+ [answers.projectRootDir]: path.join(TEMPLATE_ROOT_DIR, "serenity-js", "common", "config"),
608
+ [answers.destSpecRootPath]: path.join(TEMPLATE_ROOT_DIR, "serenity-js", answers.serenityAdapter),
609
+ [answers.destSerenityLibRootPath]: path.join(TEMPLATE_ROOT_DIR, "serenity-js", "common", "serenity")
610
+ });
611
+ await Promise.all(templateDirectories.map(async ([destinationRootDir, templateRootDir]) => {
612
+ const pathsToTemplates = await readDir(templateRootDir);
613
+ await Promise.all(pathsToTemplates.map(async (pathToTemplate) => {
614
+ const extension = answers.isUsingTypeScript ? ".ts" : ".js";
615
+ const destination = path.join(destinationRootDir, path.relative(templateRootDir, pathToTemplate)).replace(/\.ejs$/, "").replace(/\.ts$/, extension);
616
+ const contents = await renderFile(
617
+ pathToTemplate,
618
+ { answers, _: new EjsHelpers({ useEsm: answers.esmSupport, useTypeScript: answers.isUsingTypeScript }) }
619
+ );
620
+ await fs.mkdir(path.dirname(destination), { recursive: true });
621
+ await fs.writeFile(destination, contents);
622
+ }));
623
+ }));
624
+ }
625
+ async function generateLocalRunnerTestFiles(answers) {
626
+ const testFiles = answers.framework === "cucumber" ? [path.join(TEMPLATE_ROOT_DIR, "cucumber")] : [path.join(TEMPLATE_ROOT_DIR, "mochaJasmine")];
627
+ if (answers.usePageObjects) {
628
+ testFiles.push(path.join(TEMPLATE_ROOT_DIR, "pageobjects"));
629
+ }
630
+ const files = (await Promise.all(testFiles.map((dirPath) => readDir(
631
+ dirPath,
632
+ [(file, stats) => !stats.isDirectory() && !(file.endsWith(".ejs") || file.endsWith(".feature"))]
633
+ )))).reduce((cur, acc) => [...acc, ...cur], []);
634
+ await Promise.all(files.map(async (file) => {
635
+ const renderedTpl = await renderFile(file, { answers });
636
+ const isJSX = answers.preset && TSX_BASED_FRAMEWORKS.includes(answers.preset);
637
+ const fileEnding = (answers.isUsingTypeScript ? ".ts" : ".js") + (isJSX ? "x" : "");
638
+ const destPath = (file.endsWith("page.js.ejs") ? path.join(answers.destPageObjectRootPath, path.basename(file)) : file.includes("step_definition") ? path.join(answers.destStepRootPath, path.basename(file)) : path.join(answers.destSpecRootPath, path.basename(file))).replace(/\.ejs$/, "").replace(/\.js$/, fileEnding);
639
+ await fs.mkdir(path.dirname(destPath), { recursive: true });
640
+ await fs.writeFile(destPath, renderedTpl);
641
+ }));
642
+ }
643
+ async function generateBrowserRunnerTestFiles(answers) {
644
+ const isUsingFramework = typeof answers.preset === "string";
645
+ const preset = getPreset(answers);
646
+ const tplRootDir = path.join(TEMPLATE_ROOT_DIR, "browser");
647
+ await fs.mkdir(answers.destSpecRootPath, { recursive: true });
648
+ if (isUsingFramework) {
649
+ const renderedCss = await renderFile(path.join(tplRootDir, "Component.css.ejs"), { answers });
650
+ await fs.writeFile(path.join(answers.destSpecRootPath, "Component.css"), renderedCss);
651
+ }
652
+ const testExt = `${answers.isUsingTypeScript ? "ts" : "js"}${TSX_BASED_FRAMEWORKS.includes(preset) ? "x" : ""}`;
653
+ const fileExt = ["svelte", "vue"].includes(preset) ? preset : testExt;
654
+ if (preset) {
655
+ const componentOutFileName = `Component.${fileExt}`;
656
+ const renderedComponent = await renderFile(path.join(tplRootDir, `Component.${preset}.ejs`), { answers });
657
+ await fs.writeFile(path.join(answers.destSpecRootPath, componentOutFileName), renderedComponent);
658
+ }
659
+ const componentFileName = preset ? `Component.${preset}.test.ejs` : "standalone.test.ejs";
660
+ const renderedTest = await renderFile(path.join(tplRootDir, componentFileName), { answers });
661
+ await fs.writeFile(path.join(answers.destSpecRootPath, `Component.test.${testExt}`), renderedTest);
662
+ }
663
+ async function formatConfigFilePaths(config) {
664
+ const fullPath = path.isAbsolute(config) ? config : path.join(process.cwd(), config);
665
+ const fullPathNoExtension = fullPath.substring(0, fullPath.lastIndexOf(path.extname(fullPath)));
666
+ return { fullPath, fullPathNoExtension };
667
+ }
668
+
669
+ // package.json
670
+ var package_default = {
671
+ name: "create-wdio",
672
+ version: "9.0.0",
673
+ description: "Install and setup a WebdriverIO project with all its dependencies in a single run",
674
+ author: "WebdriverIO Team <mail@webdriver.io>",
675
+ homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/create-wdio",
676
+ license: "MIT",
677
+ type: "module",
678
+ exports: {
679
+ ".": {
680
+ types: "./build/index.d.ts",
681
+ import: "./build/index.js",
682
+ importSource: "./src/index.ts"
683
+ },
684
+ "./config/cli": {
685
+ types: "./build/config/cli.d.ts",
686
+ import: "./build/config/cli.js",
687
+ importSource: "./src/config/cli.ts"
688
+ },
689
+ "./install/cli": {
690
+ types: "./build/install/cli.d.ts",
691
+ import: "./build/install/cli.js",
692
+ importSource: "./src/install/cli.ts"
693
+ },
694
+ "./utils": {
695
+ types: "./build/utils/index.d.ts",
696
+ import: "./build/utils/index.js",
697
+ importSource: "./src/utils/index.ts"
698
+ }
699
+ },
700
+ types: "./build/index.d.ts",
701
+ typeScriptVersion: "3.8.3",
702
+ engines: {
703
+ node: ">=12.0.0"
704
+ },
705
+ repository: {
706
+ type: "git",
707
+ url: "git+https://github.com/webdriverio/webdriverio.git",
708
+ directory: "packages/create-wdio"
709
+ },
710
+ keywords: [
711
+ "webdriverio",
712
+ "create-wdio",
713
+ "wdio",
714
+ "installer",
715
+ "e2e"
716
+ ],
717
+ bugs: {
718
+ url: "https://github.com/webdriverio/webdriverio/issues"
719
+ },
720
+ bin: {
721
+ "create-wdio": "./bin/wdio.js"
722
+ },
723
+ dependencies: {
724
+ chalk: "^5.3.0",
725
+ commander: "^14.0.0",
726
+ "cross-spawn": "^7.0.3",
727
+ ejs: "^3.1.10",
728
+ execa: "^9.6.0",
729
+ "import-meta-resolve": "^4.1.0",
730
+ inquirer: "^12.7.0",
731
+ "normalize-package-data": "^7.0.0",
732
+ "read-pkg-up": "^10.1.0",
733
+ "recursive-readdir": "^2.2.3",
734
+ semver: "^7.6.3",
735
+ "type-fest": "^4.41.0",
736
+ yargs: "^17.7.2"
737
+ },
738
+ devDependencies: {
739
+ "@types/cross-spawn": "^6.0.6",
740
+ "@types/ejs": "^3.1.5",
741
+ "@types/fs-extra": "^11.0.4",
742
+ "@types/normalize-package-data": "^2.4.4",
743
+ "@types/semver": "^7.5.8",
744
+ "@types/yargs": "^17.0.33"
745
+ },
746
+ publishConfig: {
747
+ access: "public"
748
+ }
749
+ };
750
+
751
+ // src/constants.ts
752
+ import path2 from "node:path";
753
+ import fs2 from "node:fs";
754
+ import chalk2 from "chalk";
755
+ var colorItBold = chalk2.bold.rgb(234, 89, 6);
756
+ var colorIt = chalk2.rgb(234, 89, 6);
757
+ var PROGRAM_TITLE = `
758
+ ${colorItBold("Webdriver.IO")}
759
+ ${colorIt("Next-gen browser and mobile automation")}
760
+ ${colorIt("test framework for Node.js")}
761
+ `;
762
+ var UNSUPPORTED_NODE_VERSION = `\u26A0\uFE0F Unsupported Node.js Version Error \u26A0\uFE0F
763
+ You are using Node.js ${process.version} which is too old to be used with WebdriverIO.
764
+ Please update to Node.js v20 to continue.
765
+ `;
766
+ var INSTALL_COMMAND = {
767
+ npm: "install",
768
+ pnpm: "add",
769
+ yarn: "add",
770
+ bun: "install"
771
+ };
772
+ var SUPPORTED_PACKAGE_MANAGERS = Object.keys(INSTALL_COMMAND);
773
+ var CONFIG_HELPER_INTRO = `
774
+ ===============================
775
+ \u{1F916} WDIO Configuration Wizard \u{1F9D9}
776
+ ===============================
777
+ `;
778
+ var CLI_EPILOGUE = `Documentation: https://webdriver.io
779
+ @wdio/cli (v${package_default.version})`;
780
+ var SUPPORTED_PACKAGES = {
781
+ runner: [
782
+ { name: "E2E Testing - of Web or Mobile Applications", value: "@wdio/local-runner$--$local$--$e2e" },
783
+ { name: "Component or Unit Testing - in the browser\n > https://webdriver.io/docs/component-testing", value: "@wdio/browser-runner$--$browser$--$component" },
784
+ { name: "Desktop Testing - of Electron Applications\n > https://webdriver.io/docs/desktop-testing/electron", value: "@wdio/local-runner$--$local$--$electron" },
785
+ { name: "Desktop Testing - of MacOS Applications\n > https://webdriver.io/docs/desktop-testing/macos", value: "@wdio/local-runner$--$local$--$macos" },
786
+ { name: "VS Code Extension Testing\n > https://webdriver.io/docs/vscode-extension-testing", value: "@wdio/local-runner$--$local$--$vscode" },
787
+ { name: "Roku Testing - of OTT apps running on RokuOS\n > https://webdriver.io/docs/wdio-roku-service", value: "@wdio/local-runner$--$local$--$roku" }
788
+ ],
789
+ framework: [
790
+ { name: "Mocha (https://mochajs.org/)", value: "@wdio/mocha-framework$--$mocha" },
791
+ { name: "Mocha with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$mocha" },
792
+ { name: "Jasmine (https://jasmine.github.io/)", value: "@wdio/jasmine-framework$--$jasmine" },
793
+ { name: "Jasmine with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$jasmine" },
794
+ { name: "Cucumber (https://cucumber.io/)", value: "@wdio/cucumber-framework$--$cucumber" },
795
+ { name: "Cucumber with Serenity/JS (https://serenity-js.org/)", value: "@serenity-js/webdriverio$--$@serenity-js/webdriverio$--$cucumber" }
796
+ ],
797
+ reporter: [
798
+ { name: "spec", value: "@wdio/spec-reporter$--$spec", checked: true },
799
+ { name: "dot", value: "@wdio/dot-reporter$--$dot" },
800
+ { name: "junit", value: "@wdio/junit-reporter$--$junit" },
801
+ { name: "allure", value: "@wdio/allure-reporter$--$allure" },
802
+ { name: "sumologic", value: "@wdio/sumologic-reporter$--$sumologic" },
803
+ { name: "concise", value: "@wdio/concise-reporter$--$concise" },
804
+ { name: "json", value: "@wdio/json-reporter$--$json" },
805
+ // external
806
+ { name: "reportportal", value: "wdio-reportportal-reporter$--$reportportal" },
807
+ { name: "video", value: "wdio-video-reporter$--$video" },
808
+ { name: "cucumber-json", value: "wdio-cucumberjs-json-reporter$--$cucumberjs-json" },
809
+ { name: "mochawesome", value: "wdio-mochawesome-reporter$--$mochawesome" },
810
+ { name: "timeline", value: "wdio-timeline-reporter$--$timeline" },
811
+ { name: "html-nice", value: "wdio-html-nice-reporter$--$html-nice" },
812
+ { name: "slack", value: "@moroo/wdio-slack-reporter$--$slack" },
813
+ { name: "teamcity", value: "wdio-teamcity-reporter$--$teamcity" },
814
+ { name: "delta", value: "@delta-reporter/wdio-delta-reporter-service$--$delta" },
815
+ { name: "testrail", value: "@wdio/testrail-reporter$--$testrail" },
816
+ { name: "light", value: "wdio-light-reporter$--$light" },
817
+ { name: "wdio-json-html-reporter", value: "wdio-json-html-reporter$--$jsonhtml" }
818
+ ],
819
+ plugin: [
820
+ { 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" },
821
+ { name: "angular-component-harnesses: support for Angular component test harnesses\n > https://www.npmjs.com/package/@badisi/wdio-harness", value: "@badisi/wdio-harness$--$harness" },
822
+ { 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" }
823
+ ],
824
+ service: [
825
+ // internal or community driver services
826
+ { name: "visual", value: "@wdio/visual-service$--$visual" },
827
+ { name: "vite", value: "wdio-vite-service$--$vite" },
828
+ { name: "nuxt", value: "wdio-nuxt-service$--$nuxt" },
829
+ { name: "firefox-profile", value: "@wdio/firefox-profile-service$--$firefox-profile" },
830
+ { name: "gmail", value: "wdio-gmail-service$--$gmail" },
831
+ { name: "sauce", value: "@wdio/sauce-service$--$sauce" },
832
+ { name: "testingbot", value: "@wdio/testingbot-service$--$testingbot" },
833
+ { name: "browserstack", value: "@wdio/browserstack-service$--$browserstack" },
834
+ { name: "lighthouse", value: "@wdio/lighthouse-service$--$lighthouse" },
835
+ { name: "vscode", value: "wdio-vscode-service$--$vscode" },
836
+ { name: "electron", value: "wdio-electron-service$--$electron" },
837
+ { name: "appium", value: "@wdio/appium-service$--$appium" },
838
+ // external
839
+ { name: "eslinter-service", value: "wdio-eslinter-service$--$eslinter" },
840
+ { name: "lambdatest", value: "wdio-lambdatest-service$--$lambdatest" },
841
+ { name: "zafira-listener", value: "wdio-zafira-listener-service$--$zafira-listener" },
842
+ { name: "reportportal", value: "wdio-reportportal-service$--$reportportal" },
843
+ { name: "docker", value: "wdio-docker-service$--$docker" },
844
+ { name: "ui5", value: "wdio-ui5-service$--$ui5" },
845
+ { name: "wiremock", value: "wdio-wiremock-service$--$wiremock" },
846
+ { name: "ng-apimock", value: "wdio-ng-apimock-service$--$ng-apimock" },
847
+ { name: "slack", value: "wdio-slack-service$--$slack" },
848
+ { name: "cucumber-viewport-logger", value: "wdio-cucumber-viewport-logger-service$--$cucumber-viewport-logger" },
849
+ { name: "intercept", value: "wdio-intercept-service$--$intercept" },
850
+ { name: "docker", value: "wdio-docker-service$--$docker" },
851
+ { name: "novus-visual-regression", value: "wdio-novus-visual-regression-service$--$novus-visual-regression" },
852
+ { name: "rerun", value: "wdio-rerun-service$--$rerun" },
853
+ { name: "winappdriver", value: "wdio-winappdriver-service$--$winappdriver" },
854
+ { name: "ywinappdriver", value: "wdio-ywinappdriver-service$--$ywinappdriver" },
855
+ { name: "performancetotal", value: "wdio-performancetotal-service$--$performancetotal" },
856
+ { name: "cleanuptotal", value: "wdio-cleanuptotal-service$--$cleanuptotal" },
857
+ { name: "aws-device-farm", value: "wdio-aws-device-farm-service$--$aws-device-farm" },
858
+ { name: "ms-teams", value: "wdio-ms-teams-service$--$ms-teams" },
859
+ { name: "tesults", value: "wdio-tesults-service$--$tesults" },
860
+ { name: "azure-devops", value: "@gmangiapelo/wdio-azure-devops-service$--$azure-devops" },
861
+ { name: "google-Chat", value: "wdio-google-chat-service$--$google-chat" },
862
+ { name: "qmate-service", value: "@sap_oss/wdio-qmate-service$--$qmate-service" },
863
+ { name: "robonut", value: "wdio-robonut-service$--$robonut" },
864
+ { name: "qunit", value: "wdio-qunit-service$--$qunit" },
865
+ { name: "roku", value: "wdio-roku-service$--$roku" },
866
+ { name: "obsidian", value: "wdio-obsidian-service$--$obsidian" }
867
+ ]
868
+ };
869
+ var configHelperSuccessMessage = ({ projectRootDir, runScript, extraInfo = "" }) => `
870
+ \u{1F916} Successfully setup project at ${projectRootDir} \u{1F389}
871
+
872
+ Join our Discord Community Server and instantly find answers to your issues or queries. Or just join and say hi \u{1F44B}!
873
+ \u{1F517} https://discord.webdriver.io
874
+
875
+ Visit the project on GitHub to report bugs \u{1F41B} or raise feature requests \u{1F4A1}:
876
+ \u{1F517} https://github.com/webdriverio/webdriverio
877
+ ${extraInfo}
878
+ To run your tests, execute:
879
+ $ cd ${projectRootDir}
880
+ $ npm run ${runScript}
881
+ `;
882
+ var isNuxtProject = [
883
+ path2.join(process.cwd(), "nuxt.config.js"),
884
+ path2.join(process.cwd(), "nuxt.config.ts"),
885
+ path2.join(process.cwd(), "nuxt.config.mjs"),
886
+ path2.join(process.cwd(), "nuxt.config.mts")
887
+ ].map((p) => {
888
+ try {
889
+ fs2.accessSync(p);
890
+ return true;
891
+ } catch {
892
+ return false;
893
+ }
894
+ }).some(Boolean);
895
+ var SUPPORTED_CONFIG_FILE_EXTENSION = ["js", "ts", "mjs", "mts", "cjs", "cts"];
896
+ var CONFIG_HELPER_SERENITY_BANNER = `
897
+ Learn more about Serenity/JS:
898
+ \u{1F517} https://serenity-js.org/
899
+ \u{1F517} https://serenity-js.org/handbook/test-runners/webdriverio/
900
+ `;
901
+ function usesSerenity(answers) {
902
+ return answers.framework.includes("serenity-js");
903
+ }
904
+ var ProtocolOptions = /* @__PURE__ */ ((ProtocolOptions2) => {
905
+ ProtocolOptions2["HTTPS"] = "https";
906
+ ProtocolOptions2["HTTP"] = "http";
907
+ return ProtocolOptions2;
908
+ })(ProtocolOptions || {});
909
+ var BackendChoice = /* @__PURE__ */ ((BackendChoice2) => {
910
+ BackendChoice2["Local"] = "On my local machine";
911
+ BackendChoice2["Experitest"] = "In the cloud using Experitest";
912
+ BackendChoice2["Saucelabs"] = "In the cloud using Sauce Labs";
913
+ BackendChoice2["Browserstack"] = "In the cloud using BrowserStack";
914
+ BackendChoice2["OtherVendors"] = "In the cloud using Testingbot or LambdaTest or a different service";
915
+ BackendChoice2["Grid"] = "I have my own Selenium cloud";
916
+ return BackendChoice2;
917
+ })(BackendChoice || {});
918
+ var RegionOptions = /* @__PURE__ */ ((RegionOptions2) => {
919
+ RegionOptions2["US"] = "us";
920
+ RegionOptions2["EU"] = "eu";
921
+ return RegionOptions2;
922
+ })(RegionOptions || {});
923
+ var ElectronBuildToolChoice = /* @__PURE__ */ ((ElectronBuildToolChoice2) => {
924
+ ElectronBuildToolChoice2["ElectronForge"] = "Electron Forge (https://www.electronforge.io/)";
925
+ ElectronBuildToolChoice2["ElectronBuilder"] = "electron-builder (https://www.electron.build/)";
926
+ ElectronBuildToolChoice2["SomethingElse"] = "Something else";
927
+ return ElectronBuildToolChoice2;
928
+ })(ElectronBuildToolChoice || {});
929
+ var SUPPORTED_BROWSER_RUNNER_PRESETS = [
930
+ { name: "Lit (https://lit.dev/)", value: "$--$" },
931
+ { name: "Vue.js (https://vuejs.org/)", value: "@vitejs/plugin-vue$--$vue" },
932
+ { name: "Svelte (https://svelte.dev/)", value: "@sveltejs/vite-plugin-svelte$--$svelte" },
933
+ { name: "SolidJS (https://www.solidjs.com/)", value: "vite-plugin-solid$--$solid" },
934
+ { name: "StencilJS (https://stenciljs.com/)", value: "$--$stencil" },
935
+ { name: "React (https://reactjs.org/)", value: "@vitejs/plugin-react$--$react" },
936
+ { name: "Preact (https://preactjs.com/)", value: "@preact/preset-vite$--$preact" },
937
+ { name: "Other", value: null }
938
+ ];
939
+ function isBrowserRunner(answers) {
940
+ return answers.runner === SUPPORTED_PACKAGES.runner[1].value;
941
+ }
942
+ function getTestingPurpose(answers) {
943
+ return convertPackageHashToObject(answers.runner).purpose;
944
+ }
945
+ var TESTING_LIBRARY_PACKAGES = {
946
+ react: "@testing-library/react",
947
+ preact: "@testing-library/preact",
948
+ vue: "@testing-library/vue",
949
+ svelte: "@testing-library/svelte",
950
+ solid: "solid-testing-library"
951
+ };
952
+ var E2E_ENVIRONMENTS = [
953
+ { name: "Web - web applications in the browser", value: "web" },
954
+ { name: "Mobile - native, hybrid and mobile web apps, on Android or iOS", value: "mobile" }
955
+ ];
956
+ var MOBILE_ENVIRONMENTS = [
957
+ { 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" },
958
+ { name: "iOS - applications on iOS, iPadOS, and tvOS\n > using XCTest (https://appium.github.io/appium-xcuitest-driver)", value: "ios" }
959
+ ];
960
+ var BROWSER_ENVIRONMENTS = [
961
+ { name: "Chrome", value: "chrome", checked: true },
962
+ { name: "Firefox", value: "firefox" },
963
+ { name: "Safari", value: "safari" },
964
+ { name: "Microsoft Edge", value: "MicrosoftEdge" }
965
+ ];
966
+ function selectDefaultService(serviceNames) {
967
+ serviceNames = Array.isArray(serviceNames) ? serviceNames : [serviceNames];
968
+ return SUPPORTED_PACKAGES.service.filter(({ name }) => serviceNames.includes(name)).map(({ value }) => value);
969
+ }
970
+ function prioServiceOrderFor(serviceNamesParam) {
971
+ const serviceNames = Array.isArray(serviceNamesParam) ? serviceNamesParam : [serviceNamesParam];
972
+ let services = SUPPORTED_PACKAGES.service;
973
+ for (const serviceName of serviceNames) {
974
+ const index = services.findIndex(({ name }) => name === serviceName);
975
+ services = [services[index], ...services.slice(0, index), ...services.slice(index + 1)];
976
+ }
977
+ return services;
978
+ }
979
+ var QUESTIONNAIRE = [{
980
+ type: "list",
981
+ name: "runner",
982
+ message: "What type of testing would you like to do?",
983
+ choices: SUPPORTED_PACKAGES.runner
984
+ }, {
985
+ type: "list",
986
+ name: "preset",
987
+ message: "Which framework do you use for building components?",
988
+ choices: SUPPORTED_BROWSER_RUNNER_PRESETS,
989
+ // only ask if there are more than 1 runner to pick from
990
+ when: (
991
+ /* istanbul ignore next */
992
+ isBrowserRunner
993
+ )
994
+ }, {
995
+ type: "confirm",
996
+ name: "installTestingLibrary",
997
+ message: "Do you like to use Testing Library (https://testing-library.com/) as test utility?",
998
+ default: true,
999
+ // only ask if there are more than 1 runner to pick from
1000
+ when: (
1001
+ /* istanbul ignore next */
1002
+ (answers) => isBrowserRunner(answers) && /**
1003
+ * Only show if Testing Library has an add-on for framework
1004
+ */
1005
+ answers.preset && TESTING_LIBRARY_PACKAGES[convertPackageHashToObject(answers.preset).short]
1006
+ )
1007
+ }, {
1008
+ type: "list",
1009
+ name: "electronBuildTool",
1010
+ message: "Which tool are you using to build your Electron app?",
1011
+ choices: Object.values(ElectronBuildToolChoice),
1012
+ when: (
1013
+ /* instanbul ignore next */
1014
+ (answers) => getTestingPurpose(answers) === "electron"
1015
+ )
1016
+ }, {
1017
+ type: "input",
1018
+ name: "electronAppBinaryPath",
1019
+ message: "What is the path to the binary of your built Electron app?",
1020
+ when: (
1021
+ /* istanbul ignore next */
1022
+ (answers) => getTestingPurpose(answers) === "electron" && answers.electronBuildTool === "Something else" /* SomethingElse */
1023
+ )
1024
+ }, {
1025
+ type: "list",
1026
+ name: "backend",
1027
+ message: "Where is your automation backend located?",
1028
+ choices: Object.values(BackendChoice),
1029
+ when: (
1030
+ /* instanbul ignore next */
1031
+ (answers) => getTestingPurpose(answers) === "e2e"
1032
+ )
1033
+ }, {
1034
+ type: "list",
1035
+ name: "e2eEnvironment",
1036
+ message: "Which environment you would like to automate?",
1037
+ choices: E2E_ENVIRONMENTS,
1038
+ default: "web",
1039
+ when: (
1040
+ /* istanbul ignore next */
1041
+ (answers) => getTestingPurpose(answers) === "e2e"
1042
+ )
1043
+ }, {
1044
+ type: "list",
1045
+ name: "mobileEnvironment",
1046
+ message: "Which mobile environment you'd like to automate?",
1047
+ choices: MOBILE_ENVIRONMENTS,
1048
+ when: (
1049
+ /* instanbul ignore next */
1050
+ (answers) => getTestingPurpose(answers) === "e2e" && answers.e2eEnvironment === "mobile"
1051
+ )
1052
+ }, {
1053
+ type: "checkbox",
1054
+ name: "browserEnvironment",
1055
+ message: "With which browser should we start?",
1056
+ choices: BROWSER_ENVIRONMENTS,
1057
+ when: (
1058
+ /* instanbul ignore next */
1059
+ (answers) => getTestingPurpose(answers) === "e2e" && answers.e2eEnvironment === "web"
1060
+ )
1061
+ }, {
1062
+ type: "input",
1063
+ name: "hostname",
1064
+ message: "What is the host address of that cloud service?",
1065
+ when: (
1066
+ /* istanbul ignore next */
1067
+ (answers) => answers.backend && answers.backend.indexOf("different service") > -1
1068
+ )
1069
+ }, {
1070
+ type: "input",
1071
+ name: "port",
1072
+ message: "What is the port on which that service is running?",
1073
+ default: "80",
1074
+ when: (
1075
+ /* istanbul ignore next */
1076
+ (answers) => answers.backend && answers.backend.indexOf("different service") > -1
1077
+ )
1078
+ }, {
1079
+ type: "input",
1080
+ name: "expEnvAccessKey",
1081
+ message: "Access key from Experitest Cloud",
1082
+ default: "EXPERITEST_ACCESS_KEY",
1083
+ when: (
1084
+ /* istanbul ignore next */
1085
+ (answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
1086
+ )
1087
+ }, {
1088
+ type: "input",
1089
+ name: "expEnvHostname",
1090
+ message: "Environment variable for cloud url",
1091
+ default: "example.experitest.com",
1092
+ when: (
1093
+ /* istanbul ignore next */
1094
+ (answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
1095
+ )
1096
+ }, {
1097
+ type: "input",
1098
+ name: "expEnvPort",
1099
+ message: "Environment variable for port",
1100
+ default: "443",
1101
+ when: (
1102
+ /* istanbul ignore next */
1103
+ (answers) => answers.backend === "In the cloud using Experitest" /* Experitest */
1104
+ )
1105
+ }, {
1106
+ type: "list",
1107
+ name: "expEnvProtocol",
1108
+ message: "Choose a protocol for environment variable",
1109
+ default: "https" /* HTTPS */,
1110
+ choices: Object.values(ProtocolOptions),
1111
+ when: (
1112
+ /* istanbul ignore next */
1113
+ (answers) => answers.backend === "In the cloud using Experitest" /* Experitest */ && answers.expEnvPort !== "80" && answers.expEnvPort !== "443"
1114
+ )
1115
+ }, {
1116
+ type: "input",
1117
+ name: "env_user",
1118
+ message: "Environment variable for username",
1119
+ default: "LT_USERNAME",
1120
+ when: (
1121
+ /* istanbul ignore next */
1122
+ (answers) => answers.backend && answers.backend.indexOf("LambdaTest") > -1 && answers.hostname.indexOf("lambdatest.com") > -1
1123
+ )
1124
+ }, {
1125
+ type: "input",
1126
+ name: "env_key",
1127
+ message: "Environment variable for access key",
1128
+ default: "LT_ACCESS_KEY",
1129
+ when: (
1130
+ /* istanbul ignore next */
1131
+ (answers) => answers.backend && answers.backend.indexOf("LambdaTest") > -1 && answers.hostname.indexOf("lambdatest.com") > -1
1132
+ )
1133
+ }, {
1134
+ type: "input",
1135
+ name: "env_user",
1136
+ message: "Environment variable for username",
1137
+ default: "BROWSERSTACK_USERNAME",
1138
+ when: (
1139
+ /* istanbul ignore next */
1140
+ (answers) => answers.backend === "In the cloud using BrowserStack" /* Browserstack */
1141
+ )
1142
+ }, {
1143
+ type: "input",
1144
+ name: "env_key",
1145
+ message: "Environment variable for access key",
1146
+ default: "BROWSERSTACK_ACCESS_KEY",
1147
+ when: (
1148
+ /* istanbul ignore next */
1149
+ (answers) => answers.backend === "In the cloud using BrowserStack" /* Browserstack */
1150
+ )
1151
+ }, {
1152
+ type: "input",
1153
+ name: "env_user",
1154
+ message: "Environment variable for username",
1155
+ default: "SAUCE_USERNAME",
1156
+ when: (
1157
+ /* istanbul ignore next */
1158
+ (answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
1159
+ )
1160
+ }, {
1161
+ type: "input",
1162
+ name: "env_key",
1163
+ message: "Environment variable for access key",
1164
+ default: "SAUCE_ACCESS_KEY",
1165
+ when: (
1166
+ /* istanbul ignore next */
1167
+ (answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
1168
+ )
1169
+ }, {
1170
+ type: "list",
1171
+ name: "region",
1172
+ message: "In which region do you want to run your Sauce Labs tests in?",
1173
+ choices: Object.values(RegionOptions),
1174
+ when: (
1175
+ /* istanbul ignore next */
1176
+ (answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */
1177
+ )
1178
+ }, {
1179
+ type: "confirm",
1180
+ name: "useSauceConnect",
1181
+ 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",
1182
+ default: isNuxtProject,
1183
+ when: (
1184
+ /* istanbul ignore next */
1185
+ (answers) => answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */ && !isNuxtProject
1186
+ )
1187
+ }, {
1188
+ type: "input",
1189
+ name: "hostname",
1190
+ message: "What is the IP or URI to your Selenium standalone or grid server?",
1191
+ default: "localhost",
1192
+ when: (
1193
+ /* istanbul ignore next */
1194
+ (answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
1195
+ )
1196
+ }, {
1197
+ type: "input",
1198
+ name: "port",
1199
+ message: "What is the port which your Selenium standalone or grid server is running on?",
1200
+ default: "4444",
1201
+ when: (
1202
+ /* istanbul ignore next */
1203
+ (answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
1204
+ )
1205
+ }, {
1206
+ type: "input",
1207
+ name: "path",
1208
+ message: "What is the path to your browser driver or grid server?",
1209
+ default: "/",
1210
+ when: (
1211
+ /* istanbul ignore next */
1212
+ (answers) => answers.backend && answers.backend.toString().indexOf("own Selenium cloud") > -1
1213
+ )
1214
+ }, {
1215
+ type: "list",
1216
+ name: "framework",
1217
+ message: "Which framework do you want to use?",
1218
+ choices: (
1219
+ /* instanbul ignore next */
1220
+ (answers) => {
1221
+ if (isBrowserRunner(answers)) {
1222
+ return SUPPORTED_PACKAGES.framework.slice(0, 1);
1223
+ }
1224
+ if (getTestingPurpose(answers) === "electron") {
1225
+ return SUPPORTED_PACKAGES.framework.filter(
1226
+ ({ value }) => !value.startsWith("@serenity-js")
1227
+ );
1228
+ }
1229
+ return SUPPORTED_PACKAGES.framework;
1230
+ }
1231
+ )
1232
+ }, {
1233
+ type: "confirm",
1234
+ name: "isUsingTypeScript",
1235
+ message: "Do you want to use Typescript to write tests?",
1236
+ when: (
1237
+ /* istanbul ignore next */
1238
+ (answers) => {
1239
+ if (answers.preset?.includes("stencil")) {
1240
+ return false;
1241
+ }
1242
+ return true;
1243
+ }
1244
+ ),
1245
+ default: (
1246
+ /* istanbul ignore next */
1247
+ (answers) => answers.preset?.includes("stencil") || detectCompiler(answers)
1248
+ )
1249
+ }, {
1250
+ type: "confirm",
1251
+ name: "generateTestFiles",
1252
+ message: "Do you want WebdriverIO to autogenerate some test files?",
1253
+ default: true,
1254
+ when: (
1255
+ /* istanbul ignore next */
1256
+ (answers) => {
1257
+ if (["vscode", "electron", "macos"].includes(getTestingPurpose(answers)) && answers.framework.includes("cucumber")) {
1258
+ return false;
1259
+ }
1260
+ return true;
1261
+ }
1262
+ )
1263
+ }, {
1264
+ type: "input",
1265
+ name: "specs",
1266
+ message: "What should be the location of your spec files?",
1267
+ default: (
1268
+ /* istanbul ignore next */
1269
+ (answers) => {
1270
+ const pattern = isBrowserRunner(answers) ? "src/**/*.test" : "test/specs/**/*";
1271
+ return getDefaultFiles(answers, pattern);
1272
+ }
1273
+ ),
1274
+ when: (
1275
+ /* istanbul ignore next */
1276
+ (answers) => answers.generateTestFiles && /(mocha|jasmine)/.test(answers.framework)
1277
+ )
1278
+ }, {
1279
+ type: "input",
1280
+ name: "specs",
1281
+ message: "What should be the location of your feature files?",
1282
+ default: (answers) => getDefaultFiles(answers, "features/**/*.feature"),
1283
+ when: (
1284
+ /* istanbul ignore next */
1285
+ (answers) => answers.generateTestFiles && answers.framework.includes("cucumber")
1286
+ )
1287
+ }, {
1288
+ type: "input",
1289
+ name: "stepDefinitions",
1290
+ message: "What should be the location of your step definitions?",
1291
+ default: (answers) => getDefaultFiles(answers, "features/step-definitions/steps"),
1292
+ when: (
1293
+ /* istanbul ignore next */
1294
+ (answers) => answers.generateTestFiles && answers.framework.includes("cucumber")
1295
+ )
1296
+ }, {
1297
+ type: "confirm",
1298
+ name: "usePageObjects",
1299
+ message: "Do you want to use page objects (https://martinfowler.com/bliki/PageObject.html)?",
1300
+ default: true,
1301
+ when: (
1302
+ /* istanbul ignore next */
1303
+ (answers) => answers.generateTestFiles && /**
1304
+ * page objects aren't common for component testing
1305
+ */
1306
+ !isBrowserRunner(answers) && /**
1307
+ * and also not needed when running VS Code tests since the service comes with
1308
+ * its own page object implementation, nor when running Electron or MacOS tests
1309
+ */
1310
+ !["vscode", "electron", "macos"].includes(getTestingPurpose(answers)) && /**
1311
+ * Serenity/JS generates Lean Page Objects by default, so there's no need to ask about it
1312
+ * See https://serenity-js.org/handbook/web-testing/page-objects-pattern/
1313
+ */
1314
+ !usesSerenity(answers)
1315
+ )
1316
+ }, {
1317
+ type: "input",
1318
+ name: "pages",
1319
+ message: "Where are your page objects located?",
1320
+ default: (
1321
+ /* istanbul ignore next */
1322
+ (answers) => answers.framework.match(/(mocha|jasmine)/) ? getDefaultFiles(answers, "test/pageobjects/**/*") : getDefaultFiles(answers, "features/pageobjects/**/*")
1323
+ ),
1324
+ when: (
1325
+ /* istanbul ignore next */
1326
+ (answers) => answers.generateTestFiles && answers.usePageObjects
1327
+ )
1328
+ }, {
1329
+ type: "input",
1330
+ name: "serenityLibPath",
1331
+ message: "What should be the location of your Serenity/JS Screenplay Pattern library?",
1332
+ default: (
1333
+ /* istanbul ignore next */
1334
+ async (answers) => {
1335
+ const projectRootDir = await getProjectRoot(answers);
1336
+ const specsDir = path2.resolve(projectRootDir, path2.dirname(answers.specs || "").replace(/\*\*$/, ""));
1337
+ return path2.resolve(specsDir, "..", "serenity");
1338
+ }
1339
+ ),
1340
+ when: (
1341
+ /* istanbul ignore next */
1342
+ (answers) => answers.generateTestFiles && usesSerenity(answers)
1343
+ )
1344
+ }, {
1345
+ type: "checkbox",
1346
+ name: "reporters",
1347
+ message: "Which reporter do you want to use?",
1348
+ choices: SUPPORTED_PACKAGES.reporter
1349
+ }, {
1350
+ type: "checkbox",
1351
+ name: "plugins",
1352
+ message: "Do you want to add a plugin to your test setup?",
1353
+ choices: SUPPORTED_PACKAGES.plugin,
1354
+ default: []
1355
+ }, {
1356
+ type: "confirm",
1357
+ name: "includeVisualTesting",
1358
+ message: "Would you like to include Visual Testing to your setup? For more information see https://webdriver.io/docs/visual-testing!",
1359
+ default: false,
1360
+ when: (
1361
+ /* istanbul ignore next */
1362
+ (answers) => {
1363
+ return ["e2e", "component"].includes(getTestingPurpose(answers));
1364
+ }
1365
+ )
1366
+ }, {
1367
+ type: "checkbox",
1368
+ name: "services",
1369
+ message: "Do you want to add a service to your test setup?",
1370
+ choices: (answers) => {
1371
+ const services = [];
1372
+ if (answers.backend === "In the cloud using BrowserStack" /* Browserstack */) {
1373
+ services.push("browserstack");
1374
+ } else if (answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */) {
1375
+ services.push("sauce");
1376
+ }
1377
+ if (answers.e2eEnvironment === "mobile") {
1378
+ services.push("appium");
1379
+ }
1380
+ if (getTestingPurpose(answers) === "e2e" && isNuxtProject) {
1381
+ services.push("nuxt");
1382
+ }
1383
+ if (getTestingPurpose(answers) === "vscode") {
1384
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "vscode")];
1385
+ } else if (getTestingPurpose(answers) === "electron") {
1386
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "electron")];
1387
+ } else if (getTestingPurpose(answers) === "macos") {
1388
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "appium")];
1389
+ } else if (getTestingPurpose(answers) === "roku") {
1390
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === "roku")];
1391
+ }
1392
+ return prioServiceOrderFor(services);
1393
+ },
1394
+ default: (answers) => {
1395
+ const defaultServices = [];
1396
+ if (answers.backend === "In the cloud using BrowserStack" /* Browserstack */) {
1397
+ defaultServices.push("browserstack");
1398
+ } else if (answers.backend === "In the cloud using Sauce Labs" /* Saucelabs */) {
1399
+ defaultServices.push("sauce");
1400
+ }
1401
+ if (answers.e2eEnvironment === "mobile" || getTestingPurpose(answers) === "macos") {
1402
+ defaultServices.push("appium");
1403
+ }
1404
+ if (getTestingPurpose(answers) === "vscode") {
1405
+ defaultServices.push("vscode");
1406
+ } else if (getTestingPurpose(answers) === "electron") {
1407
+ defaultServices.push("electron");
1408
+ } else if (getTestingPurpose(answers) === "roku") {
1409
+ defaultServices.push("roku");
1410
+ }
1411
+ if (isNuxtProject) {
1412
+ defaultServices.push("nuxt");
1413
+ }
1414
+ if (answers.includeVisualTesting) {
1415
+ defaultServices.push("visual");
1416
+ }
1417
+ return selectDefaultService(defaultServices);
1418
+ }
1419
+ }, {
1420
+ type: "input",
1421
+ name: "outputDir",
1422
+ message: "In which directory should the xunit reports get stored?",
1423
+ default: "./",
1424
+ when: (
1425
+ /* istanbul ignore next */
1426
+ (answers) => answers.reporters.includes("junit")
1427
+ )
1428
+ }, {
1429
+ type: "input",
1430
+ name: "outputDir",
1431
+ message: "In which directory should the json reports get stored?",
1432
+ default: "./",
1433
+ when: (
1434
+ /* istanbul ignore next */
1435
+ (answers) => answers.reporters.includes("json")
1436
+ )
1437
+ }, {
1438
+ type: "input",
1439
+ name: "outputDir",
1440
+ message: "In which directory should the mochawesome json reports get stored?",
1441
+ default: "./",
1442
+ when: (
1443
+ /* istanbul ignore next */
1444
+ (answers) => answers.reporters.includes("mochawesome")
1445
+ )
1446
+ }, {
1447
+ type: "confirm",
1448
+ name: "npmInstall",
1449
+ message: () => `Do you want me to run \`${detectPackageManager()} install\``,
1450
+ default: true
1451
+ }];
1452
+ var COMMUNITY_PACKAGES_WITH_TS_SUPPORT = [
1453
+ "wdio-electron-service",
1454
+ "wdio-vscode-service",
1455
+ "wdio-nuxt-service",
1456
+ "wdio-vite-service",
1457
+ "wdio-gmail-service",
1458
+ "wdio-roku-service",
1459
+ "wdio-obsidian-service"
1460
+ ];
1461
+ var DEPENDENCIES_INSTALLATION_MESSAGE = `
1462
+ To install dependencies, execute:
1463
+ %s
1464
+ `;
1465
+
1466
+ // src/cli/utils.ts
1467
+ import path3 from "node:path";
1468
+ import fs3 from "node:fs/promises";
1469
+ import inquirer2 from "inquirer";
1470
+ async function runConfigCommand(parsedAnswers, npmTag) {
1471
+ console.log("\n");
1472
+ await createPackageJSON(parsedAnswers);
1473
+ await setupTypeScript(parsedAnswers);
1474
+ await npmInstall(parsedAnswers, npmTag);
1475
+ await createWDIOConfig(parsedAnswers);
1476
+ await createWDIOScript(parsedAnswers);
1477
+ console.log(
1478
+ configHelperSuccessMessage({
1479
+ projectRootDir: parsedAnswers.projectRootDir,
1480
+ runScript: parsedAnswers.serenityAdapter ? "serenity" : "wdio",
1481
+ extraInfo: parsedAnswers.serenityAdapter ? CONFIG_HELPER_SERENITY_BANNER : ""
1482
+ })
1483
+ );
1484
+ await runAppiumInstaller(parsedAnswers);
1485
+ }
1486
+ async function missingConfigurationPrompt(command2, configPath, runConfigCmd = runConfigCommand) {
1487
+ const message = `Could not execute "${command2}" due to missing configuration, file "${path3.parse(configPath).name}[.js/.ts]" not found! Would you like to create one?`;
1488
+ const { config } = await inquirer2.prompt({
1489
+ type: "confirm",
1490
+ name: "config",
1491
+ message,
1492
+ default: false
1493
+ });
1494
+ if (!config) {
1495
+ console.log(`No WebdriverIO configuration found in "${process.cwd()}"`);
1496
+ if (!process.env.WDIO_UNIT_TESTS) {
1497
+ process.exit(0);
1498
+ }
1499
+ return configPath;
1500
+ }
1501
+ const parsedAnswers = await parseAnswers(false);
1502
+ await runConfigCmd(parsedAnswers, "latest");
1503
+ return configPath;
1504
+ }
1505
+ var parseAnswers = async function(yes) {
1506
+ console.log(CONFIG_HELPER_INTRO);
1507
+ const answers = await getAnswers(yes);
1508
+ const frameworkPackage = convertPackageHashToObject(answers.framework);
1509
+ const runnerPackage = convertPackageHashToObject(answers.runner || SUPPORTED_PACKAGES.runner[0].value);
1510
+ const servicePackages = answers.services.map((service) => convertPackageHashToObject(service));
1511
+ const pluginPackages = answers.plugins.map((plugin) => convertPackageHashToObject(plugin));
1512
+ const serenityPackages = getSerenityPackages(answers);
1513
+ const reporterPackages = answers.reporters.map((reporter) => convertPackageHashToObject(reporter));
1514
+ const presetPackage = convertPackageHashToObject(answers.preset || "");
1515
+ const projectProps = await getProjectProps(process.cwd());
1516
+ const projectRootDir = await getProjectRoot(answers);
1517
+ const packagesToInstall = [
1518
+ runnerPackage.package,
1519
+ frameworkPackage.package,
1520
+ presetPackage.package,
1521
+ ...reporterPackages.map((reporter) => reporter.package),
1522
+ ...pluginPackages.map((plugin) => plugin.package),
1523
+ ...servicePackages.map((service) => service.package),
1524
+ ...serenityPackages
1525
+ ].filter(Boolean);
1526
+ const hasRootTSConfig = await fs3.access(path3.resolve(projectRootDir, "tsconfig.json")).then(() => true, () => false);
1527
+ const tsConfigFilePath = !hasRootTSConfig ? path3.resolve(projectRootDir, "tsconfig.json") : answers.specs ? path3.resolve(
1528
+ path3.dirname(answers.specs.split(path3.sep).filter((s) => !s.includes("*")).join(path3.sep)),
1529
+ "tsconfig.json"
1530
+ ) : path3.resolve(projectRootDir, `tsconfig.${runnerPackage.short === "local" ? "e2e" : "wdio"}.json`);
1531
+ const parsedPaths = getPathForFileGeneration(answers, projectRootDir);
1532
+ const isUsingTypeScript = answers.isUsingTypeScript;
1533
+ const wdioConfigFilename = `wdio.conf.${isUsingTypeScript ? "ts" : "js"}`;
1534
+ const wdioConfigPath = path3.resolve(projectRootDir, wdioConfigFilename);
1535
+ return {
1536
+ projectName: projectProps?.packageJson.name || "Test Suite",
1537
+ // default values required in templates
1538
+ ...{
1539
+ usePageObjects: false,
1540
+ installTestingLibrary: false
1541
+ },
1542
+ ...answers,
1543
+ useSauceConnect: isNuxtProject || answers.useSauceConnect,
1544
+ rawAnswers: answers,
1545
+ runner: runnerPackage.short,
1546
+ preset: presetPackage.short,
1547
+ framework: frameworkPackage.short,
1548
+ purpose: runnerPackage.purpose,
1549
+ serenityAdapter: frameworkPackage.package === "@serenity-js/webdriverio" && frameworkPackage.purpose,
1550
+ reporters: reporterPackages.map(({ short }) => short),
1551
+ plugins: pluginPackages.map(({ short }) => short),
1552
+ services: servicePackages.map(({ short }) => short),
1553
+ specs: answers.specs && `./${path3.relative(projectRootDir, answers.specs).replaceAll(path3.sep, "/")}`,
1554
+ stepDefinitions: answers.stepDefinitions && `./${path3.relative(projectRootDir, answers.stepDefinitions).replaceAll(path3.sep, "/")}`,
1555
+ packagesToInstall,
1556
+ isUsingTypeScript,
1557
+ esmSupport: projectProps && !projectProps.esmSupported ? false : true,
1558
+ isSync: false,
1559
+ _async: "async ",
1560
+ _await: "await ",
1561
+ projectRootDir,
1562
+ destSpecRootPath: parsedPaths.destSpecRootPath,
1563
+ destStepRootPath: parsedPaths.destStepRootPath,
1564
+ destPageObjectRootPath: parsedPaths.destPageObjectRootPath,
1565
+ destSerenityLibRootPath: parsedPaths.destSerenityLibRootPath,
1566
+ relativePath: parsedPaths.relativePath,
1567
+ hasRootTSConfig,
1568
+ tsConfigFilePath,
1569
+ tsProject: `./${path3.relative(projectRootDir, tsConfigFilePath).replaceAll(path3.sep, "/")}`,
1570
+ wdioConfigPath
1571
+ };
1572
+ };
1573
+ async function canAccessConfigPath(configPathNoExtension, configPath) {
1574
+ return new Promise((resolve, reject) => {
1575
+ if (configPath) {
1576
+ fs3.access(configPath).then(() => resolve(configPath), reject);
1577
+ } else {
1578
+ reject();
1579
+ }
1580
+ }).catch(() => Promise.all(SUPPORTED_CONFIG_FILE_EXTENSION.map(async (supportedExtension) => {
1581
+ const configPathWithExtension = `${configPathNoExtension}.${supportedExtension}`;
1582
+ return fs3.access(configPathWithExtension).then(() => configPathWithExtension, () => void 0);
1583
+ })).then(
1584
+ (configFilePaths) => configFilePaths.find(Boolean),
1585
+ () => void 0
1586
+ ));
1587
+ }
1588
+
1589
+ // src/cli/config.ts
1590
+ var hasYarnLock = false;
1591
+ try {
1592
+ fss.accessSync("yarn.lock");
1593
+ hasYarnLock = true;
1594
+ } catch {
1595
+ hasYarnLock = false;
1596
+ }
1597
+ var command = "config";
1598
+ var desc = "Initialize WebdriverIO and setup configuration in your current project.";
1599
+ var cmdArgs = {
1600
+ yarn: {
1601
+ type: "boolean",
1602
+ desc: "Install packages via Yarn package manager.",
1603
+ default: hasYarnLock
1604
+ },
1605
+ yes: {
1606
+ alias: "y",
1607
+ desc: "will fill in all config defaults without prompting",
1608
+ type: "boolean",
1609
+ default: false
1610
+ },
1611
+ npmTag: {
1612
+ alias: "t",
1613
+ desc: "define NPM tag to use for WebdriverIO related packages",
1614
+ type: "string",
1615
+ default: "latest"
1616
+ }
1617
+ };
1618
+ var builder = (yargs) => {
1619
+ return yargs.options(cmdArgs).epilogue(CLI_EPILOGUE).help();
1620
+ };
1621
+ async function handler(argv, runConfigCmd = runConfigCommand) {
1622
+ const parsedAnswers = await parseAnswers(argv.yes);
1623
+ await runConfigCmd(parsedAnswers, argv.npmTag);
1624
+ return {
1625
+ success: true,
1626
+ parsedAnswers,
1627
+ installedPackages: parsedAnswers.packagesToInstall.map((pkg) => pkg.split("--")[0])
1628
+ };
1629
+ }
1630
+ export {
1631
+ config_exports as config
1632
+ };