create-wdio 9.32.0 → 10.0.0-alpha.149
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.
- package/AGENTS.md +40 -0
- package/README.md +8 -0
- package/build/answerFlags.d.ts +68 -0
- package/build/answerFlags.d.ts.map +1 -0
- package/build/cli/agent.d.ts +17 -0
- package/build/cli/agent.d.ts.map +1 -0
- package/build/cli/config.d.ts +4 -4
- package/build/cli/config.d.ts.map +1 -1
- package/build/cli/utils.d.ts +3 -3
- package/build/cli/utils.d.ts.map +1 -1
- package/build/config/cli.js +534 -67
- package/build/constants.d.ts +7 -0
- package/build/constants.d.ts.map +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +380 -13
- package/build/install/cli.js +471 -70
- package/build/templates/snippets/cucumber.ejs +1 -1
- package/build/templates/snippets/services.ejs +9 -0
- package/build/types.d.ts +10 -2
- package/build/types.d.ts.map +1 -1
- package/build/utils/index.js +269 -7
- package/build/utils.d.ts +16 -4
- package/build/utils.d.ts.map +1 -1
- package/package.json +5 -6
- package/templates/wdio-session/SKILL.md +96 -0
package/build/config/cli.js
CHANGED
|
@@ -24,7 +24,6 @@ import path from "node:path";
|
|
|
24
24
|
import util, { promisify } from "node:util";
|
|
25
25
|
import fs from "node:fs/promises";
|
|
26
26
|
import { execSync } from "node:child_process";
|
|
27
|
-
import readDir from "recursive-readdir";
|
|
28
27
|
import { $ } from "execa";
|
|
29
28
|
import { readPackageUp } from "read-pkg-up";
|
|
30
29
|
|
|
@@ -124,17 +123,380 @@ function getInstallCommand(pm, packages, dev) {
|
|
|
124
123
|
return `${pm} ${installCommand[pm]} ${packages.join(" ")} ${devParam}`;
|
|
125
124
|
}
|
|
126
125
|
|
|
126
|
+
// src/answerFlags.ts
|
|
127
|
+
var AnswerFlagError = class extends Error {
|
|
128
|
+
name = "AnswerFlagError";
|
|
129
|
+
};
|
|
130
|
+
var HASH = "$--$";
|
|
131
|
+
function packageChoices(packages, key = (short) => short) {
|
|
132
|
+
return () => Object.fromEntries(packages().map(({ value }) => {
|
|
133
|
+
const [pkg, short, purpose] = value.split(HASH);
|
|
134
|
+
return [key(short, purpose, pkg), value];
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
var ANSWER_FLAGS = [{
|
|
138
|
+
name: "runner",
|
|
139
|
+
answer: "runner",
|
|
140
|
+
type: "string",
|
|
141
|
+
desc: "Type of testing",
|
|
142
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.runner, (_, purpose) => purpose)
|
|
143
|
+
}, {
|
|
144
|
+
name: "preset",
|
|
145
|
+
answer: "preset",
|
|
146
|
+
type: "string",
|
|
147
|
+
desc: "UI framework of your components (with --runner component)",
|
|
148
|
+
choices: () => Object.fromEntries(SUPPORTED_BROWSER_RUNNER_PRESETS.map(({ name, value }) => [
|
|
149
|
+
value === null ? "other" : value.split(HASH)[1] || name.split(" ")[0].toLowerCase(),
|
|
150
|
+
value
|
|
151
|
+
]))
|
|
152
|
+
}, {
|
|
153
|
+
name: "testingLibrary",
|
|
154
|
+
answer: "installTestingLibrary",
|
|
155
|
+
type: "boolean",
|
|
156
|
+
desc: "Install Testing Library (with --preset vue, svelte, solid, react or preact)"
|
|
157
|
+
}, {
|
|
158
|
+
name: "desktopFramework",
|
|
159
|
+
answer: "desktopFramework",
|
|
160
|
+
type: "string",
|
|
161
|
+
desc: "Desktop app framework (with --runner desktop)",
|
|
162
|
+
choices: () => ({
|
|
163
|
+
electron: "Electron (https://www.electronjs.org/)" /* Electron */,
|
|
164
|
+
tauri: "Tauri (https://tauri.app/)" /* Tauri */,
|
|
165
|
+
dioxus: "Dioxus (https://dioxuslabs.com/)" /* Dioxus */,
|
|
166
|
+
macos: "Native macOS app" /* MacOS */
|
|
167
|
+
})
|
|
168
|
+
}, {
|
|
169
|
+
name: "electronBuildTool",
|
|
170
|
+
answer: "electronBuildTool",
|
|
171
|
+
type: "string",
|
|
172
|
+
desc: "Tool that builds your Electron app (with --desktop-framework electron)",
|
|
173
|
+
choices: () => ({
|
|
174
|
+
forge: "Electron Forge (https://www.electronforge.io/)" /* ElectronForge */,
|
|
175
|
+
builder: "electron-builder (https://www.electron.build/)" /* ElectronBuilder */,
|
|
176
|
+
other: "Something else" /* SomethingElse */
|
|
177
|
+
})
|
|
178
|
+
}, {
|
|
179
|
+
name: "electronAppBinaryPath",
|
|
180
|
+
answer: "electronAppBinaryPath",
|
|
181
|
+
type: "string",
|
|
182
|
+
desc: "Path to the built Electron app (with --electron-build-tool other)"
|
|
183
|
+
}, {
|
|
184
|
+
name: "tauriDriverProvider",
|
|
185
|
+
answer: "tauriDriverProvider",
|
|
186
|
+
type: "string",
|
|
187
|
+
desc: "WebDriver provider for Tauri (with --desktop-framework tauri)",
|
|
188
|
+
choices: () => ({
|
|
189
|
+
official: "Official tauri-driver (cross-platform, Cargo-installed)" /* Official */,
|
|
190
|
+
crabnebula: "CrabNebula tauri-driver (managed cloud / drop-in)" /* CrabNebula */,
|
|
191
|
+
embedded: "Embedded driver via tauri-plugin-wdio-webdriver crate" /* Embedded */
|
|
192
|
+
})
|
|
193
|
+
}, {
|
|
194
|
+
name: "tauriFrontendPlugin",
|
|
195
|
+
answer: "tauriUseFrontendPlugin",
|
|
196
|
+
type: "boolean",
|
|
197
|
+
desc: "Install @wdio/tauri-plugin (with --desktop-framework tauri)"
|
|
198
|
+
}, {
|
|
199
|
+
name: "tauriAppBinaryPath",
|
|
200
|
+
answer: "tauriAppBinaryPath",
|
|
201
|
+
type: "string",
|
|
202
|
+
desc: "Path to the built Tauri binary (with --desktop-framework tauri)"
|
|
203
|
+
}, {
|
|
204
|
+
name: "dioxusDriverProvider",
|
|
205
|
+
answer: "dioxusDriverProvider",
|
|
206
|
+
type: "string",
|
|
207
|
+
desc: "WebDriver provider for Dioxus (with --desktop-framework dioxus)",
|
|
208
|
+
choices: () => ({
|
|
209
|
+
embedded: "Embedded in-process driver (recommended \u2014 no external driver, all platforms)" /* Embedded */,
|
|
210
|
+
external: "External driver via wdio-dioxus-driver (Windows only)" /* External */
|
|
211
|
+
})
|
|
212
|
+
}, {
|
|
213
|
+
name: "dioxusAppBinaryPath",
|
|
214
|
+
answer: "dioxusAppBinaryPath",
|
|
215
|
+
type: "string",
|
|
216
|
+
desc: "Path to the Dioxus debug binary (with --desktop-framework dioxus)"
|
|
217
|
+
}, {
|
|
218
|
+
name: "backend",
|
|
219
|
+
answer: "backend",
|
|
220
|
+
type: "string",
|
|
221
|
+
desc: "Where the automation backend runs (with --runner e2e)",
|
|
222
|
+
choices: () => ({
|
|
223
|
+
local: "On my local machine" /* Local */,
|
|
224
|
+
experitest: "In the cloud using Experitest" /* Experitest */,
|
|
225
|
+
saucelabs: "In the cloud using Sauce Labs" /* Saucelabs */,
|
|
226
|
+
browserstack: "In the cloud using BrowserStack" /* Browserstack */,
|
|
227
|
+
other: "In the cloud using Testingbot or LambdaTest or a different service" /* OtherVendors */,
|
|
228
|
+
grid: "I have my own Selenium cloud" /* Grid */
|
|
229
|
+
})
|
|
230
|
+
}, {
|
|
231
|
+
name: "environment",
|
|
232
|
+
answer: "e2eEnvironment",
|
|
233
|
+
type: "string",
|
|
234
|
+
desc: "What to automate (with --runner e2e). Implied by --browsers or --mobile-environment",
|
|
235
|
+
choices: () => ({ web: "web", mobile: "mobile" })
|
|
236
|
+
}, {
|
|
237
|
+
name: "mobileEnvironment",
|
|
238
|
+
answer: "mobileEnvironment",
|
|
239
|
+
type: "string",
|
|
240
|
+
desc: "Mobile platform (with --environment mobile)",
|
|
241
|
+
choices: () => ({ android: "android", ios: "ios" })
|
|
242
|
+
}, {
|
|
243
|
+
name: "browsers",
|
|
244
|
+
answer: "browserEnvironment",
|
|
245
|
+
type: "array",
|
|
246
|
+
desc: "Comma-separated browsers to start with (with --environment web)",
|
|
247
|
+
choices: () => ({ chrome: "chrome", firefox: "firefox", safari: "safari", edge: "MicrosoftEdge" })
|
|
248
|
+
}, {
|
|
249
|
+
name: "hostname",
|
|
250
|
+
answer: "hostname",
|
|
251
|
+
type: "string",
|
|
252
|
+
desc: "Host of the cloud service or Selenium server (with --backend other or grid)"
|
|
253
|
+
}, {
|
|
254
|
+
name: "port",
|
|
255
|
+
answer: "port",
|
|
256
|
+
type: "string",
|
|
257
|
+
desc: "Port of the cloud service or Selenium server (with --backend other or grid)"
|
|
258
|
+
}, {
|
|
259
|
+
name: "path",
|
|
260
|
+
answer: "path",
|
|
261
|
+
type: "string",
|
|
262
|
+
desc: "Path of the Selenium server (with --backend grid)"
|
|
263
|
+
}, {
|
|
264
|
+
name: "envUser",
|
|
265
|
+
answer: "env_user",
|
|
266
|
+
type: "string",
|
|
267
|
+
desc: "Environment variable for the cloud username (with --backend saucelabs or browserstack, or other with a lambdatest.com --hostname)"
|
|
268
|
+
}, {
|
|
269
|
+
name: "envKey",
|
|
270
|
+
answer: "env_key",
|
|
271
|
+
type: "string",
|
|
272
|
+
desc: "Environment variable for the cloud access key (with --backend saucelabs or browserstack, or other with a lambdatest.com --hostname)"
|
|
273
|
+
}, {
|
|
274
|
+
name: "region",
|
|
275
|
+
answer: "region",
|
|
276
|
+
type: "string",
|
|
277
|
+
desc: "Sauce Labs region (with --backend saucelabs)",
|
|
278
|
+
choices: () => Object.fromEntries(Object.values(RegionOptions).map((region) => [region, region]))
|
|
279
|
+
}, {
|
|
280
|
+
name: "sauceConnect",
|
|
281
|
+
answer: "useSauceConnect",
|
|
282
|
+
type: "boolean",
|
|
283
|
+
desc: "Set up Sauce Connect for a local app (with --backend saucelabs)"
|
|
284
|
+
}, {
|
|
285
|
+
name: "expAccessKey",
|
|
286
|
+
answer: "expEnvAccessKey",
|
|
287
|
+
type: "string",
|
|
288
|
+
desc: "Experitest access key (with --backend experitest)"
|
|
289
|
+
}, {
|
|
290
|
+
name: "expHostname",
|
|
291
|
+
answer: "expEnvHostname",
|
|
292
|
+
type: "string",
|
|
293
|
+
desc: "Experitest cloud host (with --backend experitest)"
|
|
294
|
+
}, {
|
|
295
|
+
name: "expPort",
|
|
296
|
+
answer: "expEnvPort",
|
|
297
|
+
type: "string",
|
|
298
|
+
desc: "Experitest cloud port (with --backend experitest)"
|
|
299
|
+
}, {
|
|
300
|
+
name: "expProtocol",
|
|
301
|
+
answer: "expEnvProtocol",
|
|
302
|
+
type: "string",
|
|
303
|
+
desc: "Experitest protocol (with --backend experitest and a port other than 80 or 443)",
|
|
304
|
+
choices: () => ({ http: "http", https: "https" })
|
|
305
|
+
}, {
|
|
306
|
+
name: "framework",
|
|
307
|
+
answer: "framework",
|
|
308
|
+
type: "string",
|
|
309
|
+
desc: "Test framework",
|
|
310
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.framework, (short, purpose, pkg) => pkg.startsWith("@serenity-js") ? `serenity-${purpose}` : short)
|
|
311
|
+
}, {
|
|
312
|
+
name: "typescript",
|
|
313
|
+
answer: "isUsingTypeScript",
|
|
314
|
+
type: "boolean",
|
|
315
|
+
desc: "Write tests in TypeScript"
|
|
316
|
+
}, {
|
|
317
|
+
name: "generateTestFiles",
|
|
318
|
+
answer: "generateTestFiles",
|
|
319
|
+
type: "boolean",
|
|
320
|
+
desc: "Generate example test files"
|
|
321
|
+
}, {
|
|
322
|
+
name: "specs",
|
|
323
|
+
answer: "specs",
|
|
324
|
+
type: "string",
|
|
325
|
+
desc: "Location of spec or feature files (with --generate-test-files)"
|
|
326
|
+
}, {
|
|
327
|
+
name: "stepDefinitions",
|
|
328
|
+
answer: "stepDefinitions",
|
|
329
|
+
type: "string",
|
|
330
|
+
desc: "Location of step definitions (with --framework cucumber)"
|
|
331
|
+
}, {
|
|
332
|
+
name: "pageObjects",
|
|
333
|
+
answer: "usePageObjects",
|
|
334
|
+
type: "boolean",
|
|
335
|
+
desc: "Generate page objects (with --runner e2e and --generate-test-files)"
|
|
336
|
+
}, {
|
|
337
|
+
name: "pages",
|
|
338
|
+
answer: "pages",
|
|
339
|
+
type: "string",
|
|
340
|
+
desc: "Location of page objects (with --page-objects)"
|
|
341
|
+
}, {
|
|
342
|
+
name: "serenityLibPath",
|
|
343
|
+
answer: "serenityLibPath",
|
|
344
|
+
type: "string",
|
|
345
|
+
desc: "Location of the Serenity/JS Screenplay library (with --framework serenity-*)"
|
|
346
|
+
}, {
|
|
347
|
+
name: "agentSupport",
|
|
348
|
+
answer: "agentSupport",
|
|
349
|
+
type: "boolean",
|
|
350
|
+
desc: "Write the AGENTS.md section and the wdio-session skill"
|
|
351
|
+
}, {
|
|
352
|
+
name: "reporters",
|
|
353
|
+
answer: "reporters",
|
|
354
|
+
type: "array",
|
|
355
|
+
desc: "Comma-separated reporters",
|
|
356
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.reporter)
|
|
357
|
+
}, {
|
|
358
|
+
name: "plugins",
|
|
359
|
+
answer: "plugins",
|
|
360
|
+
type: "array",
|
|
361
|
+
desc: "Comma-separated plugins",
|
|
362
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.plugin)
|
|
363
|
+
}, {
|
|
364
|
+
name: "visualTesting",
|
|
365
|
+
answer: "includeVisualTesting",
|
|
366
|
+
type: "boolean",
|
|
367
|
+
desc: "Add visual testing (with --runner e2e or component)"
|
|
368
|
+
}, {
|
|
369
|
+
name: "services",
|
|
370
|
+
answer: "services",
|
|
371
|
+
type: "array",
|
|
372
|
+
desc: "Comma-separated services, replaces the ones the wizard picks for your setup",
|
|
373
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.service)
|
|
374
|
+
}, {
|
|
375
|
+
name: "npmInstall",
|
|
376
|
+
answer: "npmInstall",
|
|
377
|
+
type: "boolean",
|
|
378
|
+
desc: "Install the dependencies"
|
|
379
|
+
}];
|
|
380
|
+
function toKebabCase(name) {
|
|
381
|
+
return name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
382
|
+
}
|
|
383
|
+
function flagFor(answer) {
|
|
384
|
+
return ANSWER_FLAGS.find((flag) => flag.answer === answer);
|
|
385
|
+
}
|
|
386
|
+
function describeChoices(flag) {
|
|
387
|
+
return Object.keys(flag.choices()).join(", ");
|
|
388
|
+
}
|
|
389
|
+
function describeAnswerFlag(flag) {
|
|
390
|
+
return flag.choices ? `${flag.desc}: ${describeChoices(flag)}` : flag.desc;
|
|
391
|
+
}
|
|
392
|
+
var ANSWER_FLAGS_HELP = [
|
|
393
|
+
"Every wizard question has a flag. A flag answers its question and the wizard",
|
|
394
|
+
"asks the rest, or uses the defaults for the rest with --yes. Boolean flags",
|
|
395
|
+
"take a --no- prefix, e.g. --no-typescript. A flag for a question the wizard",
|
|
396
|
+
"does not ask for your setup is an error, and so is a value it does not offer."
|
|
397
|
+
].join("\n");
|
|
398
|
+
function getAnswerFlagYargsOptions() {
|
|
399
|
+
return Object.fromEntries(ANSWER_FLAGS.map((flag) => [toKebabCase(flag.name), {
|
|
400
|
+
desc: describeAnswerFlag(flag),
|
|
401
|
+
type: flag.type === "boolean" ? "boolean" : "string",
|
|
402
|
+
group: "Wizard answers:"
|
|
403
|
+
}]));
|
|
404
|
+
}
|
|
405
|
+
function lastValue(raw) {
|
|
406
|
+
return Array.isArray(raw) ? raw[raw.length - 1] : raw;
|
|
407
|
+
}
|
|
408
|
+
function toAnswerValue(flag, value) {
|
|
409
|
+
if (!flag.choices) {
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
const choices = flag.choices();
|
|
413
|
+
const key = value.toLowerCase();
|
|
414
|
+
if (!Object.prototype.hasOwnProperty.call(choices, key)) {
|
|
415
|
+
throw new AnswerFlagError(
|
|
416
|
+
`Invalid value "${value}" for --${toKebabCase(flag.name)}. Use one of: ${describeChoices(flag)}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return choices[key];
|
|
420
|
+
}
|
|
421
|
+
function parseAnswerFlags(argv) {
|
|
422
|
+
const answers = {};
|
|
423
|
+
for (const flag of ANSWER_FLAGS) {
|
|
424
|
+
const raw = argv[flag.name];
|
|
425
|
+
if (typeof raw === "undefined") {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (flag.type === "boolean") {
|
|
429
|
+
answers[flag.answer] = raw === true || raw === "true";
|
|
430
|
+
} else if (flag.type === "array") {
|
|
431
|
+
const values = [raw].flat().flatMap((value) => String(value).split(",")).map((value) => value.trim()).filter(Boolean).map((value) => toAnswerValue(flag, value));
|
|
432
|
+
answers[flag.answer] = [...new Set(values)];
|
|
433
|
+
} else {
|
|
434
|
+
answers[flag.answer] = toAnswerValue(flag, String(lastValue(raw)));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!answers.e2eEnvironment) {
|
|
438
|
+
if (answers.mobileEnvironment) {
|
|
439
|
+
answers.e2eEnvironment = "mobile";
|
|
440
|
+
} else if (answers.browserEnvironment) {
|
|
441
|
+
answers.e2eEnvironment = "web";
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return answers;
|
|
445
|
+
}
|
|
446
|
+
function isAsked(question, answers) {
|
|
447
|
+
const when = question.when;
|
|
448
|
+
if (typeof when === "function") {
|
|
449
|
+
return Boolean(when(answers));
|
|
450
|
+
}
|
|
451
|
+
return typeof when === "undefined" || Boolean(when);
|
|
452
|
+
}
|
|
453
|
+
function choiceValues(question, answers) {
|
|
454
|
+
if (!question.choices) {
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
const choices = typeof question.choices === "function" ? question.choices(answers) : question.choices;
|
|
458
|
+
return choices.filter((choice) => typeof choice !== "undefined").map((choice) => choice && typeof choice === "object" ? choice.value : choice);
|
|
459
|
+
}
|
|
460
|
+
function assertAnswerFlagsApply(flagAnswers, answers) {
|
|
461
|
+
for (const [answer, value] of Object.entries(flagAnswers)) {
|
|
462
|
+
const flag = flagFor(answer);
|
|
463
|
+
const usage = `--${toKebabCase(flag.name)}`;
|
|
464
|
+
const questions = QUESTIONNAIRE.filter((question) => question.name === answer && isAsked(question, answers));
|
|
465
|
+
if (questions.length === 0) {
|
|
466
|
+
throw new AnswerFlagError(`${usage} does not apply to this setup. ${flag.desc}.`);
|
|
467
|
+
}
|
|
468
|
+
const offered = questions.map((question) => choiceValues(question, answers));
|
|
469
|
+
if (offered.some((values) => typeof values === "undefined")) {
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const allowed = new Set(offered.flat());
|
|
473
|
+
const rejected = [value].flat().filter((item) => !allowed.has(item));
|
|
474
|
+
if (rejected.length === 0) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const cliValues = Object.entries(flag.choices());
|
|
478
|
+
const toCli = (item) => cliValues.find(([, v]) => v === item)?.[0] ?? String(item);
|
|
479
|
+
throw new AnswerFlagError(
|
|
480
|
+
`${usage} ${rejected.map(toCli).join(",")} is not available for this setup. Use one of: ${[...new Set([...allowed].map(toCli))].join(", ")}`
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
127
485
|
// src/utils.ts
|
|
128
486
|
var NPM_COMMAND = /^win/.test(process.platform) ? "npm.cmd" : "npm";
|
|
129
487
|
var __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
130
488
|
process.on("SIGINT", () => printAndExit(void 0, "SIGINT"));
|
|
131
489
|
var TEMPLATE_ROOT_DIR = process.env.WDIO_UNIT_TESTS ? path.join(__dirname, "templates", "exampleFiles") : path.join(__dirname, "..", "templates", "exampleFiles");
|
|
132
|
-
function runProgram(command2, args, options) {
|
|
490
|
+
function runProgram(command2, args, options, passUsageError = false) {
|
|
133
491
|
const child = spawn(command2, args, { stdio: "inherit", ...options });
|
|
134
492
|
return new Promise((resolve, rejects) => {
|
|
135
493
|
let error;
|
|
136
494
|
child.on("error", (e) => error = e);
|
|
137
495
|
child.on("close", (code, signal) => {
|
|
496
|
+
if (passUsageError && code === 2) {
|
|
497
|
+
process.exit(2);
|
|
498
|
+
return rejects(`Usage error calling: ${command2} ${args.join(" ")}`);
|
|
499
|
+
}
|
|
138
500
|
if (code !== 0) {
|
|
139
501
|
const errorMessage = error && error.message || `Error calling: ${command2} ${args.join(" ")}`;
|
|
140
502
|
printAndExit(errorMessage, signal);
|
|
@@ -158,24 +520,35 @@ function convertPackageHashToObject(pkg, hash = "$--$") {
|
|
|
158
520
|
const [p, short, purpose] = pkg.split(hash);
|
|
159
521
|
return { package: p, short, purpose };
|
|
160
522
|
}
|
|
161
|
-
async function getAnswers(yes) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
523
|
+
async function getAnswers(yes, flagAnswers = {}) {
|
|
524
|
+
const answers = yes ? await getDefaultAnswers(flagAnswers) : await promptAnswers(flagAnswers);
|
|
525
|
+
assertAnswerFlagsApply(flagAnswers, answers);
|
|
526
|
+
return answers;
|
|
527
|
+
}
|
|
528
|
+
async function getDefaultAnswers(flagAnswers) {
|
|
529
|
+
const ignoredQuestions = ["e2eEnvironment"];
|
|
530
|
+
const filteredQuestionaire = QUESTIONNAIRE.filter((question) => !ignoredQuestions.includes(question.name));
|
|
531
|
+
const answers = { ...flagAnswers };
|
|
532
|
+
for (const question of filteredQuestionaire) {
|
|
533
|
+
if (Object.prototype.hasOwnProperty.call(flagAnswers, question.name)) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
if (question.when && !question.when(answers)) {
|
|
537
|
+
continue;
|
|
173
538
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
539
|
+
if (typeof question.default === "undefined" && !question.choices) {
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
Object.assign(answers, {
|
|
543
|
+
[question.name]: typeof question.default !== "undefined" ? typeof question.default === "function" ? await question.default(answers) : question.default : question.choices && question.choices.length ? typeof question.choices === "function" ? question.choices(answers)[0].value ? question.choices(answers)[0].value : question.choices(answers)[0] : question.choices[0].value ? question.type === "checkbox" ? [question.choices[0].value] : question.choices[0].value : question.choices[0] : {}
|
|
544
|
+
});
|
|
178
545
|
}
|
|
546
|
+
answers.isUsingTypeScript = await answers.isUsingTypeScript;
|
|
547
|
+
answers.specs = await answers.specs;
|
|
548
|
+
answers.pages = await answers.pages;
|
|
549
|
+
return answers;
|
|
550
|
+
}
|
|
551
|
+
async function promptAnswers(flagAnswers) {
|
|
179
552
|
const projectProps = await getProjectProps(process.cwd());
|
|
180
553
|
const isProjectExisting = Boolean(projectProps);
|
|
181
554
|
const nameInPackageJsonIsNotCreateWdioDefault = projectProps?.packageJson?.name !== "my-new-project";
|
|
@@ -211,7 +584,7 @@ async function getAnswers(yes) {
|
|
|
211
584
|
if (answers.createPackageJSON) {
|
|
212
585
|
answers.projectRoot = process.cwd();
|
|
213
586
|
}
|
|
214
|
-
return inquirer.prompt(QUESTIONNAIRE, answers);
|
|
587
|
+
return inquirer.prompt(QUESTIONNAIRE, { ...answers, ...flagAnswers });
|
|
215
588
|
}
|
|
216
589
|
async function getProjectProps(cwd = process.cwd()) {
|
|
217
590
|
try {
|
|
@@ -267,11 +640,8 @@ async function createPackageJSON(parsedAnswers) {
|
|
|
267
640
|
return;
|
|
268
641
|
}
|
|
269
642
|
if (parsedAnswers.createPackageJSON === false) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return !process.env.WDIO_UNIT_TESTS && process.exit(0);
|
|
273
|
-
}
|
|
274
|
-
return;
|
|
643
|
+
console.log(`No WebdriverIO configuration found in "${parsedAnswers.wdioConfigPath}"`);
|
|
644
|
+
process.exit(0);
|
|
275
645
|
}
|
|
276
646
|
if (parsedAnswers.createPackageJSON) {
|
|
277
647
|
console.log(`Creating a ${chalk.bold("package.json")} for the directory...`);
|
|
@@ -318,10 +688,13 @@ async function setupTypeScript(parsedAnswers) {
|
|
|
318
688
|
const config = {
|
|
319
689
|
compilerOptions: {
|
|
320
690
|
// compiler
|
|
321
|
-
moduleResolution:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
691
|
+
// TypeScript 6 deprecates `moduleResolution: 'node'` (node10). ESM uses
|
|
692
|
+
// `bundler`, which resolves imports as `tsx` does; CommonJS uses `NodeNext`,
|
|
693
|
+
// which follows the `type` of the project's package.json.
|
|
694
|
+
moduleResolution: parsedAnswers.esmSupport ? "bundler" : "NodeNext",
|
|
695
|
+
module: parsedAnswers.esmSupport ? "ESNext" : "NodeNext",
|
|
696
|
+
target: "es2024",
|
|
697
|
+
lib: ["es2024", "dom"],
|
|
325
698
|
types,
|
|
326
699
|
skipLibCheck: true,
|
|
327
700
|
// bundler
|
|
@@ -466,8 +839,10 @@ function getPreset(parsedAnswers) {
|
|
|
466
839
|
function addServiceDeps(names, packages, update = false) {
|
|
467
840
|
if (names.some(({ short }) => short === "appium")) {
|
|
468
841
|
const result = execSync("appium --version || echo APPIUM_MISSING", { stdio: "pipe" }).toString().trim();
|
|
469
|
-
|
|
470
|
-
|
|
842
|
+
const major = Number.parseInt(result.replace(/^v/i, "").split(".")[0] || "", 10);
|
|
843
|
+
const hasAppium3 = result !== "APPIUM_MISSING" && !Number.isNaN(major) && major >= 3;
|
|
844
|
+
if (!hasAppium3) {
|
|
845
|
+
packages.push("appium@^3");
|
|
471
846
|
} else if (update) {
|
|
472
847
|
console.log(
|
|
473
848
|
"\n=======",
|
|
@@ -527,10 +902,15 @@ async function createWDIOScript(parsedAnswers) {
|
|
|
527
902
|
console.log(chalk.green(chalk.bold("\u2714 Success!")));
|
|
528
903
|
return true;
|
|
529
904
|
}
|
|
530
|
-
async function runAppiumInstaller(parsedAnswers) {
|
|
905
|
+
async function runAppiumInstaller(parsedAnswers, yes = false) {
|
|
531
906
|
if (parsedAnswers.e2eEnvironment !== "mobile") {
|
|
532
907
|
return;
|
|
533
908
|
}
|
|
909
|
+
if (yes || !process.stdin.isTTY) {
|
|
910
|
+
return console.log(
|
|
911
|
+
"Skipping the interactive Appium installer. Run `npx appium-installer` later, or see https://appium.io/docs/en/latest/quickstart/"
|
|
912
|
+
);
|
|
913
|
+
}
|
|
534
914
|
const answer = await inquirer.prompt({
|
|
535
915
|
name: "continueWithAppiumSetup",
|
|
536
916
|
message: "Continue with Appium setup using appium-installer (https://github.com/AppiumTestDistribution/appium-installer)?",
|
|
@@ -626,6 +1006,10 @@ async function generateTestFiles(answers) {
|
|
|
626
1006
|
}
|
|
627
1007
|
return generateBrowserRunnerTestFiles(answers);
|
|
628
1008
|
}
|
|
1009
|
+
async function readDir(dir) {
|
|
1010
|
+
const entries = await fs.readdir(dir, { recursive: true, withFileTypes: true });
|
|
1011
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => path.join(entry.parentPath, entry.name));
|
|
1012
|
+
}
|
|
629
1013
|
async function generateSerenityExamples(answers) {
|
|
630
1014
|
const templateDirectories = Object.entries({
|
|
631
1015
|
[answers.destSpecRootPath]: path.join(TEMPLATE_ROOT_DIR, "serenity-js", answers.serenityAdapter),
|
|
@@ -650,10 +1034,7 @@ async function generateLocalRunnerTestFiles(answers) {
|
|
|
650
1034
|
if (answers.usePageObjects) {
|
|
651
1035
|
testFiles.push(path.join(TEMPLATE_ROOT_DIR, "pageobjects"));
|
|
652
1036
|
}
|
|
653
|
-
const files = (await Promise.all(testFiles.map((
|
|
654
|
-
dirPath,
|
|
655
|
-
[(file, stats) => !stats.isDirectory() && !(file.endsWith(".ejs") || file.endsWith(".feature"))]
|
|
656
|
-
)))).reduce((cur, acc) => [...acc, ...cur], []);
|
|
1037
|
+
const files = (await Promise.all(testFiles.map(readDir))).flat().filter((file) => file.endsWith(".ejs") || file.endsWith(".feature"));
|
|
657
1038
|
await Promise.all(files.map(async (file) => {
|
|
658
1039
|
const renderedTpl = await renderFile(file, { answers });
|
|
659
1040
|
const isJSX = answers.preset && TSX_BASED_FRAMEWORKS.includes(answers.preset);
|
|
@@ -692,7 +1073,7 @@ async function formatConfigFilePaths(config) {
|
|
|
692
1073
|
// package.json
|
|
693
1074
|
var package_default = {
|
|
694
1075
|
name: "create-wdio",
|
|
695
|
-
version: "9.
|
|
1076
|
+
version: "9.32.0",
|
|
696
1077
|
description: "Install and setup a WebdriverIO project with all its dependencies in a single run",
|
|
697
1078
|
author: "WebdriverIO Team <mail@webdriver.io>",
|
|
698
1079
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/create-wdio",
|
|
@@ -721,9 +1102,9 @@ var package_default = {
|
|
|
721
1102
|
}
|
|
722
1103
|
},
|
|
723
1104
|
types: "./build/index.d.ts",
|
|
724
|
-
typeScriptVersion: "
|
|
1105
|
+
typeScriptVersion: "6.0.3",
|
|
725
1106
|
engines: {
|
|
726
|
-
node: ">=
|
|
1107
|
+
node: ">=22.19.0"
|
|
727
1108
|
},
|
|
728
1109
|
repository: {
|
|
729
1110
|
type: "git",
|
|
@@ -753,10 +1134,9 @@ var package_default = {
|
|
|
753
1134
|
inquirer: "^12.7.0",
|
|
754
1135
|
"normalize-package-data": "^7.0.0",
|
|
755
1136
|
"read-pkg-up": "^10.1.0",
|
|
756
|
-
"recursive-readdir": "^2.2.3",
|
|
757
1137
|
semver: "^7.6.3",
|
|
758
1138
|
"type-fest": "^4.41.0",
|
|
759
|
-
yargs: "^
|
|
1139
|
+
yargs: "^18.2.0"
|
|
760
1140
|
},
|
|
761
1141
|
devDependencies: {
|
|
762
1142
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -784,7 +1164,7 @@ var PROGRAM_TITLE = `
|
|
|
784
1164
|
`;
|
|
785
1165
|
var UNSUPPORTED_NODE_VERSION = `\u26A0\uFE0F Unsupported Node.js Version Error \u26A0\uFE0F
|
|
786
1166
|
You are using Node.js ${process.version} which is too old to be used with WebdriverIO.
|
|
787
|
-
Please update to Node.js
|
|
1167
|
+
Please update to Node.js v22.19.0 to continue.
|
|
788
1168
|
`;
|
|
789
1169
|
var INSTALL_COMMAND = {
|
|
790
1170
|
npm: "install",
|
|
@@ -846,6 +1226,7 @@ var SUPPORTED_PACKAGES = {
|
|
|
846
1226
|
service: [
|
|
847
1227
|
// internal or community driver services
|
|
848
1228
|
{ name: "visual", value: "@wdio/visual-service$--$visual" },
|
|
1229
|
+
{ name: "ai", value: "@wdio/ai-service$--$ai" },
|
|
849
1230
|
{ name: "vite", value: "wdio-vite-service$--$vite" },
|
|
850
1231
|
{ name: "nuxt", value: "wdio-nuxt-service$--$nuxt" },
|
|
851
1232
|
{ name: "firefox-profile", value: "@wdio/firefox-profile-service$--$firefox-profile" },
|
|
@@ -1540,6 +1921,11 @@ var QUESTIONNAIRE = [{
|
|
|
1540
1921
|
/* istanbul ignore next */
|
|
1541
1922
|
(answers) => answers.generateTestFiles && usesSerenity(answers)
|
|
1542
1923
|
)
|
|
1924
|
+
}, {
|
|
1925
|
+
type: "confirm",
|
|
1926
|
+
name: "agentSupport",
|
|
1927
|
+
message: "Set up coding agent support (AGENTS.md section and wdio-session skill)?",
|
|
1928
|
+
default: true
|
|
1543
1929
|
}, {
|
|
1544
1930
|
type: "checkbox",
|
|
1545
1931
|
name: "reporters",
|
|
@@ -1677,10 +2063,79 @@ To install dependencies, execute:
|
|
|
1677
2063
|
`;
|
|
1678
2064
|
|
|
1679
2065
|
// src/cli/utils.ts
|
|
1680
|
-
import
|
|
1681
|
-
import
|
|
2066
|
+
import path4 from "node:path";
|
|
2067
|
+
import fs4 from "node:fs/promises";
|
|
1682
2068
|
import inquirer2 from "inquirer";
|
|
1683
|
-
|
|
2069
|
+
|
|
2070
|
+
// src/cli/agent.ts
|
|
2071
|
+
import fs3 from "node:fs";
|
|
2072
|
+
import path3 from "node:path";
|
|
2073
|
+
import { fileURLToPath } from "node:url";
|
|
2074
|
+
var AGENT_SECTION_HEADING = "## End-to-end tests (WebdriverIO v10)";
|
|
2075
|
+
var AGENT_SECTION = `${AGENT_SECTION_HEADING}
|
|
2076
|
+
|
|
2077
|
+
- Docs: https://webdriver.io/llms.txt - fetch the relevant page as Markdown (append \`.md\`) before using an API you are not sure about. Do not use APIs from WebdriverIO v8 or older.
|
|
2078
|
+
- Config: \`wdio.conf.ts\`. Specs: \`test/specs/**/*.e2e.ts\`. Page objects: \`test/pageobjects/\`.
|
|
2079
|
+
- Run all tests: \`npx wdio run wdio.conf.ts\`
|
|
2080
|
+
- Run a single spec: \`npx wdio run wdio.conf.ts --spec test/specs/login.e2e.ts\`
|
|
2081
|
+
- Tests are async: always \`await\` commands, e.g. \`await $('button').click()\`. Never use the removed sync mode.
|
|
2082
|
+
- Prefer user-facing selectors: accessibility name or text (\`$('aria/Submit')\`, \`$('button=Submit')\`), then \`data-testid\`. Avoid XPath and generated CSS classes.
|
|
2083
|
+
- Rely on auto-waiting and \`expect-webdriverio\` matchers (\`await expect($('h1')).toHaveText('Welcome')\`) instead of \`browser.pause()\`.
|
|
2084
|
+
- To explore the app or verify a selector, use the \`wdio-mcp\` MCP server.
|
|
2085
|
+
- To drive the app from the shell, follow \`.agents/skills/wdio-session/SKILL.md\` (\`npx wdio session\`).
|
|
2086
|
+
- When a test fails, read the DevTools trace in \`test-results/\` (see \`transcript.md\`) before changing code.
|
|
2087
|
+
`;
|
|
2088
|
+
function readIfExists(file) {
|
|
2089
|
+
return fs3.existsSync(file) ? fs3.readFileSync(file, "utf-8") : void 0;
|
|
2090
|
+
}
|
|
2091
|
+
function readSessionSkill(start = path3.dirname(fileURLToPath(import.meta.url))) {
|
|
2092
|
+
let dir = start;
|
|
2093
|
+
for (let i = 0; i < 8; i++) {
|
|
2094
|
+
const candidates = [
|
|
2095
|
+
path3.join(dir, "wdio-session", "src", "skill", "SKILL.md"),
|
|
2096
|
+
path3.join(dir, "packages", "wdio-session", "src", "skill", "SKILL.md"),
|
|
2097
|
+
path3.join(dir, "node_modules", "@wdio", "session", "src", "skill", "SKILL.md"),
|
|
2098
|
+
path3.join(dir, "templates", "wdio-session", "SKILL.md")
|
|
2099
|
+
];
|
|
2100
|
+
for (const candidate of candidates) {
|
|
2101
|
+
const markdown = readIfExists(candidate);
|
|
2102
|
+
if (markdown) {
|
|
2103
|
+
return markdown;
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
const parent = path3.dirname(dir);
|
|
2107
|
+
if (parent === dir) {
|
|
2108
|
+
break;
|
|
2109
|
+
}
|
|
2110
|
+
dir = parent;
|
|
2111
|
+
}
|
|
2112
|
+
throw new Error("Cannot find the wdio-session skill (src/skill/SKILL.md).");
|
|
2113
|
+
}
|
|
2114
|
+
function writeAgentSupport(root) {
|
|
2115
|
+
const skillDest = path3.join(root, ".agents", "skills", "wdio-session", "SKILL.md");
|
|
2116
|
+
if (!fs3.existsSync(skillDest)) {
|
|
2117
|
+
const markdown = readSessionSkill();
|
|
2118
|
+
fs3.mkdirSync(path3.dirname(skillDest), { recursive: true });
|
|
2119
|
+
fs3.writeFileSync(skillDest, markdown);
|
|
2120
|
+
}
|
|
2121
|
+
const agentsPath = path3.join(root, "AGENTS.md");
|
|
2122
|
+
const agents = readIfExists(agentsPath) || "";
|
|
2123
|
+
if (!agents.includes(AGENT_SECTION_HEADING)) {
|
|
2124
|
+
const sep = agents.length === 0 ? "" : agents.endsWith("\n") ? "\n" : "\n\n";
|
|
2125
|
+
fs3.writeFileSync(agentsPath, agents + sep + AGENT_SECTION);
|
|
2126
|
+
}
|
|
2127
|
+
const gitignorePath = path3.join(root, ".gitignore");
|
|
2128
|
+
const gitignore = readIfExists(gitignorePath) || "";
|
|
2129
|
+
const hasEntry = gitignore.split("\n").some((line) => line.trim() === ".wdio/session/" || line.trim() === ".wdio/session");
|
|
2130
|
+
if (!hasEntry) {
|
|
2131
|
+
const sep = gitignore.length === 0 || gitignore.endsWith("\n") ? "" : "\n";
|
|
2132
|
+
fs3.writeFileSync(gitignorePath, `${gitignore}${sep}.wdio/session/
|
|
2133
|
+
`);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
// src/cli/utils.ts
|
|
2138
|
+
async function runConfigCommand(parsedAnswers, npmTag, yes = false) {
|
|
1684
2139
|
console.log("\n");
|
|
1685
2140
|
await createPackageJSON(parsedAnswers);
|
|
1686
2141
|
await setupTypeScript(parsedAnswers);
|
|
@@ -1709,10 +2164,13 @@ async function runConfigCommand(parsedAnswers, npmTag) {
|
|
|
1709
2164
|
extraInfo: extraInfoParts.join("\n")
|
|
1710
2165
|
})
|
|
1711
2166
|
);
|
|
1712
|
-
await runAppiumInstaller(parsedAnswers);
|
|
2167
|
+
await runAppiumInstaller(parsedAnswers, yes);
|
|
2168
|
+
if (parsedAnswers.rawAnswers?.agentSupport) {
|
|
2169
|
+
writeAgentSupport(parsedAnswers.projectRootDir);
|
|
2170
|
+
}
|
|
1713
2171
|
}
|
|
1714
2172
|
async function missingConfigurationPrompt(command2, configPath, runConfigCmd = runConfigCommand) {
|
|
1715
|
-
const message = `Could not execute "${command2}" due to missing configuration, file "${
|
|
2173
|
+
const message = `Could not execute "${command2}" due to missing configuration, file "${path4.parse(configPath).name}[.js/.ts]" not found! Would you like to create one?`;
|
|
1716
2174
|
const { config } = await inquirer2.prompt({
|
|
1717
2175
|
type: "confirm",
|
|
1718
2176
|
name: "config",
|
|
@@ -1721,10 +2179,7 @@ async function missingConfigurationPrompt(command2, configPath, runConfigCmd = r
|
|
|
1721
2179
|
});
|
|
1722
2180
|
if (!config) {
|
|
1723
2181
|
console.log(`No WebdriverIO configuration found in "${process.cwd()}"`);
|
|
1724
|
-
|
|
1725
|
-
process.exit(0);
|
|
1726
|
-
}
|
|
1727
|
-
return configPath;
|
|
2182
|
+
process.exit(0);
|
|
1728
2183
|
}
|
|
1729
2184
|
const parsedAnswers = await parseAnswers(false);
|
|
1730
2185
|
await runConfigCmd(parsedAnswers, "latest");
|
|
@@ -1736,8 +2191,8 @@ function resolveTsConfigFilePath({
|
|
|
1736
2191
|
specs,
|
|
1737
2192
|
runnerShort
|
|
1738
2193
|
}) {
|
|
1739
|
-
const rootTsConfigPath =
|
|
1740
|
-
const dedicatedTsConfigPath =
|
|
2194
|
+
const rootTsConfigPath = path4.resolve(projectRootDir, "tsconfig.json");
|
|
2195
|
+
const dedicatedTsConfigPath = path4.resolve(
|
|
1741
2196
|
projectRootDir,
|
|
1742
2197
|
`tsconfig.${runnerShort === "local" ? "e2e" : "wdio"}.json`
|
|
1743
2198
|
);
|
|
@@ -1747,13 +2202,13 @@ function resolveTsConfigFilePath({
|
|
|
1747
2202
|
if (!specs) {
|
|
1748
2203
|
return dedicatedTsConfigPath;
|
|
1749
2204
|
}
|
|
1750
|
-
const specPathWithoutGlobs = specs.split(
|
|
1751
|
-
const candidate =
|
|
1752
|
-
return
|
|
2205
|
+
const specPathWithoutGlobs = specs.split(path4.sep).filter((segment) => !segment.includes("*")).join(path4.sep);
|
|
2206
|
+
const candidate = path4.resolve(projectRootDir, path4.dirname(specPathWithoutGlobs), "tsconfig.json");
|
|
2207
|
+
return path4.resolve(candidate) === rootTsConfigPath ? dedicatedTsConfigPath : candidate;
|
|
1753
2208
|
}
|
|
1754
|
-
var parseAnswers = async function(yes) {
|
|
2209
|
+
var parseAnswers = async function(yes, flagAnswers = {}) {
|
|
1755
2210
|
console.log(CONFIG_HELPER_INTRO);
|
|
1756
|
-
const answers = await getAnswers(yes);
|
|
2211
|
+
const answers = await getAnswers(yes, flagAnswers);
|
|
1757
2212
|
const frameworkPackage = convertPackageHashToObject(answers.framework);
|
|
1758
2213
|
const runnerPackage = convertPackageHashToObject(answers.runner || SUPPORTED_PACKAGES.runner[0].value);
|
|
1759
2214
|
const servicePackages = answers.services.map((service) => convertPackageHashToObject(service));
|
|
@@ -1772,7 +2227,7 @@ var parseAnswers = async function(yes) {
|
|
|
1772
2227
|
...servicePackages.map((service) => service.package),
|
|
1773
2228
|
...serenityPackages
|
|
1774
2229
|
].filter(Boolean);
|
|
1775
|
-
const hasRootTSConfig = await
|
|
2230
|
+
const hasRootTSConfig = await fs4.access(path4.resolve(projectRootDir, "tsconfig.json")).then(() => true, () => false);
|
|
1776
2231
|
const tsConfigFilePath = resolveTsConfigFilePath({
|
|
1777
2232
|
projectRootDir,
|
|
1778
2233
|
hasRootTSConfig,
|
|
@@ -1782,7 +2237,7 @@ var parseAnswers = async function(yes) {
|
|
|
1782
2237
|
const parsedPaths = getPathForFileGeneration(answers, projectRootDir);
|
|
1783
2238
|
const isUsingTypeScript = answers.isUsingTypeScript;
|
|
1784
2239
|
const wdioConfigFilename = `wdio.conf.${isUsingTypeScript ? "ts" : "js"}`;
|
|
1785
|
-
const wdioConfigPath =
|
|
2240
|
+
const wdioConfigPath = path4.resolve(projectRootDir, wdioConfigFilename);
|
|
1786
2241
|
return {
|
|
1787
2242
|
projectName: projectProps?.packageJson.name || "Test Suite",
|
|
1788
2243
|
// default values required in templates
|
|
@@ -1801,8 +2256,8 @@ var parseAnswers = async function(yes) {
|
|
|
1801
2256
|
reporters: reporterPackages.map(({ short }) => short),
|
|
1802
2257
|
plugins: pluginPackages.map(({ short }) => short),
|
|
1803
2258
|
services: servicePackages.map(({ short }) => short),
|
|
1804
|
-
specs: answers.specs && `./${
|
|
1805
|
-
stepDefinitions: answers.stepDefinitions && `./${
|
|
2259
|
+
specs: answers.specs && `./${path4.relative(projectRootDir, answers.specs).replaceAll(path4.sep, "/")}`,
|
|
2260
|
+
stepDefinitions: answers.stepDefinitions && `./${path4.relative(projectRootDir, answers.stepDefinitions).replaceAll(path4.sep, "/")}`,
|
|
1806
2261
|
packagesToInstall,
|
|
1807
2262
|
isUsingTypeScript,
|
|
1808
2263
|
esmSupport: projectProps && !projectProps.esmSupported ? false : true,
|
|
@@ -1817,20 +2272,20 @@ var parseAnswers = async function(yes) {
|
|
|
1817
2272
|
relativePath: parsedPaths.relativePath,
|
|
1818
2273
|
hasRootTSConfig,
|
|
1819
2274
|
tsConfigFilePath,
|
|
1820
|
-
tsProject: `./${
|
|
2275
|
+
tsProject: `./${path4.relative(projectRootDir, tsConfigFilePath).replaceAll(path4.sep, "/")}`,
|
|
1821
2276
|
wdioConfigPath
|
|
1822
2277
|
};
|
|
1823
2278
|
};
|
|
1824
2279
|
async function canAccessConfigPath(configPathNoExtension, configPath) {
|
|
1825
2280
|
return new Promise((resolve, reject) => {
|
|
1826
2281
|
if (configPath) {
|
|
1827
|
-
|
|
2282
|
+
fs4.access(configPath).then(() => resolve(configPath), reject);
|
|
1828
2283
|
} else {
|
|
1829
2284
|
reject();
|
|
1830
2285
|
}
|
|
1831
2286
|
}).catch(() => Promise.all(SUPPORTED_CONFIG_FILE_EXTENSION.map(async (supportedExtension) => {
|
|
1832
2287
|
const configPathWithExtension = `${configPathNoExtension}.${supportedExtension}`;
|
|
1833
|
-
return
|
|
2288
|
+
return fs4.access(configPathWithExtension).then(() => configPathWithExtension, () => void 0);
|
|
1834
2289
|
})).then(
|
|
1835
2290
|
(configFilePaths) => configFilePaths.find(Boolean),
|
|
1836
2291
|
() => void 0
|
|
@@ -1867,11 +2322,23 @@ var cmdArgs = {
|
|
|
1867
2322
|
}
|
|
1868
2323
|
};
|
|
1869
2324
|
var builder = (yargs) => {
|
|
1870
|
-
return yargs.options(cmdArgs).
|
|
2325
|
+
return yargs.options({ ...cmdArgs, ...getAnswerFlagYargsOptions() }).example("$0 config", "Answer every question in the wizard").example("$0 config --yes", "Use the defaults: Mocha, Chrome and page objects").example("$0 config --yes --framework cucumber --no-typescript", "Use the defaults, but Cucumber in JavaScript").example("$0 config --yes --environment mobile --mobile-environment android", "Android app tests with the Appium service").example("$0 config --runner component --preset react --framework mocha", "Answer some questions, the wizard asks the rest").epilogue(`${ANSWER_FLAGS_HELP}
|
|
2326
|
+
|
|
2327
|
+
${CLI_EPILOGUE}`).help();
|
|
1871
2328
|
};
|
|
1872
2329
|
async function handler(argv, runConfigCmd = runConfigCommand) {
|
|
1873
|
-
|
|
1874
|
-
|
|
2330
|
+
let parsedAnswers;
|
|
2331
|
+
try {
|
|
2332
|
+
parsedAnswers = await parseAnswers(argv.yes, parseAnswerFlags(argv));
|
|
2333
|
+
} catch (err) {
|
|
2334
|
+
if (!(err instanceof AnswerFlagError)) {
|
|
2335
|
+
throw err;
|
|
2336
|
+
}
|
|
2337
|
+
console.error(`Error: ${err.message}
|
|
2338
|
+
Run "wdio config --help" for all flags.`);
|
|
2339
|
+
return process.exit(2);
|
|
2340
|
+
}
|
|
2341
|
+
await runConfigCmd(parsedAnswers, argv.npmTag, argv.yes);
|
|
1875
2342
|
return {
|
|
1876
2343
|
success: true,
|
|
1877
2344
|
parsedAnswers,
|