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/install/cli.js
CHANGED
|
@@ -13,8 +13,8 @@ __export(install_exports, {
|
|
|
13
13
|
desc: () => desc,
|
|
14
14
|
handler: () => handler
|
|
15
15
|
});
|
|
16
|
-
import
|
|
17
|
-
import
|
|
16
|
+
import fs5 from "node:fs/promises";
|
|
17
|
+
import path5 from "node:path";
|
|
18
18
|
|
|
19
19
|
// src/utils.ts
|
|
20
20
|
import url from "node:url";
|
|
@@ -22,7 +22,6 @@ import path2 from "node:path";
|
|
|
22
22
|
import util, { promisify } from "node:util";
|
|
23
23
|
import fs2 from "node:fs/promises";
|
|
24
24
|
import { execSync } from "node:child_process";
|
|
25
|
-
import readDir from "recursive-readdir";
|
|
26
25
|
import { $ } from "execa";
|
|
27
26
|
import { readPackageUp } from "read-pkg-up";
|
|
28
27
|
|
|
@@ -85,7 +84,7 @@ import spawn from "cross-spawn";
|
|
|
85
84
|
// package.json
|
|
86
85
|
var package_default = {
|
|
87
86
|
name: "create-wdio",
|
|
88
|
-
version: "9.
|
|
87
|
+
version: "9.32.0",
|
|
89
88
|
description: "Install and setup a WebdriverIO project with all its dependencies in a single run",
|
|
90
89
|
author: "WebdriverIO Team <mail@webdriver.io>",
|
|
91
90
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/create-wdio",
|
|
@@ -114,9 +113,9 @@ var package_default = {
|
|
|
114
113
|
}
|
|
115
114
|
},
|
|
116
115
|
types: "./build/index.d.ts",
|
|
117
|
-
typeScriptVersion: "
|
|
116
|
+
typeScriptVersion: "6.0.3",
|
|
118
117
|
engines: {
|
|
119
|
-
node: ">=
|
|
118
|
+
node: ">=22.19.0"
|
|
120
119
|
},
|
|
121
120
|
repository: {
|
|
122
121
|
type: "git",
|
|
@@ -146,10 +145,9 @@ var package_default = {
|
|
|
146
145
|
inquirer: "^12.7.0",
|
|
147
146
|
"normalize-package-data": "^7.0.0",
|
|
148
147
|
"read-pkg-up": "^10.1.0",
|
|
149
|
-
"recursive-readdir": "^2.2.3",
|
|
150
148
|
semver: "^7.6.3",
|
|
151
149
|
"type-fest": "^4.41.0",
|
|
152
|
-
yargs: "^
|
|
150
|
+
yargs: "^18.2.0"
|
|
153
151
|
},
|
|
154
152
|
devDependencies: {
|
|
155
153
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -177,7 +175,7 @@ var PROGRAM_TITLE = `
|
|
|
177
175
|
`;
|
|
178
176
|
var UNSUPPORTED_NODE_VERSION = `\u26A0\uFE0F Unsupported Node.js Version Error \u26A0\uFE0F
|
|
179
177
|
You are using Node.js ${process.version} which is too old to be used with WebdriverIO.
|
|
180
|
-
Please update to Node.js
|
|
178
|
+
Please update to Node.js v22.19.0 to continue.
|
|
181
179
|
`;
|
|
182
180
|
var INSTALL_COMMAND = {
|
|
183
181
|
npm: "install",
|
|
@@ -239,6 +237,7 @@ var SUPPORTED_PACKAGES = {
|
|
|
239
237
|
service: [
|
|
240
238
|
// internal or community driver services
|
|
241
239
|
{ name: "visual", value: "@wdio/visual-service$--$visual" },
|
|
240
|
+
{ name: "ai", value: "@wdio/ai-service$--$ai" },
|
|
242
241
|
{ name: "vite", value: "wdio-vite-service$--$vite" },
|
|
243
242
|
{ name: "nuxt", value: "wdio-nuxt-service$--$nuxt" },
|
|
244
243
|
{ name: "firefox-profile", value: "@wdio/firefox-profile-service$--$firefox-profile" },
|
|
@@ -933,6 +932,11 @@ var QUESTIONNAIRE = [{
|
|
|
933
932
|
/* istanbul ignore next */
|
|
934
933
|
(answers) => answers.generateTestFiles && usesSerenity(answers)
|
|
935
934
|
)
|
|
935
|
+
}, {
|
|
936
|
+
type: "confirm",
|
|
937
|
+
name: "agentSupport",
|
|
938
|
+
message: "Set up coding agent support (AGENTS.md section and wdio-session skill)?",
|
|
939
|
+
default: true
|
|
936
940
|
}, {
|
|
937
941
|
type: "checkbox",
|
|
938
942
|
name: "reporters",
|
|
@@ -1111,17 +1115,326 @@ function getInstallCommand(pm, packages, dev) {
|
|
|
1111
1115
|
return `${pm} ${installCommand[pm]} ${packages.join(" ")} ${devParam}`;
|
|
1112
1116
|
}
|
|
1113
1117
|
|
|
1118
|
+
// src/answerFlags.ts
|
|
1119
|
+
var AnswerFlagError = class extends Error {
|
|
1120
|
+
name = "AnswerFlagError";
|
|
1121
|
+
};
|
|
1122
|
+
var HASH = "$--$";
|
|
1123
|
+
function packageChoices(packages, key = (short) => short) {
|
|
1124
|
+
return () => Object.fromEntries(packages().map(({ value }) => {
|
|
1125
|
+
const [pkg, short, purpose] = value.split(HASH);
|
|
1126
|
+
return [key(short, purpose, pkg), value];
|
|
1127
|
+
}));
|
|
1128
|
+
}
|
|
1129
|
+
var ANSWER_FLAGS = [{
|
|
1130
|
+
name: "runner",
|
|
1131
|
+
answer: "runner",
|
|
1132
|
+
type: "string",
|
|
1133
|
+
desc: "Type of testing",
|
|
1134
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.runner, (_, purpose) => purpose)
|
|
1135
|
+
}, {
|
|
1136
|
+
name: "preset",
|
|
1137
|
+
answer: "preset",
|
|
1138
|
+
type: "string",
|
|
1139
|
+
desc: "UI framework of your components (with --runner component)",
|
|
1140
|
+
choices: () => Object.fromEntries(SUPPORTED_BROWSER_RUNNER_PRESETS.map(({ name, value }) => [
|
|
1141
|
+
value === null ? "other" : value.split(HASH)[1] || name.split(" ")[0].toLowerCase(),
|
|
1142
|
+
value
|
|
1143
|
+
]))
|
|
1144
|
+
}, {
|
|
1145
|
+
name: "testingLibrary",
|
|
1146
|
+
answer: "installTestingLibrary",
|
|
1147
|
+
type: "boolean",
|
|
1148
|
+
desc: "Install Testing Library (with --preset vue, svelte, solid, react or preact)"
|
|
1149
|
+
}, {
|
|
1150
|
+
name: "desktopFramework",
|
|
1151
|
+
answer: "desktopFramework",
|
|
1152
|
+
type: "string",
|
|
1153
|
+
desc: "Desktop app framework (with --runner desktop)",
|
|
1154
|
+
choices: () => ({
|
|
1155
|
+
electron: "Electron (https://www.electronjs.org/)" /* Electron */,
|
|
1156
|
+
tauri: "Tauri (https://tauri.app/)" /* Tauri */,
|
|
1157
|
+
dioxus: "Dioxus (https://dioxuslabs.com/)" /* Dioxus */,
|
|
1158
|
+
macos: "Native macOS app" /* MacOS */
|
|
1159
|
+
})
|
|
1160
|
+
}, {
|
|
1161
|
+
name: "electronBuildTool",
|
|
1162
|
+
answer: "electronBuildTool",
|
|
1163
|
+
type: "string",
|
|
1164
|
+
desc: "Tool that builds your Electron app (with --desktop-framework electron)",
|
|
1165
|
+
choices: () => ({
|
|
1166
|
+
forge: "Electron Forge (https://www.electronforge.io/)" /* ElectronForge */,
|
|
1167
|
+
builder: "electron-builder (https://www.electron.build/)" /* ElectronBuilder */,
|
|
1168
|
+
other: "Something else" /* SomethingElse */
|
|
1169
|
+
})
|
|
1170
|
+
}, {
|
|
1171
|
+
name: "electronAppBinaryPath",
|
|
1172
|
+
answer: "electronAppBinaryPath",
|
|
1173
|
+
type: "string",
|
|
1174
|
+
desc: "Path to the built Electron app (with --electron-build-tool other)"
|
|
1175
|
+
}, {
|
|
1176
|
+
name: "tauriDriverProvider",
|
|
1177
|
+
answer: "tauriDriverProvider",
|
|
1178
|
+
type: "string",
|
|
1179
|
+
desc: "WebDriver provider for Tauri (with --desktop-framework tauri)",
|
|
1180
|
+
choices: () => ({
|
|
1181
|
+
official: "Official tauri-driver (cross-platform, Cargo-installed)" /* Official */,
|
|
1182
|
+
crabnebula: "CrabNebula tauri-driver (managed cloud / drop-in)" /* CrabNebula */,
|
|
1183
|
+
embedded: "Embedded driver via tauri-plugin-wdio-webdriver crate" /* Embedded */
|
|
1184
|
+
})
|
|
1185
|
+
}, {
|
|
1186
|
+
name: "tauriFrontendPlugin",
|
|
1187
|
+
answer: "tauriUseFrontendPlugin",
|
|
1188
|
+
type: "boolean",
|
|
1189
|
+
desc: "Install @wdio/tauri-plugin (with --desktop-framework tauri)"
|
|
1190
|
+
}, {
|
|
1191
|
+
name: "tauriAppBinaryPath",
|
|
1192
|
+
answer: "tauriAppBinaryPath",
|
|
1193
|
+
type: "string",
|
|
1194
|
+
desc: "Path to the built Tauri binary (with --desktop-framework tauri)"
|
|
1195
|
+
}, {
|
|
1196
|
+
name: "dioxusDriverProvider",
|
|
1197
|
+
answer: "dioxusDriverProvider",
|
|
1198
|
+
type: "string",
|
|
1199
|
+
desc: "WebDriver provider for Dioxus (with --desktop-framework dioxus)",
|
|
1200
|
+
choices: () => ({
|
|
1201
|
+
embedded: "Embedded in-process driver (recommended \u2014 no external driver, all platforms)" /* Embedded */,
|
|
1202
|
+
external: "External driver via wdio-dioxus-driver (Windows only)" /* External */
|
|
1203
|
+
})
|
|
1204
|
+
}, {
|
|
1205
|
+
name: "dioxusAppBinaryPath",
|
|
1206
|
+
answer: "dioxusAppBinaryPath",
|
|
1207
|
+
type: "string",
|
|
1208
|
+
desc: "Path to the Dioxus debug binary (with --desktop-framework dioxus)"
|
|
1209
|
+
}, {
|
|
1210
|
+
name: "backend",
|
|
1211
|
+
answer: "backend",
|
|
1212
|
+
type: "string",
|
|
1213
|
+
desc: "Where the automation backend runs (with --runner e2e)",
|
|
1214
|
+
choices: () => ({
|
|
1215
|
+
local: "On my local machine" /* Local */,
|
|
1216
|
+
experitest: "In the cloud using Experitest" /* Experitest */,
|
|
1217
|
+
saucelabs: "In the cloud using Sauce Labs" /* Saucelabs */,
|
|
1218
|
+
browserstack: "In the cloud using BrowserStack" /* Browserstack */,
|
|
1219
|
+
other: "In the cloud using Testingbot or LambdaTest or a different service" /* OtherVendors */,
|
|
1220
|
+
grid: "I have my own Selenium cloud" /* Grid */
|
|
1221
|
+
})
|
|
1222
|
+
}, {
|
|
1223
|
+
name: "environment",
|
|
1224
|
+
answer: "e2eEnvironment",
|
|
1225
|
+
type: "string",
|
|
1226
|
+
desc: "What to automate (with --runner e2e). Implied by --browsers or --mobile-environment",
|
|
1227
|
+
choices: () => ({ web: "web", mobile: "mobile" })
|
|
1228
|
+
}, {
|
|
1229
|
+
name: "mobileEnvironment",
|
|
1230
|
+
answer: "mobileEnvironment",
|
|
1231
|
+
type: "string",
|
|
1232
|
+
desc: "Mobile platform (with --environment mobile)",
|
|
1233
|
+
choices: () => ({ android: "android", ios: "ios" })
|
|
1234
|
+
}, {
|
|
1235
|
+
name: "browsers",
|
|
1236
|
+
answer: "browserEnvironment",
|
|
1237
|
+
type: "array",
|
|
1238
|
+
desc: "Comma-separated browsers to start with (with --environment web)",
|
|
1239
|
+
choices: () => ({ chrome: "chrome", firefox: "firefox", safari: "safari", edge: "MicrosoftEdge" })
|
|
1240
|
+
}, {
|
|
1241
|
+
name: "hostname",
|
|
1242
|
+
answer: "hostname",
|
|
1243
|
+
type: "string",
|
|
1244
|
+
desc: "Host of the cloud service or Selenium server (with --backend other or grid)"
|
|
1245
|
+
}, {
|
|
1246
|
+
name: "port",
|
|
1247
|
+
answer: "port",
|
|
1248
|
+
type: "string",
|
|
1249
|
+
desc: "Port of the cloud service or Selenium server (with --backend other or grid)"
|
|
1250
|
+
}, {
|
|
1251
|
+
name: "path",
|
|
1252
|
+
answer: "path",
|
|
1253
|
+
type: "string",
|
|
1254
|
+
desc: "Path of the Selenium server (with --backend grid)"
|
|
1255
|
+
}, {
|
|
1256
|
+
name: "envUser",
|
|
1257
|
+
answer: "env_user",
|
|
1258
|
+
type: "string",
|
|
1259
|
+
desc: "Environment variable for the cloud username (with --backend saucelabs or browserstack, or other with a lambdatest.com --hostname)"
|
|
1260
|
+
}, {
|
|
1261
|
+
name: "envKey",
|
|
1262
|
+
answer: "env_key",
|
|
1263
|
+
type: "string",
|
|
1264
|
+
desc: "Environment variable for the cloud access key (with --backend saucelabs or browserstack, or other with a lambdatest.com --hostname)"
|
|
1265
|
+
}, {
|
|
1266
|
+
name: "region",
|
|
1267
|
+
answer: "region",
|
|
1268
|
+
type: "string",
|
|
1269
|
+
desc: "Sauce Labs region (with --backend saucelabs)",
|
|
1270
|
+
choices: () => Object.fromEntries(Object.values(RegionOptions).map((region) => [region, region]))
|
|
1271
|
+
}, {
|
|
1272
|
+
name: "sauceConnect",
|
|
1273
|
+
answer: "useSauceConnect",
|
|
1274
|
+
type: "boolean",
|
|
1275
|
+
desc: "Set up Sauce Connect for a local app (with --backend saucelabs)"
|
|
1276
|
+
}, {
|
|
1277
|
+
name: "expAccessKey",
|
|
1278
|
+
answer: "expEnvAccessKey",
|
|
1279
|
+
type: "string",
|
|
1280
|
+
desc: "Experitest access key (with --backend experitest)"
|
|
1281
|
+
}, {
|
|
1282
|
+
name: "expHostname",
|
|
1283
|
+
answer: "expEnvHostname",
|
|
1284
|
+
type: "string",
|
|
1285
|
+
desc: "Experitest cloud host (with --backend experitest)"
|
|
1286
|
+
}, {
|
|
1287
|
+
name: "expPort",
|
|
1288
|
+
answer: "expEnvPort",
|
|
1289
|
+
type: "string",
|
|
1290
|
+
desc: "Experitest cloud port (with --backend experitest)"
|
|
1291
|
+
}, {
|
|
1292
|
+
name: "expProtocol",
|
|
1293
|
+
answer: "expEnvProtocol",
|
|
1294
|
+
type: "string",
|
|
1295
|
+
desc: "Experitest protocol (with --backend experitest and a port other than 80 or 443)",
|
|
1296
|
+
choices: () => ({ http: "http", https: "https" })
|
|
1297
|
+
}, {
|
|
1298
|
+
name: "framework",
|
|
1299
|
+
answer: "framework",
|
|
1300
|
+
type: "string",
|
|
1301
|
+
desc: "Test framework",
|
|
1302
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.framework, (short, purpose, pkg) => pkg.startsWith("@serenity-js") ? `serenity-${purpose}` : short)
|
|
1303
|
+
}, {
|
|
1304
|
+
name: "typescript",
|
|
1305
|
+
answer: "isUsingTypeScript",
|
|
1306
|
+
type: "boolean",
|
|
1307
|
+
desc: "Write tests in TypeScript"
|
|
1308
|
+
}, {
|
|
1309
|
+
name: "generateTestFiles",
|
|
1310
|
+
answer: "generateTestFiles",
|
|
1311
|
+
type: "boolean",
|
|
1312
|
+
desc: "Generate example test files"
|
|
1313
|
+
}, {
|
|
1314
|
+
name: "specs",
|
|
1315
|
+
answer: "specs",
|
|
1316
|
+
type: "string",
|
|
1317
|
+
desc: "Location of spec or feature files (with --generate-test-files)"
|
|
1318
|
+
}, {
|
|
1319
|
+
name: "stepDefinitions",
|
|
1320
|
+
answer: "stepDefinitions",
|
|
1321
|
+
type: "string",
|
|
1322
|
+
desc: "Location of step definitions (with --framework cucumber)"
|
|
1323
|
+
}, {
|
|
1324
|
+
name: "pageObjects",
|
|
1325
|
+
answer: "usePageObjects",
|
|
1326
|
+
type: "boolean",
|
|
1327
|
+
desc: "Generate page objects (with --runner e2e and --generate-test-files)"
|
|
1328
|
+
}, {
|
|
1329
|
+
name: "pages",
|
|
1330
|
+
answer: "pages",
|
|
1331
|
+
type: "string",
|
|
1332
|
+
desc: "Location of page objects (with --page-objects)"
|
|
1333
|
+
}, {
|
|
1334
|
+
name: "serenityLibPath",
|
|
1335
|
+
answer: "serenityLibPath",
|
|
1336
|
+
type: "string",
|
|
1337
|
+
desc: "Location of the Serenity/JS Screenplay library (with --framework serenity-*)"
|
|
1338
|
+
}, {
|
|
1339
|
+
name: "agentSupport",
|
|
1340
|
+
answer: "agentSupport",
|
|
1341
|
+
type: "boolean",
|
|
1342
|
+
desc: "Write the AGENTS.md section and the wdio-session skill"
|
|
1343
|
+
}, {
|
|
1344
|
+
name: "reporters",
|
|
1345
|
+
answer: "reporters",
|
|
1346
|
+
type: "array",
|
|
1347
|
+
desc: "Comma-separated reporters",
|
|
1348
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.reporter)
|
|
1349
|
+
}, {
|
|
1350
|
+
name: "plugins",
|
|
1351
|
+
answer: "plugins",
|
|
1352
|
+
type: "array",
|
|
1353
|
+
desc: "Comma-separated plugins",
|
|
1354
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.plugin)
|
|
1355
|
+
}, {
|
|
1356
|
+
name: "visualTesting",
|
|
1357
|
+
answer: "includeVisualTesting",
|
|
1358
|
+
type: "boolean",
|
|
1359
|
+
desc: "Add visual testing (with --runner e2e or component)"
|
|
1360
|
+
}, {
|
|
1361
|
+
name: "services",
|
|
1362
|
+
answer: "services",
|
|
1363
|
+
type: "array",
|
|
1364
|
+
desc: "Comma-separated services, replaces the ones the wizard picks for your setup",
|
|
1365
|
+
choices: packageChoices(() => SUPPORTED_PACKAGES.service)
|
|
1366
|
+
}, {
|
|
1367
|
+
name: "npmInstall",
|
|
1368
|
+
answer: "npmInstall",
|
|
1369
|
+
type: "boolean",
|
|
1370
|
+
desc: "Install the dependencies"
|
|
1371
|
+
}];
|
|
1372
|
+
function toKebabCase(name) {
|
|
1373
|
+
return name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
1374
|
+
}
|
|
1375
|
+
function flagFor(answer) {
|
|
1376
|
+
return ANSWER_FLAGS.find((flag) => flag.answer === answer);
|
|
1377
|
+
}
|
|
1378
|
+
var ANSWER_FLAGS_HELP = [
|
|
1379
|
+
"Every wizard question has a flag. A flag answers its question and the wizard",
|
|
1380
|
+
"asks the rest, or uses the defaults for the rest with --yes. Boolean flags",
|
|
1381
|
+
"take a --no- prefix, e.g. --no-typescript. A flag for a question the wizard",
|
|
1382
|
+
"does not ask for your setup is an error, and so is a value it does not offer."
|
|
1383
|
+
].join("\n");
|
|
1384
|
+
function isAsked(question, answers) {
|
|
1385
|
+
const when = question.when;
|
|
1386
|
+
if (typeof when === "function") {
|
|
1387
|
+
return Boolean(when(answers));
|
|
1388
|
+
}
|
|
1389
|
+
return typeof when === "undefined" || Boolean(when);
|
|
1390
|
+
}
|
|
1391
|
+
function choiceValues(question, answers) {
|
|
1392
|
+
if (!question.choices) {
|
|
1393
|
+
return void 0;
|
|
1394
|
+
}
|
|
1395
|
+
const choices = typeof question.choices === "function" ? question.choices(answers) : question.choices;
|
|
1396
|
+
return choices.filter((choice) => typeof choice !== "undefined").map((choice) => choice && typeof choice === "object" ? choice.value : choice);
|
|
1397
|
+
}
|
|
1398
|
+
function assertAnswerFlagsApply(flagAnswers, answers) {
|
|
1399
|
+
for (const [answer, value] of Object.entries(flagAnswers)) {
|
|
1400
|
+
const flag = flagFor(answer);
|
|
1401
|
+
const usage = `--${toKebabCase(flag.name)}`;
|
|
1402
|
+
const questions = QUESTIONNAIRE.filter((question) => question.name === answer && isAsked(question, answers));
|
|
1403
|
+
if (questions.length === 0) {
|
|
1404
|
+
throw new AnswerFlagError(`${usage} does not apply to this setup. ${flag.desc}.`);
|
|
1405
|
+
}
|
|
1406
|
+
const offered = questions.map((question) => choiceValues(question, answers));
|
|
1407
|
+
if (offered.some((values) => typeof values === "undefined")) {
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
const allowed = new Set(offered.flat());
|
|
1411
|
+
const rejected = [value].flat().filter((item) => !allowed.has(item));
|
|
1412
|
+
if (rejected.length === 0) {
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
const cliValues = Object.entries(flag.choices());
|
|
1416
|
+
const toCli = (item) => cliValues.find(([, v]) => v === item)?.[0] ?? String(item);
|
|
1417
|
+
throw new AnswerFlagError(
|
|
1418
|
+
`${usage} ${rejected.map(toCli).join(",")} is not available for this setup. Use one of: ${[...new Set([...allowed].map(toCli))].join(", ")}`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1114
1423
|
// src/utils.ts
|
|
1115
1424
|
var NPM_COMMAND = /^win/.test(process.platform) ? "npm.cmd" : "npm";
|
|
1116
1425
|
var __dirname = path2.dirname(url.fileURLToPath(import.meta.url));
|
|
1117
1426
|
process.on("SIGINT", () => printAndExit(void 0, "SIGINT"));
|
|
1118
1427
|
var TEMPLATE_ROOT_DIR = process.env.WDIO_UNIT_TESTS ? path2.join(__dirname, "templates", "exampleFiles") : path2.join(__dirname, "..", "templates", "exampleFiles");
|
|
1119
|
-
function runProgram(command2, args, options) {
|
|
1428
|
+
function runProgram(command2, args, options, passUsageError = false) {
|
|
1120
1429
|
const child = spawn(command2, args, { stdio: "inherit", ...options });
|
|
1121
1430
|
return new Promise((resolve, rejects) => {
|
|
1122
1431
|
let error;
|
|
1123
1432
|
child.on("error", (e) => error = e);
|
|
1124
1433
|
child.on("close", (code, signal) => {
|
|
1434
|
+
if (passUsageError && code === 2) {
|
|
1435
|
+
process.exit(2);
|
|
1436
|
+
return rejects(`Usage error calling: ${command2} ${args.join(" ")}`);
|
|
1437
|
+
}
|
|
1125
1438
|
if (code !== 0) {
|
|
1126
1439
|
const errorMessage = error && error.message || `Error calling: ${command2} ${args.join(" ")}`;
|
|
1127
1440
|
printAndExit(errorMessage, signal);
|
|
@@ -1145,24 +1458,35 @@ function convertPackageHashToObject(pkg, hash = "$--$") {
|
|
|
1145
1458
|
const [p, short, purpose] = pkg.split(hash);
|
|
1146
1459
|
return { package: p, short, purpose };
|
|
1147
1460
|
}
|
|
1148
|
-
async function getAnswers(yes) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1461
|
+
async function getAnswers(yes, flagAnswers = {}) {
|
|
1462
|
+
const answers = yes ? await getDefaultAnswers(flagAnswers) : await promptAnswers(flagAnswers);
|
|
1463
|
+
assertAnswerFlagsApply(flagAnswers, answers);
|
|
1464
|
+
return answers;
|
|
1465
|
+
}
|
|
1466
|
+
async function getDefaultAnswers(flagAnswers) {
|
|
1467
|
+
const ignoredQuestions = ["e2eEnvironment"];
|
|
1468
|
+
const filteredQuestionaire = QUESTIONNAIRE.filter((question) => !ignoredQuestions.includes(question.name));
|
|
1469
|
+
const answers = { ...flagAnswers };
|
|
1470
|
+
for (const question of filteredQuestionaire) {
|
|
1471
|
+
if (Object.prototype.hasOwnProperty.call(flagAnswers, question.name)) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
if (question.when && !question.when(answers)) {
|
|
1475
|
+
continue;
|
|
1160
1476
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1477
|
+
if (typeof question.default === "undefined" && !question.choices) {
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1480
|
+
Object.assign(answers, {
|
|
1481
|
+
[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] : {}
|
|
1482
|
+
});
|
|
1165
1483
|
}
|
|
1484
|
+
answers.isUsingTypeScript = await answers.isUsingTypeScript;
|
|
1485
|
+
answers.specs = await answers.specs;
|
|
1486
|
+
answers.pages = await answers.pages;
|
|
1487
|
+
return answers;
|
|
1488
|
+
}
|
|
1489
|
+
async function promptAnswers(flagAnswers) {
|
|
1166
1490
|
const projectProps = await getProjectProps(process.cwd());
|
|
1167
1491
|
const isProjectExisting = Boolean(projectProps);
|
|
1168
1492
|
const nameInPackageJsonIsNotCreateWdioDefault = projectProps?.packageJson?.name !== "my-new-project";
|
|
@@ -1198,7 +1522,7 @@ async function getAnswers(yes) {
|
|
|
1198
1522
|
if (answers.createPackageJSON) {
|
|
1199
1523
|
answers.projectRoot = process.cwd();
|
|
1200
1524
|
}
|
|
1201
|
-
return inquirer.prompt(QUESTIONNAIRE, answers);
|
|
1525
|
+
return inquirer.prompt(QUESTIONNAIRE, { ...answers, ...flagAnswers });
|
|
1202
1526
|
}
|
|
1203
1527
|
async function getProjectProps(cwd = process.cwd()) {
|
|
1204
1528
|
try {
|
|
@@ -1254,11 +1578,8 @@ async function createPackageJSON(parsedAnswers) {
|
|
|
1254
1578
|
return;
|
|
1255
1579
|
}
|
|
1256
1580
|
if (parsedAnswers.createPackageJSON === false) {
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
return !process.env.WDIO_UNIT_TESTS && process.exit(0);
|
|
1260
|
-
}
|
|
1261
|
-
return;
|
|
1581
|
+
console.log(`No WebdriverIO configuration found in "${parsedAnswers.wdioConfigPath}"`);
|
|
1582
|
+
process.exit(0);
|
|
1262
1583
|
}
|
|
1263
1584
|
if (parsedAnswers.createPackageJSON) {
|
|
1264
1585
|
console.log(`Creating a ${chalk2.bold("package.json")} for the directory...`);
|
|
@@ -1305,10 +1626,13 @@ async function setupTypeScript(parsedAnswers) {
|
|
|
1305
1626
|
const config = {
|
|
1306
1627
|
compilerOptions: {
|
|
1307
1628
|
// compiler
|
|
1308
|
-
moduleResolution:
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1629
|
+
// TypeScript 6 deprecates `moduleResolution: 'node'` (node10). ESM uses
|
|
1630
|
+
// `bundler`, which resolves imports as `tsx` does; CommonJS uses `NodeNext`,
|
|
1631
|
+
// which follows the `type` of the project's package.json.
|
|
1632
|
+
moduleResolution: parsedAnswers.esmSupport ? "bundler" : "NodeNext",
|
|
1633
|
+
module: parsedAnswers.esmSupport ? "ESNext" : "NodeNext",
|
|
1634
|
+
target: "es2024",
|
|
1635
|
+
lib: ["es2024", "dom"],
|
|
1312
1636
|
types,
|
|
1313
1637
|
skipLibCheck: true,
|
|
1314
1638
|
// bundler
|
|
@@ -1453,8 +1777,10 @@ function getPreset(parsedAnswers) {
|
|
|
1453
1777
|
function addServiceDeps(names, packages, update = false) {
|
|
1454
1778
|
if (names.some(({ short }) => short === "appium")) {
|
|
1455
1779
|
const result = execSync("appium --version || echo APPIUM_MISSING", { stdio: "pipe" }).toString().trim();
|
|
1456
|
-
|
|
1457
|
-
|
|
1780
|
+
const major = Number.parseInt(result.replace(/^v/i, "").split(".")[0] || "", 10);
|
|
1781
|
+
const hasAppium3 = result !== "APPIUM_MISSING" && !Number.isNaN(major) && major >= 3;
|
|
1782
|
+
if (!hasAppium3) {
|
|
1783
|
+
packages.push("appium@^3");
|
|
1458
1784
|
} else if (update) {
|
|
1459
1785
|
console.log(
|
|
1460
1786
|
"\n=======",
|
|
@@ -1514,10 +1840,15 @@ async function createWDIOScript(parsedAnswers) {
|
|
|
1514
1840
|
console.log(chalk2.green(chalk2.bold("\u2714 Success!")));
|
|
1515
1841
|
return true;
|
|
1516
1842
|
}
|
|
1517
|
-
async function runAppiumInstaller(parsedAnswers) {
|
|
1843
|
+
async function runAppiumInstaller(parsedAnswers, yes = false) {
|
|
1518
1844
|
if (parsedAnswers.e2eEnvironment !== "mobile") {
|
|
1519
1845
|
return;
|
|
1520
1846
|
}
|
|
1847
|
+
if (yes || !process.stdin.isTTY) {
|
|
1848
|
+
return console.log(
|
|
1849
|
+
"Skipping the interactive Appium installer. Run `npx appium-installer` later, or see https://appium.io/docs/en/latest/quickstart/"
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1521
1852
|
const answer = await inquirer.prompt({
|
|
1522
1853
|
name: "continueWithAppiumSetup",
|
|
1523
1854
|
message: "Continue with Appium setup using appium-installer (https://github.com/AppiumTestDistribution/appium-installer)?",
|
|
@@ -1613,6 +1944,10 @@ async function generateTestFiles(answers) {
|
|
|
1613
1944
|
}
|
|
1614
1945
|
return generateBrowserRunnerTestFiles(answers);
|
|
1615
1946
|
}
|
|
1947
|
+
async function readDir(dir) {
|
|
1948
|
+
const entries = await fs2.readdir(dir, { recursive: true, withFileTypes: true });
|
|
1949
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => path2.join(entry.parentPath, entry.name));
|
|
1950
|
+
}
|
|
1616
1951
|
async function generateSerenityExamples(answers) {
|
|
1617
1952
|
const templateDirectories = Object.entries({
|
|
1618
1953
|
[answers.destSpecRootPath]: path2.join(TEMPLATE_ROOT_DIR, "serenity-js", answers.serenityAdapter),
|
|
@@ -1637,10 +1972,7 @@ async function generateLocalRunnerTestFiles(answers) {
|
|
|
1637
1972
|
if (answers.usePageObjects) {
|
|
1638
1973
|
testFiles.push(path2.join(TEMPLATE_ROOT_DIR, "pageobjects"));
|
|
1639
1974
|
}
|
|
1640
|
-
const files = (await Promise.all(testFiles.map((
|
|
1641
|
-
dirPath,
|
|
1642
|
-
[(file, stats) => !stats.isDirectory() && !(file.endsWith(".ejs") || file.endsWith(".feature"))]
|
|
1643
|
-
)))).reduce((cur, acc) => [...acc, ...cur], []);
|
|
1975
|
+
const files = (await Promise.all(testFiles.map(readDir))).flat().filter((file) => file.endsWith(".ejs") || file.endsWith(".feature"));
|
|
1644
1976
|
await Promise.all(files.map(async (file) => {
|
|
1645
1977
|
const renderedTpl = await renderFile(file, { answers });
|
|
1646
1978
|
const isJSX = answers.preset && TSX_BASED_FRAMEWORKS.includes(answers.preset);
|
|
@@ -1710,10 +2042,79 @@ function replaceConfig(config, type, name) {
|
|
|
1710
2042
|
import fss from "node:fs";
|
|
1711
2043
|
|
|
1712
2044
|
// src/cli/utils.ts
|
|
1713
|
-
import
|
|
1714
|
-
import
|
|
2045
|
+
import path4 from "node:path";
|
|
2046
|
+
import fs4 from "node:fs/promises";
|
|
1715
2047
|
import inquirer2 from "inquirer";
|
|
1716
|
-
|
|
2048
|
+
|
|
2049
|
+
// src/cli/agent.ts
|
|
2050
|
+
import fs3 from "node:fs";
|
|
2051
|
+
import path3 from "node:path";
|
|
2052
|
+
import { fileURLToPath } from "node:url";
|
|
2053
|
+
var AGENT_SECTION_HEADING = "## End-to-end tests (WebdriverIO v10)";
|
|
2054
|
+
var AGENT_SECTION = `${AGENT_SECTION_HEADING}
|
|
2055
|
+
|
|
2056
|
+
- 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.
|
|
2057
|
+
- Config: \`wdio.conf.ts\`. Specs: \`test/specs/**/*.e2e.ts\`. Page objects: \`test/pageobjects/\`.
|
|
2058
|
+
- Run all tests: \`npx wdio run wdio.conf.ts\`
|
|
2059
|
+
- Run a single spec: \`npx wdio run wdio.conf.ts --spec test/specs/login.e2e.ts\`
|
|
2060
|
+
- Tests are async: always \`await\` commands, e.g. \`await $('button').click()\`. Never use the removed sync mode.
|
|
2061
|
+
- Prefer user-facing selectors: accessibility name or text (\`$('aria/Submit')\`, \`$('button=Submit')\`), then \`data-testid\`. Avoid XPath and generated CSS classes.
|
|
2062
|
+
- Rely on auto-waiting and \`expect-webdriverio\` matchers (\`await expect($('h1')).toHaveText('Welcome')\`) instead of \`browser.pause()\`.
|
|
2063
|
+
- To explore the app or verify a selector, use the \`wdio-mcp\` MCP server.
|
|
2064
|
+
- To drive the app from the shell, follow \`.agents/skills/wdio-session/SKILL.md\` (\`npx wdio session\`).
|
|
2065
|
+
- When a test fails, read the DevTools trace in \`test-results/\` (see \`transcript.md\`) before changing code.
|
|
2066
|
+
`;
|
|
2067
|
+
function readIfExists(file) {
|
|
2068
|
+
return fs3.existsSync(file) ? fs3.readFileSync(file, "utf-8") : void 0;
|
|
2069
|
+
}
|
|
2070
|
+
function readSessionSkill(start = path3.dirname(fileURLToPath(import.meta.url))) {
|
|
2071
|
+
let dir = start;
|
|
2072
|
+
for (let i = 0; i < 8; i++) {
|
|
2073
|
+
const candidates = [
|
|
2074
|
+
path3.join(dir, "wdio-session", "src", "skill", "SKILL.md"),
|
|
2075
|
+
path3.join(dir, "packages", "wdio-session", "src", "skill", "SKILL.md"),
|
|
2076
|
+
path3.join(dir, "node_modules", "@wdio", "session", "src", "skill", "SKILL.md"),
|
|
2077
|
+
path3.join(dir, "templates", "wdio-session", "SKILL.md")
|
|
2078
|
+
];
|
|
2079
|
+
for (const candidate of candidates) {
|
|
2080
|
+
const markdown = readIfExists(candidate);
|
|
2081
|
+
if (markdown) {
|
|
2082
|
+
return markdown;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
const parent = path3.dirname(dir);
|
|
2086
|
+
if (parent === dir) {
|
|
2087
|
+
break;
|
|
2088
|
+
}
|
|
2089
|
+
dir = parent;
|
|
2090
|
+
}
|
|
2091
|
+
throw new Error("Cannot find the wdio-session skill (src/skill/SKILL.md).");
|
|
2092
|
+
}
|
|
2093
|
+
function writeAgentSupport(root) {
|
|
2094
|
+
const skillDest = path3.join(root, ".agents", "skills", "wdio-session", "SKILL.md");
|
|
2095
|
+
if (!fs3.existsSync(skillDest)) {
|
|
2096
|
+
const markdown = readSessionSkill();
|
|
2097
|
+
fs3.mkdirSync(path3.dirname(skillDest), { recursive: true });
|
|
2098
|
+
fs3.writeFileSync(skillDest, markdown);
|
|
2099
|
+
}
|
|
2100
|
+
const agentsPath = path3.join(root, "AGENTS.md");
|
|
2101
|
+
const agents = readIfExists(agentsPath) || "";
|
|
2102
|
+
if (!agents.includes(AGENT_SECTION_HEADING)) {
|
|
2103
|
+
const sep = agents.length === 0 ? "" : agents.endsWith("\n") ? "\n" : "\n\n";
|
|
2104
|
+
fs3.writeFileSync(agentsPath, agents + sep + AGENT_SECTION);
|
|
2105
|
+
}
|
|
2106
|
+
const gitignorePath = path3.join(root, ".gitignore");
|
|
2107
|
+
const gitignore = readIfExists(gitignorePath) || "";
|
|
2108
|
+
const hasEntry = gitignore.split("\n").some((line) => line.trim() === ".wdio/session/" || line.trim() === ".wdio/session");
|
|
2109
|
+
if (!hasEntry) {
|
|
2110
|
+
const sep = gitignore.length === 0 || gitignore.endsWith("\n") ? "" : "\n";
|
|
2111
|
+
fs3.writeFileSync(gitignorePath, `${gitignore}${sep}.wdio/session/
|
|
2112
|
+
`);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// src/cli/utils.ts
|
|
2117
|
+
async function runConfigCommand(parsedAnswers, npmTag, yes = false) {
|
|
1717
2118
|
console.log("\n");
|
|
1718
2119
|
await createPackageJSON(parsedAnswers);
|
|
1719
2120
|
await setupTypeScript(parsedAnswers);
|
|
@@ -1742,10 +2143,13 @@ async function runConfigCommand(parsedAnswers, npmTag) {
|
|
|
1742
2143
|
extraInfo: extraInfoParts.join("\n")
|
|
1743
2144
|
})
|
|
1744
2145
|
);
|
|
1745
|
-
await runAppiumInstaller(parsedAnswers);
|
|
2146
|
+
await runAppiumInstaller(parsedAnswers, yes);
|
|
2147
|
+
if (parsedAnswers.rawAnswers?.agentSupport) {
|
|
2148
|
+
writeAgentSupport(parsedAnswers.projectRootDir);
|
|
2149
|
+
}
|
|
1746
2150
|
}
|
|
1747
2151
|
async function missingConfigurationPrompt(command2, configPath, runConfigCmd = runConfigCommand) {
|
|
1748
|
-
const message = `Could not execute "${command2}" due to missing configuration, file "${
|
|
2152
|
+
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?`;
|
|
1749
2153
|
const { config } = await inquirer2.prompt({
|
|
1750
2154
|
type: "confirm",
|
|
1751
2155
|
name: "config",
|
|
@@ -1754,10 +2158,7 @@ async function missingConfigurationPrompt(command2, configPath, runConfigCmd = r
|
|
|
1754
2158
|
});
|
|
1755
2159
|
if (!config) {
|
|
1756
2160
|
console.log(`No WebdriverIO configuration found in "${process.cwd()}"`);
|
|
1757
|
-
|
|
1758
|
-
process.exit(0);
|
|
1759
|
-
}
|
|
1760
|
-
return configPath;
|
|
2161
|
+
process.exit(0);
|
|
1761
2162
|
}
|
|
1762
2163
|
const parsedAnswers = await parseAnswers(false);
|
|
1763
2164
|
await runConfigCmd(parsedAnswers, "latest");
|
|
@@ -1769,8 +2170,8 @@ function resolveTsConfigFilePath({
|
|
|
1769
2170
|
specs,
|
|
1770
2171
|
runnerShort
|
|
1771
2172
|
}) {
|
|
1772
|
-
const rootTsConfigPath =
|
|
1773
|
-
const dedicatedTsConfigPath =
|
|
2173
|
+
const rootTsConfigPath = path4.resolve(projectRootDir, "tsconfig.json");
|
|
2174
|
+
const dedicatedTsConfigPath = path4.resolve(
|
|
1774
2175
|
projectRootDir,
|
|
1775
2176
|
`tsconfig.${runnerShort === "local" ? "e2e" : "wdio"}.json`
|
|
1776
2177
|
);
|
|
@@ -1780,13 +2181,13 @@ function resolveTsConfigFilePath({
|
|
|
1780
2181
|
if (!specs) {
|
|
1781
2182
|
return dedicatedTsConfigPath;
|
|
1782
2183
|
}
|
|
1783
|
-
const specPathWithoutGlobs = specs.split(
|
|
1784
|
-
const candidate =
|
|
1785
|
-
return
|
|
2184
|
+
const specPathWithoutGlobs = specs.split(path4.sep).filter((segment) => !segment.includes("*")).join(path4.sep);
|
|
2185
|
+
const candidate = path4.resolve(projectRootDir, path4.dirname(specPathWithoutGlobs), "tsconfig.json");
|
|
2186
|
+
return path4.resolve(candidate) === rootTsConfigPath ? dedicatedTsConfigPath : candidate;
|
|
1786
2187
|
}
|
|
1787
|
-
var parseAnswers = async function(yes) {
|
|
2188
|
+
var parseAnswers = async function(yes, flagAnswers = {}) {
|
|
1788
2189
|
console.log(CONFIG_HELPER_INTRO);
|
|
1789
|
-
const answers = await getAnswers(yes);
|
|
2190
|
+
const answers = await getAnswers(yes, flagAnswers);
|
|
1790
2191
|
const frameworkPackage = convertPackageHashToObject(answers.framework);
|
|
1791
2192
|
const runnerPackage = convertPackageHashToObject(answers.runner || SUPPORTED_PACKAGES.runner[0].value);
|
|
1792
2193
|
const servicePackages = answers.services.map((service) => convertPackageHashToObject(service));
|
|
@@ -1805,7 +2206,7 @@ var parseAnswers = async function(yes) {
|
|
|
1805
2206
|
...servicePackages.map((service) => service.package),
|
|
1806
2207
|
...serenityPackages
|
|
1807
2208
|
].filter(Boolean);
|
|
1808
|
-
const hasRootTSConfig = await
|
|
2209
|
+
const hasRootTSConfig = await fs4.access(path4.resolve(projectRootDir, "tsconfig.json")).then(() => true, () => false);
|
|
1809
2210
|
const tsConfigFilePath = resolveTsConfigFilePath({
|
|
1810
2211
|
projectRootDir,
|
|
1811
2212
|
hasRootTSConfig,
|
|
@@ -1815,7 +2216,7 @@ var parseAnswers = async function(yes) {
|
|
|
1815
2216
|
const parsedPaths = getPathForFileGeneration(answers, projectRootDir);
|
|
1816
2217
|
const isUsingTypeScript = answers.isUsingTypeScript;
|
|
1817
2218
|
const wdioConfigFilename = `wdio.conf.${isUsingTypeScript ? "ts" : "js"}`;
|
|
1818
|
-
const wdioConfigPath =
|
|
2219
|
+
const wdioConfigPath = path4.resolve(projectRootDir, wdioConfigFilename);
|
|
1819
2220
|
return {
|
|
1820
2221
|
projectName: projectProps?.packageJson.name || "Test Suite",
|
|
1821
2222
|
// default values required in templates
|
|
@@ -1834,8 +2235,8 @@ var parseAnswers = async function(yes) {
|
|
|
1834
2235
|
reporters: reporterPackages.map(({ short }) => short),
|
|
1835
2236
|
plugins: pluginPackages.map(({ short }) => short),
|
|
1836
2237
|
services: servicePackages.map(({ short }) => short),
|
|
1837
|
-
specs: answers.specs && `./${
|
|
1838
|
-
stepDefinitions: answers.stepDefinitions && `./${
|
|
2238
|
+
specs: answers.specs && `./${path4.relative(projectRootDir, answers.specs).replaceAll(path4.sep, "/")}`,
|
|
2239
|
+
stepDefinitions: answers.stepDefinitions && `./${path4.relative(projectRootDir, answers.stepDefinitions).replaceAll(path4.sep, "/")}`,
|
|
1839
2240
|
packagesToInstall,
|
|
1840
2241
|
isUsingTypeScript,
|
|
1841
2242
|
esmSupport: projectProps && !projectProps.esmSupported ? false : true,
|
|
@@ -1850,20 +2251,20 @@ var parseAnswers = async function(yes) {
|
|
|
1850
2251
|
relativePath: parsedPaths.relativePath,
|
|
1851
2252
|
hasRootTSConfig,
|
|
1852
2253
|
tsConfigFilePath,
|
|
1853
|
-
tsProject: `./${
|
|
2254
|
+
tsProject: `./${path4.relative(projectRootDir, tsConfigFilePath).replaceAll(path4.sep, "/")}`,
|
|
1854
2255
|
wdioConfigPath
|
|
1855
2256
|
};
|
|
1856
2257
|
};
|
|
1857
2258
|
async function canAccessConfigPath(configPathNoExtension, configPath) {
|
|
1858
2259
|
return new Promise((resolve, reject) => {
|
|
1859
2260
|
if (configPath) {
|
|
1860
|
-
|
|
2261
|
+
fs4.access(configPath).then(() => resolve(configPath), reject);
|
|
1861
2262
|
} else {
|
|
1862
2263
|
reject();
|
|
1863
2264
|
}
|
|
1864
2265
|
}).catch(() => Promise.all(SUPPORTED_CONFIG_FILE_EXTENSION.map(async (supportedExtension) => {
|
|
1865
2266
|
const configPathWithExtension = `${configPathNoExtension}.${supportedExtension}`;
|
|
1866
|
-
return
|
|
2267
|
+
return fs4.access(configPathWithExtension).then(() => configPathWithExtension, () => void 0);
|
|
1867
2268
|
})).then(
|
|
1868
2269
|
(configFilePaths) => configFilePaths.find(Boolean),
|
|
1869
2270
|
() => void 0
|
|
@@ -1929,7 +2330,7 @@ Available options for a ${type} are:
|
|
|
1929
2330
|
}
|
|
1930
2331
|
process.exit(0);
|
|
1931
2332
|
}
|
|
1932
|
-
const defaultPath =
|
|
2333
|
+
const defaultPath = path5.resolve(process.cwd(), "wdio.conf");
|
|
1933
2334
|
const wdioConfPathWithNoExtension = config ? (await formatConfigFilePaths(config)).fullPathNoExtension : defaultPath;
|
|
1934
2335
|
const wdioConfPath = await canAccessConfigPath(wdioConfPathWithNoExtension);
|
|
1935
2336
|
if (!wdioConfPath) {
|
|
@@ -1940,7 +2341,7 @@ Available options for a ${type} are:
|
|
|
1940
2341
|
process.exit(1);
|
|
1941
2342
|
}
|
|
1942
2343
|
}
|
|
1943
|
-
const configFile = await
|
|
2344
|
+
const configFile = await fs5.readFile(wdioConfPath, { encoding: "utf-8" });
|
|
1944
2345
|
const match = findInConfig(configFile, type);
|
|
1945
2346
|
const projectRoot = await getProjectRoot();
|
|
1946
2347
|
if (match && match[0].includes(name)) {
|
|
@@ -1959,9 +2360,9 @@ Available options for a ${type} are:
|
|
|
1959
2360
|
console.log(`Package "${selectedPackage.package}" installed successfully.`);
|
|
1960
2361
|
const newConfig = replaceConfig(configFile, type, name);
|
|
1961
2362
|
if (!newConfig) {
|
|
1962
|
-
throw new Error(`Couldn't find "${type}" property in ${
|
|
2363
|
+
throw new Error(`Couldn't find "${type}" property in ${path5.basename(wdioConfPath)}`);
|
|
1963
2364
|
}
|
|
1964
|
-
await
|
|
2365
|
+
await fs5.writeFile(wdioConfPath, newConfig, { encoding: "utf-8" });
|
|
1965
2366
|
console.log("Your wdio.conf.js file has been updated.");
|
|
1966
2367
|
process.exit(0);
|
|
1967
2368
|
}
|