codeceptjs 3.5.4 → 3.5.6

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 (42) hide show
  1. package/docs/build/Appium.js +40 -1
  2. package/docs/build/FileSystem.js +1 -1
  3. package/docs/build/Nightmare.js +4 -0
  4. package/docs/build/Playwright.js +59 -41
  5. package/docs/build/Protractor.js +4 -0
  6. package/docs/build/Puppeteer.js +21 -10
  7. package/docs/build/TestCafe.js +2 -0
  8. package/docs/build/WebDriver.js +8 -4
  9. package/docs/changelog.md +1 -170
  10. package/docs/community-helpers.md +4 -8
  11. package/docs/examples.md +2 -8
  12. package/docs/helpers/Appium.md +37 -0
  13. package/docs/helpers/FileSystem.md +1 -1
  14. package/docs/helpers/Nightmare.md +26 -24
  15. package/docs/helpers/Playwright.md +1 -1
  16. package/docs/helpers/Protractor.md +4 -2
  17. package/docs/helpers/Puppeteer.md +29 -27
  18. package/docs/helpers/TestCafe.md +16 -15
  19. package/docs/helpers/WebDriver.md +32 -30
  20. package/docs/webapi/executeAsyncScript.mustache +2 -0
  21. package/docs/webapi/executeScript.mustache +2 -0
  22. package/lib/codecept.js +4 -0
  23. package/lib/command/info.js +24 -0
  24. package/lib/command/init.js +40 -4
  25. package/lib/command/run-workers.js +5 -0
  26. package/lib/command/run.js +7 -0
  27. package/lib/data/context.js +14 -6
  28. package/lib/helper/Appium.js +40 -1
  29. package/lib/helper/FileSystem.js +1 -1
  30. package/lib/helper/Playwright.js +58 -40
  31. package/lib/helper/Puppeteer.js +17 -10
  32. package/lib/helper/WebDriver.js +4 -4
  33. package/lib/helper/scripts/highlightElement.js +1 -1
  34. package/lib/pause.js +1 -2
  35. package/lib/plugin/autoLogin.js +0 -5
  36. package/lib/plugin/retryTo.js +0 -2
  37. package/lib/plugin/tryTo.js +0 -3
  38. package/lib/session.js +1 -1
  39. package/package.json +88 -88
  40. package/translations/fr-FR.js +13 -1
  41. package/typings/promiseBasedTypes.d.ts +19 -1
  42. package/typings/types.d.ts +36 -16
@@ -117,6 +117,43 @@ const vendorPrefix = {
117
117
  * }
118
118
  * ```
119
119
  *
120
+ * Example Android App using Appiumv2 on BrowserStack:
121
+ *
122
+ * ```js
123
+ * {
124
+ * helpers: {
125
+ * Appium: {
126
+ * appiumV2: true,
127
+ * host: "hub-cloud.browserstack.com",
128
+ * port: 4444,
129
+ * user: process.env.BROWSERSTACK_USER,
130
+ * key: process.env.BROWSERSTACK_KEY,
131
+ * app: `bs://c700ce60cf1gjhgjh3ae8ed9770ghjg5a55b8e022f13c5827cg`,
132
+ * browser: '',
133
+ * desiredCapabilities: {
134
+ * 'appPackage': data.packageName,
135
+ * 'deviceName': process.env.DEVICE || 'Google Pixel 3',
136
+ * 'platformName': process.env.PLATFORM || 'android',
137
+ * 'platformVersion': process.env.OS_VERSION || '10.0',
138
+ * 'automationName': process.env.ENGINE || 'UIAutomator2',
139
+ * 'newCommandTimeout': 300000,
140
+ * 'androidDeviceReadyTimeout': 300000,
141
+ * 'androidInstallTimeout': 90000,
142
+ * 'appWaitDuration': 300000,
143
+ * 'autoGrantPermissions': true,
144
+ * 'gpsEnabled': true,
145
+ * 'isHeadless': false,
146
+ * 'noReset': false,
147
+ * 'noSign': true,
148
+ * 'bstack:options' : {
149
+ * "appiumVersion" : "2.0.1",
150
+ * },
151
+ * }
152
+ * }
153
+ * }
154
+ * }
155
+ * ```
156
+ *
120
157
  * Additional configuration params can be used from <https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md>
121
158
  *
122
159
  * ## Access From Helpers
@@ -234,7 +271,9 @@ class Appium extends Webdriver {
234
271
  const _convertedCaps = {};
235
272
  for (const [key, value] of Object.entries(capabilities)) {
236
273
  if (!key.startsWith(vendorPrefix.appium)) {
237
- _convertedCaps[`${vendorPrefix.appium}:${key}`] = value;
274
+ if (key !== 'platformName') {
275
+ _convertedCaps[`${vendorPrefix.appium}:${key}`] = value;
276
+ }
238
277
  } else {
239
278
  _convertedCaps[`${key}`] = value;
240
279
  }
@@ -52,7 +52,7 @@ class FileSystem extends Helper {
52
52
  }
53
53
 
54
54
  /**
55
- * Writes test to file
55
+ * Writes text to file
56
56
  * @param {string} name
57
57
  * @param {string} text
58
58
  */
@@ -882,6 +882,8 @@ class Nightmare extends Helper {
882
882
  *
883
883
  * @param {string|function} fn function to be executed in browser context.
884
884
  * @param {...any} args to be passed to function.
885
+ * @returns {Promise<any>} script return value
886
+ *
885
887
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
886
888
  *
887
889
  *
@@ -916,6 +918,8 @@ class Nightmare extends Helper {
916
918
  *
917
919
  * @param {string|function} fn function to be executed in browser context.
918
920
  * @param {...any} args to be passed to function.
921
+ * @returns {Promise<any>} script return value
922
+ *
919
923
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
920
924
  *
921
925
  *
@@ -47,7 +47,6 @@ const {
47
47
  setRestartStrategy, restartsSession, restartsContext, restartsBrowser,
48
48
  } = require('./extras/PlaywrightRestartOpts');
49
49
  const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine');
50
- const { highlightElement } = require('./scripts/highlightElement');
51
50
 
52
51
  const pathSeparator = path.sep;
53
52
 
@@ -94,7 +93,7 @@ const pathSeparator = path.sep;
94
93
  * @prop {string[]} [ignoreLog] - An array with console message types that are not logged to debug log. Default value is `['warning', 'log']`. E.g. you can set `[]` to log all messages. See all possible [values](https://playwright.dev/docs/api/class-consolemessage#console-message-type).
95
94
  * @prop {boolean} [ignoreHTTPSErrors] - Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false`
96
95
  * @prop {boolean} [bypassCSP] - bypass Content Security Policy or CSP
97
- * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false
96
+ * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
98
97
  */
99
98
  const config = {};
100
99
 
@@ -482,6 +481,7 @@ class Playwright extends Helper {
482
481
  contextOptions.httpCredentials = this.options.basicAuth;
483
482
  this.isAuthenticated = true;
484
483
  }
484
+ if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP;
485
485
  if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
486
486
  if (this.storageState) contextOptions.storageState = this.storageState;
487
487
  if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent;
@@ -834,8 +834,9 @@ class Playwright extends Helper {
834
834
 
835
835
  async _stopBrowser() {
836
836
  this.withinLocator = null;
837
- this._setPage(null);
837
+ await this._setPage(null);
838
838
  this.context = null;
839
+ this.frame = null;
839
840
  popupStore.clear();
840
841
  await this.browser.close();
841
842
  }
@@ -857,8 +858,8 @@ class Playwright extends Helper {
857
858
  await this.switchTo(null);
858
859
  return frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve());
859
860
  }
860
- await this.switchTo(locator);
861
- this.withinLocator = new Locator(locator);
861
+ await this.switchTo(frame);
862
+ this.withinLocator = new Locator(frame);
862
863
  return;
863
864
  }
864
865
 
@@ -874,6 +875,7 @@ class Playwright extends Helper {
874
875
  this.withinLocator = null;
875
876
  this.context = await this.page;
876
877
  this.contextLocator = null;
878
+ this.frame = null;
877
879
  }
878
880
 
879
881
  _extractDataFromPerformanceTiming(timing, ...dataNames) {
@@ -1298,6 +1300,9 @@ class Playwright extends Helper {
1298
1300
  */
1299
1301
  async _locate(locator) {
1300
1302
  const context = await this.context || await this._getContext();
1303
+
1304
+ if (this.frame) return findElements(this.frame, locator);
1305
+
1301
1306
  return findElements(context, locator);
1302
1307
  }
1303
1308
 
@@ -2008,7 +2013,7 @@ class Playwright extends Helper {
2008
2013
 
2009
2014
  await el.clear();
2010
2015
 
2011
- highlightActiveElement.call(this, el, await this._getContext());
2016
+ await highlightActiveElement.call(this, el);
2012
2017
 
2013
2018
  await el.type(value.toString(), { delay: this.options.pressKeyDelay });
2014
2019
 
@@ -2038,7 +2043,7 @@ class Playwright extends Helper {
2038
2043
 
2039
2044
  const el = els[0];
2040
2045
 
2041
- highlightActiveElement.call(this, el, this.page);
2046
+ await highlightActiveElement.call(this, el);
2042
2047
 
2043
2048
  await el.clear();
2044
2049
 
@@ -2064,7 +2069,7 @@ class Playwright extends Helper {
2064
2069
  async appendField(field, value) {
2065
2070
  const els = await findFields.call(this, field);
2066
2071
  assertElementExists(els, field, 'Field');
2067
- highlightActiveElement.call(this, els[0], await this._getContext());
2072
+ await highlightActiveElement.call(this, els[0]);
2068
2073
  await els[0].press('End');
2069
2074
  await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
2070
2075
  return this._waitForAction();
@@ -2166,7 +2171,7 @@ class Playwright extends Helper {
2166
2171
  assertElementExists(els, select, 'Selectable field');
2167
2172
  const el = els[0];
2168
2173
 
2169
- highlightActiveElement.call(this, el, await this._getContext());
2174
+ await highlightActiveElement.call(this, el);
2170
2175
 
2171
2176
  if (!Array.isArray(option)) option = [option];
2172
2177
 
@@ -2554,11 +2559,11 @@ class Playwright extends Helper {
2554
2559
  * @returns {Promise<any>}
2555
2560
  */
2556
2561
  async executeScript(fn, arg) {
2557
- let context = this.page;
2558
- if (this.context && this.context.constructor.name === 'Frame') {
2559
- context = this.context; // switching to iframe context
2562
+ if (this.context && this.context.constructor.name === 'FrameLocator') {
2563
+ // switching to iframe context
2564
+ return this.context.locator(':root').evaluate(fn, arg);
2560
2565
  }
2561
- return context.evaluate.apply(context, [fn, arg]);
2566
+ return this.page.evaluate.apply(this.page, [fn, arg]);
2562
2567
  }
2563
2568
 
2564
2569
  /**
@@ -3312,7 +3317,7 @@ class Playwright extends Helper {
3312
3317
  }
3313
3318
 
3314
3319
  async _getContext() {
3315
- if (this.context && this.context.constructor.name === 'Frame') {
3320
+ if (this.context && this.context.constructor.name === 'FrameLocator') {
3316
3321
  return this.context;
3317
3322
  }
3318
3323
  return this.page;
@@ -3417,6 +3422,14 @@ class Playwright extends Helper {
3417
3422
  }, [locator.value, text, $XPath.toString()], { timeout: waitTimeout });
3418
3423
  }
3419
3424
  } else {
3425
+ // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
3426
+ if (this.frame) {
3427
+ const { setTimeout } = require('timers/promises');
3428
+ await setTimeout(waitTimeout);
3429
+ waiter = await this.frame.locator(`:has-text('${text}')`).first().isVisible();
3430
+ if (!waiter) throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec`);
3431
+ return;
3432
+ }
3420
3433
  waiter = contextObject.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: waitTimeout });
3421
3434
  }
3422
3435
  return waiter.catch((err) => {
@@ -3480,39 +3493,42 @@ class Playwright extends Helper {
3480
3493
  }
3481
3494
 
3482
3495
  if (locator >= 0 && locator < childFrames.length) {
3483
- this.context = childFrames[locator];
3496
+ this.context = await this.page.frameLocator('iframe').nth(locator);
3484
3497
  this.contextLocator = locator;
3485
3498
  } else {
3486
3499
  throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
3487
3500
  }
3488
3501
  return;
3489
3502
  }
3490
- let contentFrame;
3491
3503
 
3492
3504
  if (!locator) {
3493
- this.context = await this.page.frames()[0];
3505
+ this.context = this.page;
3494
3506
  this.contextLocator = null;
3507
+ this.frame = null;
3495
3508
  return;
3496
3509
  }
3497
3510
 
3498
3511
  // iframe by selector
3499
- const els = await this._locate(locator);
3500
- // assertElementExists(els, locator);
3501
-
3502
- // get content of the first iframe
3503
- if ((locator.frame && locator.frame === 'iframe') || locator.toLowerCase() === 'iframe') {
3504
- contentFrame = await this.page.frames()[1];
3505
- // get content of the iframe using its name
3506
- } else if (locator.toLowerCase().includes('name=')) {
3507
- const frameName = locator.split('=')[1].replace(/"/g, '').replaceAll(/]/g, '');
3508
- contentFrame = await this.page.frame(frameName);
3512
+ locator = buildLocatorString(new Locator(locator, 'css'));
3513
+ const frame = await this._locateElement(locator);
3514
+
3515
+ if (!frame) {
3516
+ throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`);
3509
3517
  }
3510
3518
 
3519
+ if (this.frame) {
3520
+ this.frame = await this.frame.frameLocator(locator);
3521
+ } else {
3522
+ this.frame = await this.page.frameLocator(locator);
3523
+ }
3524
+
3525
+ const contentFrame = this.frame;
3526
+
3511
3527
  if (contentFrame) {
3512
3528
  this.context = contentFrame;
3513
3529
  this.contextLocator = null;
3514
3530
  } else {
3515
- this.context = els[0];
3531
+ this.context = this.page.frame(this.page.frames()[1].name());
3516
3532
  this.contextLocator = locator;
3517
3533
  }
3518
3534
  }
@@ -4268,7 +4284,7 @@ async function findElement(matcher, locator) {
4268
4284
  if (locator.react) return findReact(matcher, locator);
4269
4285
  locator = new Locator(locator, 'css');
4270
4286
 
4271
- return matcher.locator(buildLocatorString(locator));
4287
+ return matcher.locator(buildLocatorString(locator)).first();
4272
4288
  }
4273
4289
 
4274
4290
  async function getVisibleElements(elements) {
@@ -4298,7 +4314,7 @@ async function proceedClick(locator, context = null, options = {}) {
4298
4314
  assertElementExists(els, locator, 'Clickable element');
4299
4315
  }
4300
4316
 
4301
- highlightActiveElement.call(this, els[0], await this._getContext());
4317
+ await highlightActiveElement.call(this, els[0]);
4302
4318
 
4303
4319
  /*
4304
4320
  using the force true options itself but instead dispatching a click
@@ -4348,13 +4364,9 @@ async function proceedSee(assertType, text, context, strict = false) {
4348
4364
  let allText;
4349
4365
 
4350
4366
  if (!context) {
4351
- let el = await this.context;
4352
- if (el && !el.getProperty) {
4353
- // Fallback to body
4354
- el = await this.page.$('body');
4355
- }
4367
+ const el = await this.context;
4356
4368
 
4357
- allText = [await el.innerText()];
4369
+ allText = [await el.locator('body').innerText()];
4358
4370
  description = 'web application';
4359
4371
  } else {
4360
4372
  const locator = new Locator(context, 'css');
@@ -4526,7 +4538,9 @@ async function elementSelected(element) {
4526
4538
 
4527
4539
  function isFrameLocator(locator) {
4528
4540
  locator = new Locator(locator);
4529
- if (locator.isFrame()) return locator.value;
4541
+ if (locator.isFrame()) {
4542
+ return locator.value;
4543
+ }
4530
4544
  return false;
4531
4545
  }
4532
4546
 
@@ -4721,10 +4735,14 @@ async function saveTraceForContext(context, name) {
4721
4735
  return fileName;
4722
4736
  }
4723
4737
 
4724
- function highlightActiveElement(element, context) {
4725
- if (!this.options.highlightElement && !store.debugMode) return;
4726
-
4727
- highlightElement(element, context);
4738
+ async function highlightActiveElement(element) {
4739
+ if (this.options.highlightElement && global.debugMode) {
4740
+ await element.evaluate(el => {
4741
+ const prevStyle = el.style.boxShadow;
4742
+ el.style.boxShadow = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)';
4743
+ setTimeout(() => el.style.boxShadow = prevStyle, 2000);
4744
+ });
4745
+ }
4728
4746
  }
4729
4747
 
4730
4748
  const createAdvancedTestResults = (url, dataToCheck, requests) => {
@@ -1525,6 +1525,8 @@ class Protractor extends Helper {
1525
1525
  *
1526
1526
  * @param {string|function} fn function to be executed in browser context.
1527
1527
  * @param {...any} args to be passed to function.
1528
+ * @returns {Promise<any>} script return value
1529
+ *
1528
1530
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
1529
1531
  *
1530
1532
  */
@@ -1556,6 +1558,8 @@ class Protractor extends Helper {
1556
1558
  *
1557
1559
  * @param {string|function} fn function to be executed in browser context.
1558
1560
  * @param {...any} args to be passed to function.
1561
+ * @returns {Promise<any>} script return value
1562
+ *
1559
1563
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
1560
1564
  *
1561
1565
  */
@@ -69,7 +69,7 @@ const consoleLogStore = new Console();
69
69
  * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
70
70
  * @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
71
71
  * @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
72
- * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false
72
+ * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
73
73
  */
74
74
  const config = {};
75
75
 
@@ -612,8 +612,8 @@ class Puppeteer extends Helper {
612
612
  return this.switchTo(null)
613
613
  .then(() => frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()));
614
614
  }
615
- await this.switchTo(locator);
616
- this.withinLocator = new Locator(locator);
615
+ await this.switchTo(frame);
616
+ this.withinLocator = new Locator(frame);
617
617
  return;
618
618
  }
619
619
 
@@ -2314,6 +2314,8 @@ class Puppeteer extends Helper {
2314
2314
  *
2315
2315
  * @param {string|function} fn function to be executed in browser context.
2316
2316
  * @param {...any} args to be passed to function.
2317
+ * @returns {Promise<any>} script return value
2318
+ *
2317
2319
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
2318
2320
  *
2319
2321
  *
@@ -2351,6 +2353,8 @@ class Puppeteer extends Helper {
2351
2353
  *
2352
2354
  * @param {string|function} fn function to be executed in browser context.
2353
2355
  * @param {...any} args to be passed to function.
2356
+ * @returns {Promise<any>} script return value
2357
+ *
2354
2358
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
2355
2359
  *
2356
2360
  *
@@ -2942,7 +2946,7 @@ class Puppeteer extends Helper {
2942
2946
  assertElementExists(els, locator);
2943
2947
 
2944
2948
  return this.waitForFunction(isElementClickable, [els[0]], waitTimeout).catch(async (e) => {
2945
- if (/failed: timeout/i.test(e.message)) {
2949
+ if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
2946
2950
  throw new Error(`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`);
2947
2951
  } else {
2948
2952
  throw e;
@@ -3096,7 +3100,7 @@ class Puppeteer extends Helper {
3096
3100
  return currUrl.indexOf(urlPart) > -1;
3097
3101
  }, { timeout: waitTimeout }, urlPart).catch(async (e) => {
3098
3102
  const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
3099
- if (/failed: timeout/i.test(e.message)) {
3103
+ if (/Waiting failed:/i.test(e.message) || /failed: timeout/i.test(e.message)) {
3100
3104
  throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
3101
3105
  } else {
3102
3106
  throw e;
@@ -3130,7 +3134,7 @@ class Puppeteer extends Helper {
3130
3134
  return currUrl.indexOf(urlPart) > -1;
3131
3135
  }, { timeout: waitTimeout }, urlPart).catch(async (e) => {
3132
3136
  const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
3133
- if (/failed: timeout/i.test(e.message)) {
3137
+ if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
3134
3138
  throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`);
3135
3139
  } else {
3136
3140
  throw e;
@@ -3427,6 +3431,10 @@ async function findElements(matcher, locator) {
3427
3431
  if (locator.react) return findReact(matcher.executionContext(), locator);
3428
3432
  locator = new Locator(locator, 'css');
3429
3433
  if (!locator.isXPath()) return matcher.$$(locator.simplify());
3434
+ // puppeteer version < 19.4.0 is no longer supported. This one is backward support.
3435
+ if (puppeteer.default?.defaultBrowserRevision) {
3436
+ return matcher.$$(`xpath/${locator.value}`);
3437
+ }
3430
3438
  return matcher.$x(locator.value);
3431
3439
  }
3432
3440
 
@@ -3681,7 +3689,10 @@ async function elementSelected(element) {
3681
3689
 
3682
3690
  function isFrameLocator(locator) {
3683
3691
  locator = new Locator(locator);
3684
- if (locator.isFrame()) return locator.value;
3692
+ if (locator.isFrame()) {
3693
+ const _locator = new Locator(locator);
3694
+ return _locator.value;
3695
+ }
3685
3696
  return false;
3686
3697
  }
3687
3698
 
@@ -3799,7 +3810,7 @@ function getNormalizedKey(key) {
3799
3810
  }
3800
3811
 
3801
3812
  function highlightActiveElement(element, context) {
3802
- if (!this.options.highlightElement && !store.debugMode) return;
3803
-
3804
- highlightElement(element, context);
3813
+ if (this.options.highlightElement && global.debugMode) {
3814
+ highlightElement(element, context);
3815
+ }
3805
3816
  }
@@ -1270,6 +1270,8 @@ class TestCafe extends Helper {
1270
1270
  *
1271
1271
  * @param {string|function} fn function to be executed in browser context.
1272
1272
  * @param {...any} args to be passed to function.
1273
+ * @returns {Promise<any>} script return value
1274
+ *
1273
1275
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
1274
1276
  *
1275
1277
  *
@@ -62,7 +62,7 @@ const webRoot = 'body';
62
62
  * @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
63
63
  * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
64
64
  * @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
65
- * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false
65
+ * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
66
66
  */
67
67
  const config = {};
68
68
 
@@ -2173,6 +2173,8 @@ class WebDriver extends Helper {
2173
2173
  *
2174
2174
  * @param {string|function} fn function to be executed in browser context.
2175
2175
  * @param {...any} args to be passed to function.
2176
+ * @returns {Promise<any>} script return value
2177
+ *
2176
2178
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
2177
2179
  *
2178
2180
  *
@@ -2207,6 +2209,8 @@ class WebDriver extends Helper {
2207
2209
  *
2208
2210
  * @param {string|function} fn function to be executed in browser context.
2209
2211
  * @param {...any} args to be passed to function.
2212
+ * @returns {Promise<any>} script return value
2213
+ *
2210
2214
  * ⚠️ returns a _promise_ which is synchronized internally by recorder
2211
2215
  *
2212
2216
  *
@@ -4079,9 +4083,9 @@ function isModifierKey(key) {
4079
4083
  }
4080
4084
 
4081
4085
  function highlightActiveElement(element) {
4082
- if (!this.options.highlightElement && !store.debugMode) return;
4083
-
4084
- highlightElement(element, this.browser);
4086
+ if (this.options.highlightElement && global.debugMode) {
4087
+ highlightElement(element, this.browser);
4088
+ }
4085
4089
  }
4086
4090
 
4087
4091
  function prepareLocateFn(context) {