@wdio/cli 8.11.3 → 8.12.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 (30) hide show
  1. package/build/cjs/index.js +2 -2
  2. package/build/commands/config.d.ts.map +1 -1
  3. package/build/commands/config.js +2 -1
  4. package/build/commands/run.d.ts.map +1 -1
  5. package/build/commands/run.js +3 -2
  6. package/build/constants.d.ts +119 -56
  7. package/build/constants.d.ts.map +1 -1
  8. package/build/constants.js +110 -42
  9. package/build/templates/exampleFiles/cucumber/step_definitions/steps.js.ejs +6 -6
  10. package/build/templates/exampleFiles/mochaJasmine/test.e2e.js.ejs +11 -0
  11. package/build/templates/exampleFiles/pageobjects/login.page.js.ejs +9 -9
  12. package/build/templates/exampleFiles/pageobjects/page.js.ejs +2 -2
  13. package/build/templates/exampleFiles/pageobjects/secure.page.js.ejs +5 -5
  14. package/build/templates/snippets/capabilities.ejs +46 -0
  15. package/build/templates/snippets/electronTest.js.ejs +9 -0
  16. package/build/templates/snippets/macosTest.js.ejs +11 -0
  17. package/build/templates/snippets/services.ejs +18 -0
  18. package/build/templates/snippets/testWithPO.js.ejs +20 -0
  19. package/build/templates/snippets/testWithoutPO.js.ejs +16 -0
  20. package/build/templates/snippets/vscodeTest.js.ejs +9 -0
  21. package/build/templates/wdio.conf.tpl.ejs +23 -29
  22. package/build/types.d.ts +7 -2
  23. package/build/types.d.ts.map +1 -1
  24. package/build/utils.d.ts.map +1 -1
  25. package/build/utils.js +18 -11
  26. package/package.json +2 -2
  27. package/build/templates/exampleFiles/jasmine/example.e2e.js.ejs +0 -42
  28. package/build/templates/exampleFiles/mocha/example.e2e.js.ejs +0 -42
  29. /package/build/templates/{afterTest.ejs → snippets/afterTest.ejs} +0 -0
  30. /package/build/templates/{reporters.ejs → snippets/reporters.ejs} +0 -0
@@ -58,8 +58,11 @@ export const COMPILER_OPTIONS = {
58
58
  */
59
59
  export const SUPPORTED_PACKAGES = {
60
60
  runner: [
61
- { name: 'local - for e2e testing of web and mobile applications', value: '@wdio/local-runner$--$local' },
62
- { name: 'browser - for unit and component testing in the browser', value: '@wdio/browser-runner$--$browser' }
61
+ { name: 'E2E Testing - of Web or Mobile Applications', value: '@wdio/local-runner$--$local$--$e2e' },
62
+ { name: 'Component or Unit Testing - in the browser\n > https://webdriver.io/docs/component-testing', value: '@wdio/browser-runner$--$browser$--$component' },
63
+ { name: 'Desktop Testing - of Electron Applications\n > https://webdriver.io/docs/desktop-testing/electron', value: '@wdio/local-runner$--$local$--$electron' },
64
+ { name: 'Desktop Testing - of MacOS Applications\n > https://webdriver.io/docs/desktop-testing/macos', value: '@wdio/local-runner$--$local$--$macos' },
65
+ { name: 'VS Code Extension Testing\n > https://webdriver.io/docs/vscode-extension-testing', value: '@wdio/local-runner$--$local$--$vscode' }
63
66
  ],
64
67
  framework: [
65
68
  { name: 'Mocha (https://mochajs.org/)', value: '@wdio/mocha-framework$--$mocha' },
@@ -99,17 +102,18 @@ export const SUPPORTED_PACKAGES = {
99
102
  { name: 'edgedriver', value: 'wdio-edgedriver-service$--$edgedriver' },
100
103
  { name: 'safaridriver', value: 'wdio-safaridriver-service$--$safaridriver' },
101
104
  // internal
102
- { name: 'selenium-standalone', value: '@wdio/selenium-standalone-service$--$selenium-standalone' },
103
- { name: 'appium', value: '@wdio/appium-service$--$appium' },
104
- { name: 'vscode', value: 'wdio-vscode-service$--$vscode' },
105
- { name: 'electron', value: 'wdio-electron-service$--$electron' },
105
+ { name: 'firefox-profile', value: '@wdio/firefox-profile-service$--$firefox-profile' },
106
+ { name: 'gmail', value: '@wdio/gmail-service$--$gmail' },
107
+ { name: 'vite', value: 'wdio-vite-service$--$vite' },
106
108
  { name: 'devtools', value: '@wdio/devtools-service$--$devtools' },
107
109
  { name: 'sauce', value: '@wdio/sauce-service$--$sauce' },
108
110
  { name: 'testingbot', value: '@wdio/testingbot-service$--$testingbot' },
109
111
  { name: 'crossbrowsertesting', value: '@wdio/crossbrowsertesting-service$--$crossbrowsertesting' },
110
112
  { name: 'browserstack', value: '@wdio/browserstack-service$--$browserstack' },
111
- { name: 'firefox-profile', value: '@wdio/firefox-profile-service$--$firefox-profile' },
112
- { name: 'gmail', value: '@wdio/gmail-service$--$gmail' },
113
+ { name: 'vscode', value: 'wdio-vscode-service$--$vscode' },
114
+ { name: 'electron', value: 'wdio-electron-service$--$electron' },
115
+ { name: 'appium', value: '@wdio/appium-service$--$appium' },
116
+ { name: 'selenium-standalone', value: '@wdio/selenium-standalone-service$--$selenium-standalone' },
113
117
  // external
114
118
  { name: 'eslinter-service', value: 'wdio-eslinter-service$--$eslinter' },
115
119
  { name: 'lambdatest', value: 'wdio-lambdatest-service$--$lambdatest' },
@@ -174,12 +178,30 @@ export const REGION_OPTION = [
174
178
  'eu',
175
179
  'apac'
176
180
  ];
177
- function isLocalEnvironment(answers) {
178
- return answers.runner === SUPPORTED_PACKAGES.runner[0].value;
179
- }
181
+ export const E2E_ENVIRONMENTS = [
182
+ { name: 'Web - web applications in the browser', value: 'web' },
183
+ { name: 'Mobile - native, hybrid and mobile web apps, on Android or iOS', value: 'mobile' }
184
+ ];
185
+ export const MOBILE_ENVIRONMENTS = [
186
+ { 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' },
187
+ { name: 'iOS - applications on iOS, iPadOS, and tvOS\n > using XCTest (https://appium.github.io/appium-xcuitest-driver)', value: 'ios' }
188
+ ];
189
+ export const BROWSER_ENVIRONMENTS = [
190
+ { name: 'Chrome', value: 'chrome', driver: 'chromedriver' },
191
+ { name: 'Firefox', value: 'firefox', driver: 'geckodriver' },
192
+ { name: 'Safari', value: 'safari', driver: 'safaridriver' },
193
+ { name: 'Microsoft Edge', value: 'MicrosoftEdge', driver: 'edgedriver' }
194
+ ];
180
195
  function isBrowserRunner(answers) {
181
196
  return answers.runner === SUPPORTED_PACKAGES.runner[1].value;
182
197
  }
198
+ function getTestingPurpose(answers) {
199
+ return convertPackageHashToObject(answers.runner).purpose;
200
+ }
201
+ function getBrowserDriver(browserName) {
202
+ const driverName = BROWSER_ENVIRONMENTS.find((browser) => browser.value === browserName)?.driver;
203
+ return SUPPORTED_PACKAGES.service.find((svc) => svc.name === driverName)?.value;
204
+ }
183
205
  function selectDefaultService(serviceName) {
184
206
  return [SUPPORTED_PACKAGES.service.find(
185
207
  /* istanbul ignore next */
@@ -193,10 +215,8 @@ function prioServiceOrderFor(serviceName) {
193
215
  export const QUESTIONNAIRE = [{
194
216
  type: 'list',
195
217
  name: 'runner',
196
- message: 'Where should your tests be launched?',
197
- choices: SUPPORTED_PACKAGES.runner,
198
- // only ask if there are more than 1 runner to pick from
199
- when: /* istanbul ignore next */ () => SUPPORTED_PACKAGES.runner.length > 1
218
+ message: 'What type of testing would you like to do?',
219
+ choices: SUPPORTED_PACKAGES.runner
200
220
  }, {
201
221
  type: 'list',
202
222
  name: 'preset',
@@ -215,38 +235,52 @@ export const QUESTIONNAIRE = [{
215
235
  * Only show if Testing Library has an add-on for framework
216
236
  */
217
237
  answers.preset && TESTING_LIBRARY_PACKAGES[convertPackageHashToObject(answers.preset).short])
238
+ }, {
239
+ type: 'input',
240
+ name: 'appPath',
241
+ message: 'What is the path to your compiled Electron app?',
242
+ default: './dist',
243
+ when: /* istanbul ignore next */ (answers) => getTestingPurpose(answers) === 'electron'
218
244
  }, {
219
245
  type: 'list',
220
246
  name: 'backend',
221
247
  message: 'Where is your automation backend located?',
222
- choices: /* instanbul ignore next */ (answers) => {
223
- /**
224
- * browser runner currently supports only local testing
225
- * until we have tunnel support for other cloud vendors
226
- */
227
- if (isBrowserRunner(answers)) {
228
- return BACKEND_CHOICES.slice(0, 1);
229
- }
230
- return BACKEND_CHOICES;
231
- }
248
+ choices: BACKEND_CHOICES,
249
+ when: /* instanbul ignore next */ (answers) => getTestingPurpose(answers) === 'e2e'
232
250
  }, {
233
- type: 'confirm',
234
- name: 'setupMobileEnvironment',
235
- message: 'Would you like to setup Appium for mobile testing?',
236
- default: false,
237
- when: /* istanbul ignore next */ (answers) => (isLocalEnvironment(answers) &&
251
+ type: 'list',
252
+ name: 'e2eEnvironment',
253
+ message: 'Which environment you would like to automate?',
254
+ choices: E2E_ENVIRONMENTS,
255
+ default: 'web',
256
+ when: /* istanbul ignore next */ (answers) => (getTestingPurpose(answers) === 'e2e' &&
238
257
  answers.backend === BACKEND_CHOICES[0])
258
+ }, {
259
+ type: 'list',
260
+ name: 'mobileEnvironment',
261
+ message: 'Which mobile environment you\'ld like to automate?',
262
+ choices: MOBILE_ENVIRONMENTS,
263
+ when: /* instanbul ignore next */ (answers) => (getTestingPurpose(answers) === 'e2e' &&
264
+ answers.e2eEnvironment === 'mobile')
265
+ }, {
266
+ type: 'checkbox',
267
+ name: 'browserEnvironment',
268
+ message: 'With which browser should we start?',
269
+ choices: BROWSER_ENVIRONMENTS,
270
+ default: ['chrome'],
271
+ when: /* instanbul ignore next */ (answers) => (getTestingPurpose(answers) === 'e2e' &&
272
+ answers.e2eEnvironment === 'web')
239
273
  }, {
240
274
  type: 'input',
241
275
  name: 'hostname',
242
276
  message: 'What is the host address of that cloud service?',
243
- when: /* istanbul ignore next */ (answers) => answers.backend.toString().indexOf('different service') > -1
277
+ when: /* istanbul ignore next */ (answers) => answers.backend && answers.backend.indexOf('different service') > -1
244
278
  }, {
245
279
  type: 'input',
246
280
  name: 'port',
247
281
  message: 'What is the port on which that service is running?',
248
282
  default: '80',
249
- when: /* istanbul ignore next */ (answers) => answers.backend.toString().indexOf('different service') > -1
283
+ when: /* istanbul ignore next */ (answers) => answers.backend && answers.backend.indexOf('different service') > -1
250
284
  }, {
251
285
  type: 'input',
252
286
  name: 'expEnvAccessKey',
@@ -279,14 +313,14 @@ export const QUESTIONNAIRE = [{
279
313
  name: 'env_user',
280
314
  message: 'Environment variable for username',
281
315
  default: 'LT_USERNAME',
282
- when: /* istanbul ignore next */ (answers) => (answers.backend.toString().indexOf('LambdaTest') > -1 &&
316
+ when: /* istanbul ignore next */ (answers) => (answers.backend && answers.backend.indexOf('LambdaTest') > -1 &&
283
317
  answers.hostname.indexOf('lambdatest.com') > -1)
284
318
  }, {
285
319
  type: 'input',
286
320
  name: 'env_key',
287
321
  message: 'Environment variable for access key',
288
322
  default: 'LT_ACCESS_KEY',
289
- when: /* istanbul ignore next */ (answers) => (answers.backend.toString().indexOf('LambdaTest') > -1 &&
323
+ when: /* istanbul ignore next */ (answers) => (answers.backend && answers.backend.indexOf('LambdaTest') > -1 &&
290
324
  answers.hostname.indexOf('lambdatest.com') > -1)
291
325
  }, {
292
326
  type: 'input',
@@ -323,19 +357,19 @@ export const QUESTIONNAIRE = [{
323
357
  name: 'hostname',
324
358
  message: 'What is the IP or URI to your Selenium standalone or grid server?',
325
359
  default: 'localhost',
326
- when: /* istanbul ignore next */ (answers) => answers.backend.toString().indexOf('own Selenium cloud') > -1
360
+ when: /* istanbul ignore next */ (answers) => answers.backend && answers.backend.toString().indexOf('own Selenium cloud') > -1
327
361
  }, {
328
362
  type: 'input',
329
363
  name: 'port',
330
364
  message: 'What is the port which your Selenium standalone or grid server is running on?',
331
365
  default: '4444',
332
- when: /* istanbul ignore next */ (answers) => answers.backend.toString().indexOf('own Selenium cloud') > -1
366
+ when: /* istanbul ignore next */ (answers) => answers.backend && answers.backend.toString().indexOf('own Selenium cloud') > -1
333
367
  }, {
334
368
  type: 'input',
335
369
  name: 'path',
336
370
  message: 'What is the path to your browser driver or grid server?',
337
371
  default: '/',
338
- when: /* istanbul ignore next */ (answers) => answers.backend.toString().indexOf('own Selenium cloud') > -1
372
+ when: /* istanbul ignore next */ (answers) => answers.backend && answers.backend.toString().indexOf('own Selenium cloud') > -1
339
373
  }, {
340
374
  type: 'list',
341
375
  name: 'framework',
@@ -359,7 +393,16 @@ export const QUESTIONNAIRE = [{
359
393
  type: 'confirm',
360
394
  name: 'generateTestFiles',
361
395
  message: 'Do you want WebdriverIO to autogenerate some test files?',
362
- default: true
396
+ default: true,
397
+ when: /* istanbul ignore next */ (answers) => {
398
+ /**
399
+ * we only have examples for Mocha and Jasmine
400
+ */
401
+ if (['vscode', 'electron', 'macos'].includes(getTestingPurpose(answers)) && answers.framework.includes('cucumber')) {
402
+ return false;
403
+ }
404
+ return true;
405
+ }
363
406
  }, {
364
407
  type: 'input',
365
408
  name: 'specs',
@@ -390,7 +433,12 @@ export const QUESTIONNAIRE = [{
390
433
  /**
391
434
  * page objects aren't common for component testing
392
435
  */
393
- !isBrowserRunner(answers))
436
+ !isBrowserRunner(answers) &&
437
+ /**
438
+ * and also not needed when running VS Code tests since the service comes with
439
+ * its own page object implementation, nor when running Electron or MacOS tests
440
+ */
441
+ !['vscode', 'electron', 'macos'].includes(getTestingPurpose(answers)))
394
442
  }, {
395
443
  type: 'input',
396
444
  name: 'pages',
@@ -426,9 +474,18 @@ export const QUESTIONNAIRE = [{
426
474
  else if (answers.backend === BACKEND_CHOICES[2]) {
427
475
  return prioServiceOrderFor('sauce');
428
476
  }
429
- else if (answers.setupMobileEnvironment) {
477
+ else if (answers.e2eEnvironment === 'mobile') {
430
478
  return prioServiceOrderFor('appium');
431
479
  }
480
+ else if (getTestingPurpose(answers) === 'vscode') {
481
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === 'vscode')];
482
+ }
483
+ else if (getTestingPurpose(answers) === 'electron') {
484
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === 'electron')];
485
+ }
486
+ else if (getTestingPurpose(answers) === 'macos') {
487
+ return [SUPPORTED_PACKAGES.service.find(({ name }) => name === 'appium')];
488
+ }
432
489
  return SUPPORTED_PACKAGES.service;
433
490
  },
434
491
  // @ts-ignore
@@ -439,9 +496,18 @@ export const QUESTIONNAIRE = [{
439
496
  else if (answers.backend === BACKEND_CHOICES[2]) {
440
497
  return selectDefaultService('sauce');
441
498
  }
442
- else if (answers.setupMobileEnvironment) {
499
+ else if (answers.browserEnvironment && answers.browserEnvironment.length) {
500
+ return answers.browserEnvironment.map((browserName) => getBrowserDriver(browserName));
501
+ }
502
+ else if (answers.e2eEnvironment === 'mobile' || getTestingPurpose(answers) === 'macos') {
443
503
  return selectDefaultService('appium');
444
504
  }
505
+ else if (getTestingPurpose(answers) === 'vscode') {
506
+ return selectDefaultService('vscode');
507
+ }
508
+ else if (getTestingPurpose(answers) === 'electron') {
509
+ return selectDefaultService('electron');
510
+ }
445
511
  return selectDefaultService('chromedriver');
446
512
  },
447
513
  validate: /* istanbul ignore next */ (answers) => validateServiceAnswers(answers)
@@ -473,7 +539,9 @@ export const QUESTIONNAIRE = [{
473
539
  // unit and component testing in the browser
474
540
  !isBrowserRunner(answers) &&
475
541
  // mobile testing with Appium
476
- !answers.setupMobileEnvironment)
542
+ answers.e2eEnvironment !== 'mobile' &&
543
+ // nor for VS Code, Electron or MacOS testing
544
+ !['vscode', 'electron', 'macos'].includes(getTestingPurpose(answers)))
477
545
  }, {
478
546
  type: 'confirm',
479
547
  name: 'npmInstall',
@@ -1,16 +1,16 @@
1
- <%- isUsingTypeScript || esmSupport
1
+ <%- answers.isUsingTypeScript || answers.esmSupport
2
2
  ? "import { Given, When, Then } from '@wdio/cucumber-framework';"
3
3
  : "const { Given, When, Then } = require('@wdio/cucumber-framework');" %>
4
4
  <%
5
5
  /**
6
6
  * step definition without page objects
7
7
  */
8
- if (usePageObjects) { %>
9
- <%- isUsingTypeScript || esmSupport
10
- ? `import LoginPage from '${relativePath}/login.page${esmSupport ? '.js' : ''}';`
8
+ if (answers.usePageObjects) { %>
9
+ <%- answers.isUsingTypeScript || answers.esmSupport
10
+ ? `import LoginPage from '${relativePath}/login.page${answers.esmSupport ? '.js' : ''}';`
11
11
  : `const LoginPage = require('${relativePath}/login.page');` %>
12
- <%- isUsingTypeScript || esmSupport
13
- ? `import SecurePage from '${relativePath}/secure.page${esmSupport ? '.js' : ''}';`
12
+ <%- answers.isUsingTypeScript || answers.esmSupport
13
+ ? `import SecurePage from '${relativePath}/secure.page${answers.esmSupport ? '.js' : ''}';`
14
14
  : `const SecurePage = require('${relativePath}/secure.page');` %>
15
15
 
16
16
  const pages = {
@@ -0,0 +1,11 @@
1
+ <% if (answers.purpose === 'vscode') {
2
+ %><%- include('../../snippets/vscodeTest.js.ejs', { answers }) %><%
3
+ } else if (answers.purpose === 'electron') {
4
+ %><%- include('../../snippets/electronTest.js.ejs', { answers }) %><%
5
+ } else if (answers.purpose === 'macos') {
6
+ %><%- include('../../snippets/macosTest.js.ejs', { answers }) %><%
7
+ } else if (answers.usePageObjects) {
8
+ %><%- include('../../snippets/testWithPO.js.ejs', { answers }) %><%
9
+ } else if (!answers.usePageObjects) {
10
+ %><%- include('../../snippets/testWithoutPO.js.ejs', { answers }) %><%
11
+ } %>
@@ -1,7 +1,7 @@
1
- <%- isUsingTypeScript ? "import { ChainablePromiseElement } from 'webdriverio';" : "" %>
1
+ <%- answers.isUsingTypeScript ? "import { ChainablePromiseElement } from 'webdriverio';" : "" %>
2
2
 
3
- <%- isUsingTypeScript || esmSupport
4
- ? `import Page from './page${esmSupport ? '.js' : ''}';`
3
+ <%- answers.isUsingTypeScript || answers.esmSupport
4
+ ? `import Page from './page${answers.esmSupport ? '.js' : ''}';`
5
5
  : "const Page = require('./page');" %>
6
6
 
7
7
  /**
@@ -11,15 +11,15 @@ class LoginPage extends Page {
11
11
  /**
12
12
  * define selectors using getter methods
13
13
  */
14
- <%- isUsingTypeScript ? "public " : "" %>get inputUsername () {
14
+ <%- answers.isUsingTypeScript ? "public " : "" %>get inputUsername () {
15
15
  return $('#username');
16
16
  }
17
17
 
18
- <%- isUsingTypeScript ? "public " : "" %>get inputPassword () {
18
+ <%- answers.isUsingTypeScript ? "public " : "" %>get inputPassword () {
19
19
  return $('#password');
20
20
  }
21
21
 
22
- <%- isUsingTypeScript ? "public " : "" %>get btnSubmit () {
22
+ <%- answers.isUsingTypeScript ? "public " : "" %>get btnSubmit () {
23
23
  return $('button[type="submit"]');
24
24
  }
25
25
 
@@ -27,7 +27,7 @@ class LoginPage extends Page {
27
27
  * a method to encapsule automation code to interact with the page
28
28
  * e.g. to login using username and password
29
29
  */
30
- <%- isUsingTypeScript ? "public " : "" %>async login (username<%- isUsingTypeScript ? ": string": "" %>, password<%- isUsingTypeScript ? ": string": "" %>) {
30
+ <%- answers.isUsingTypeScript ? "public " : "" %>async login (username<%- answers.isUsingTypeScript ? ": string": "" %>, password<%- answers.isUsingTypeScript ? ": string": "" %>) {
31
31
  await this.inputUsername.setValue(username);
32
32
  await this.inputPassword.setValue(password);
33
33
  await this.btnSubmit.click();
@@ -36,9 +36,9 @@ class LoginPage extends Page {
36
36
  /**
37
37
  * overwrite specific options to adapt it to page object
38
38
  */
39
- <%- isUsingTypeScript ? "public " : "" %>open () {
39
+ <%- answers.isUsingTypeScript ? "public " : "" %>open () {
40
40
  return super.open('login');
41
41
  }
42
42
  }
43
43
 
44
- <%- isUsingTypeScript || esmSupport ? "export default": "module.exports =" %> new LoginPage();
44
+ <%- answers.isUsingTypeScript || answers.esmSupport ? "export default": "module.exports =" %> new LoginPage();
@@ -2,12 +2,12 @@
2
2
  * main page object containing all methods, selectors and functionality
3
3
  * that is shared across all page objects
4
4
  */
5
- <%- isUsingTypeScript || esmSupport ? "export default" : "module.exports =" %> class Page {
5
+ <%- answers.isUsingTypeScript || answers.esmSupport ? "export default" : "module.exports =" %> class Page {
6
6
  /**
7
7
  * Opens a sub page of the page
8
8
  * @param path path of the sub page (e.g. /path/to/page.html)
9
9
  */
10
- <%- isUsingTypeScript ? "public " : "" %>open (path<%- isUsingTypeScript ? ": string" : "" %>) {
10
+ <%- answers.isUsingTypeScript ? "public " : "" %>open (path<%- answers.isUsingTypeScript ? ": string" : "" %>) {
11
11
  return browser.url(`https://the-internet.herokuapp.com/${path}`)
12
12
  }
13
13
  }
@@ -1,7 +1,7 @@
1
- <%- isUsingTypeScript ? "import { ChainablePromiseElement } from 'webdriverio';" : "" %>
1
+ <%- answers.isUsingTypeScript ? "import { ChainablePromiseElement } from 'webdriverio';" : "" %>
2
2
 
3
- <%- isUsingTypeScript || esmSupport
4
- ? `import Page from './page${esmSupport ? '.js' : ''}';`
3
+ <%- answers.isUsingTypeScript || answers.esmSupport
4
+ ? `import Page from './page${answers.esmSupport ? '.js' : ''}';`
5
5
  : "const Page = require('./page');" %>
6
6
 
7
7
  /**
@@ -11,9 +11,9 @@ class SecurePage extends Page {
11
11
  /**
12
12
  * define selectors using getter methods
13
13
  */
14
- <%- isUsingTypeScript ? "public " : "" %>get flashAlert () {
14
+ <%- answers.isUsingTypeScript ? "public " : "" %>get flashAlert () {
15
15
  return $('#flash');
16
16
  }
17
17
  }
18
18
 
19
- <%- isUsingTypeScript || esmSupport ? "export default": "module.exports =" %> new SecurePage();
19
+ <%- answers.isUsingTypeScript || answers.esmSupport ? "export default": "module.exports =" %> new SecurePage();
@@ -0,0 +1,46 @@
1
+ capabilities: [{<%
2
+ if (answers.expEnvAccessKey) { %>
3
+ // Experitest Access Key
4
+ // get more information at https://docs.experitest.com/display/TE/Obtaining+Access+Key
5
+ 'experitest:accessKey': '<%- answers.expEnvAccessKey %>',<%
6
+ }
7
+ if (answers.browserEnvironment && answers.browserEnvironment.length) { %>
8
+ <%- answers.browserEnvironment.map((browserName) => `browserName: '${browserName}'`).join('\n }, {\n ') %><%
9
+ } else if (answers.mobileEnvironment === 'android') { %>
10
+ // capabilities for local Appium web tests on an Android Emulator
11
+ platformName: 'Android',
12
+ browserName: 'Chrome',
13
+ 'appium:deviceName': 'Android GoogleAPI Emulator',
14
+ 'appium:platformVersion': '12.0',
15
+ 'appium:automationName': 'UiAutomator2'<%
16
+ } else if (answers.mobileEnvironment === 'ios') { %>
17
+ // capabilities for local Appium web tests on iOS
18
+ platformName: 'iOS',
19
+ browserName: 'Safari',
20
+ 'appium:deviceName': 'iPhone Simulator',
21
+ 'appium:platformVersion': '16.4',
22
+ 'appium:automationName': 'XCUITest'<%
23
+ } else if (answers.purpose === 'macos') { %>
24
+ platformName: 'Mac',
25
+ 'appium:automationName': 'Mac2',
26
+ 'appium:bundleId': '<%= answers.generateTestFiles ? 'com.apple.calculator' : '...' %>'<%
27
+ } else if (answers.purpose === 'vscode') { %>
28
+ browserName: 'vscode',
29
+ browserVersion: 'stable', // also possible: "insiders" or a specific version e.g. "1.80.0"
30
+ 'wdio:vscodeOptions': {
31
+ // points to directory where extension package.json is located
32
+ extensionPath: __dirname,
33
+ // optional VS Code settings
34
+ userSettings: {
35
+ "editor.fontSize": 14
36
+ }
37
+ }<%
38
+ } else if (answers.purpose === 'electron') { %>
39
+ // no need to define any capabilities for local Electron web tests
40
+ // since service plugin takes care of setting everything up<%
41
+ } else {
42
+ %>
43
+ // capabilities for local browser web tests
44
+ browserName: 'chrome' // or "firefox", "microsoftedge", "safari"<%
45
+ } %>
46
+ }],
@@ -0,0 +1,9 @@
1
+ import { browser } from '@wdio/globals'
2
+
3
+ describe('Electron Testing', () => {
4
+ it('should print application metadata', async () => {
5
+ const appName = await browser.electron.app('getName')
6
+ const appVersion = await browser.electron.app('getVersion')
7
+ console.log('Testing Electron app:', appName, appVersion)
8
+ })
9
+ })
@@ -0,0 +1,11 @@
1
+ import { expect, $ } from '@wdio/globals'
2
+
3
+ describe('MacOS Testing', () => {
4
+ it('should calculate the meaning of life', async function () {
5
+ await $('//XCUIElementTypeButton[@label="seven"]').click()
6
+ await $('//XCUIElementTypeButton[@label="multiply"]').click()
7
+ await $('//XCUIElementTypeButton[@label="six"]').click()
8
+ await $('//XCUIElementTypeButton[@title="="]').click()
9
+ await expect($('//XCUIElementTypeStaticText[@label="main display"]')).toHaveText('42')
10
+ })
11
+ })
@@ -0,0 +1,18 @@
1
+ services: [<%- answers.services.map((service) => {
2
+ if (service === 'electron') {
3
+ return /*js*/`[
4
+ 'electron',
5
+ {
6
+ appPath: '${answers.appPath}',
7
+ appName: productName,
8
+ appArgs: ['foo', 'bar=baz'],
9
+ chromedriver: {
10
+ logFileName: 'wdio-chromedriver.log',
11
+ },
12
+ electronVersion: '23.1.0',
13
+ }
14
+ ]`
15
+ }
16
+
17
+ return `'${service}'`
18
+ }) %>],
@@ -0,0 +1,20 @@
1
+ /**
2
+ * test with page objects
3
+ */
4
+ <%- answers.isUsingTypeScript || answers.esmSupport
5
+ ? `import LoginPage from '${answers.relativePath}/login.page${answers.esmSupport ? '.js' : ''}'`
6
+ : `const LoginPage = require('${answers.relativePath}/login.page')` %>
7
+ <%- answers.isUsingTypeScript || answers.esmSupport
8
+ ? `import SecurePage from '${answers.relativePath}/secure.page${answers.esmSupport ? '.js' : ''}'`
9
+ : `const SecurePage = require('${answers.relativePath}/secure.page')` %>
10
+
11
+ describe('My Login application', () => {
12
+ it('should login with valid credentials', async () => {
13
+ await LoginPage.open()
14
+
15
+ await LoginPage.login('tomsmith', 'SuperSecretPassword!')
16
+ await expect(SecurePage.flashAlert).toBeExisting()
17
+ await expect(SecurePage.flashAlert).toHaveTextContaining(
18
+ 'You logged into a secure area!')
19
+ })
20
+ })
@@ -0,0 +1,16 @@
1
+ /**
2
+ * test without page objects
3
+ */
4
+ describe('My Login application', () => {
5
+ it('should login with valid credentials', async () => {
6
+ await browser.url(`https://the-internet.herokuapp.com/login`)
7
+
8
+ await $('#username').setValue('tomsmith')
9
+ await $('#password').setValue('SuperSecretPassword!')
10
+ await $('button[type="submit"]').click()
11
+
12
+ await expect($('#flash')).toBeExisting()
13
+ await expect($('#flash')).toHaveTextContaining(
14
+ 'You logged into a secure area!')
15
+ })
16
+ })
@@ -0,0 +1,9 @@
1
+ import { browser } from '@wdio/globals'
2
+
3
+ describe('VS Code Extension Testing', () => {
4
+ it('should be able to load VSCode', async () => {
5
+ const workbench = await browser.getWorkbench()
6
+ expect(await workbench.getTitleBar().getTitle())
7
+ .toContain('[Extension Development Host]')
8
+ })
9
+ })
@@ -1,7 +1,20 @@
1
- <% if (answers.isUsingTypeScript) {
1
+ <%
2
+ if (answers.purpose === 'electron') {
3
+ %>import fs from 'node:fs'
4
+ <% }
5
+
6
+ if (answers.isUsingTypeScript) {
2
7
  %>import type { Options } from '@wdio/types'
8
+ <% }
9
+
10
+ if (answers.purpose === 'electron') { %>
11
+ const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf-8'))
12
+ const productName = packageJson.build.productName
13
+
14
+ <% }
3
15
 
4
- export const config: Options.Testrunner = {<%
16
+ if (answers.isUsingTypeScript) {
17
+ %>export const config: Options.Testrunner = {<%
5
18
  } else if (answers.esmSupport) {
6
19
  %>export const config = {<%
7
20
  } else {
@@ -35,16 +48,15 @@ export const config: Options.Testrunner = {<%
35
48
  transpileOnly: true
36
49
  }
37
50
  },
38
- <% } %>
39
- <% if(answers.expEnvAccessKey){ %>
51
+ <% }
52
+ if(answers.expEnvAccessKey){ %>
40
53
  hostname: '<%- answers.expEnvHostname %>',
41
54
  <% if (answers.expEnvPort === '443'){%>protocol: 'https',
42
55
  <%} else if (answers.expEnvPort === '80'){%>protocol: 'http',
43
56
  <%} else { %>protocol: '<%- answers.expEnvProtocol %>',<%}%>
44
57
  port: <%- answers.expEnvPort %>,
45
58
  path: '/wd/hub',
46
- <% } else if(answers.hostname && answers.port) { %>
47
- //
59
+ <% } else if(answers.hostname && answers.port) { %>//
48
60
  // =====================
49
61
  // Server Configurations
50
62
  // =====================
@@ -126,25 +138,7 @@ export const config: Options.Testrunner = {<%
126
138
  // Sauce Labs platform configurator - a great tool to configure your capabilities:
127
139
  // https://saucelabs.com/platform/platform-configurator
128
140
  //
129
- capabilities: [{<%
130
- if (answers.expEnvAccessKey) { %>
131
- // Experitest Access Key
132
- // get more information at https://docs.experitest.com/display/TE/Obtaining+Access+Key
133
- 'experitest:accessKey': '<%- answers.expEnvAccessKey %>',<%
134
- }
135
- if (answers.setupMobileEnvironment) { %>
136
- // capabilities for local Appium web tests on an Android Emulator
137
- platformName: 'Android', // or "iOS"
138
- browserName: 'Chrome', // or "Safari"
139
- 'appium:deviceName': 'Android GoogleAPI Emulator', // or "iPhone Simulator"
140
- 'appium:platformVersion': '12.0', // or "16.2" (for running iOS v16)
141
- 'appium:automationName': 'UiAutomator2' // or "XCUITest"<%
142
- } else {
143
- %>
144
- // capabilities for local browser web tests
145
- browserName: 'chrome' // or "firefox", "microsoftedge", "safari"<%
146
- } %>
147
- }],
141
+ <%- include('snippets/capabilities', { answers }) %>
148
142
  //
149
143
  // ===================
150
144
  // Test Configurations
@@ -193,8 +187,8 @@ export const config: Options.Testrunner = {<%
193
187
  // your test setup with almost no effort. Unlike plugins, they don't add new
194
188
  // commands. Instead, they hook themselves up into the test process.
195
189
  <% if(answers.services.length) {
196
- %>services: [<%- answers.services.map(service => `'${service}'`) %>],
197
- <% } else {
190
+ %><%- include('snippets/services', { answers }) %><%
191
+ } else {
198
192
  %>// services: [],
199
193
  //<% } %>
200
194
  // Framework you want to run your specs with.
@@ -214,7 +208,7 @@ export const config: Options.Testrunner = {<%
214
208
  // Whether or not retried spec files should be retried immediately or deferred to the end of the queue
215
209
  // specFileRetriesDeferred: false,
216
210
  //
217
- <%- include('reporters', { reporters: answers.reporters }) %>
211
+ <%- include('snippets/reporters', { reporters: answers.reporters }) %>
218
212
  <% if(answers.framework === 'mocha') { %>
219
213
  //
220
214
  // Options to be passed to Mocha.
@@ -351,7 +345,7 @@ export const config: Options.Testrunner = {<%
351
345
  */
352
346
  // afterHook: function (test, context, { error, result, duration, passed, retries }) {
353
347
  // },
354
- <%- include('afterTest', { reporters: answers.reporters }) %>
348
+ <%- include('snippets/afterTest', { reporters: answers.reporters }) %>
355
349
  /**
356
350
  * Hook that gets executed after the suite has ended
357
351
  * @param {object} suite suite details