codeceptjs 2.6.1 → 2.6.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 2.6.2
2
+
3
+ * [WebDriver][Puppeteer] Added `forceClick` method to emulate click event instead of using native events.
4
+ * [Playwright] Updated to 0.14
5
+ * [Puppeteer] Updated to Puppeteer v3.0
6
+ * [wdio] Fixed undefined output directory for wdio plugns. Fix By @PeterNgTr
7
+ * [Playwright] Introduced `handleDownloads` method to download file. Please note, this method has slightly different API than the same one in Puppeteer.
8
+ * [allure] Fixed undefined output directory for allure plugin on using custom runner. Fix by @charliepradeep
9
+ * [WebDriver] Fixed `waitForEnabled` fix for webdriver 6. Fix by @dsharapkou
10
+ * Workers: Fixed negative failure result if use scenario with the same names. Fix by @Vorobeyko
11
+ * [MockRequest] Updated documentation to match new helper version
12
+ * Fixed: skipped tests are not reported if a suite failed in `before`. Refer #2349 & #2354. Fix by @koushikmohan1996
13
+
1
14
  ## 2.6.1
2
15
 
3
16
  * [screenshotOnFail plugin] Fixed saving screenshot of active session.
package/docs/basics.md CHANGED
@@ -147,6 +147,8 @@ I.click('#signup');
147
147
  I.click('//dev[@test-id="myid"]');
148
148
  ```
149
149
 
150
+ > ℹ If click doesn't work in a test but works for user, it is possible that frontend application is not designed for automated testing. To overcome limitation of standard click in this edgecase use `forceClick` method. It will emulate click instead of sending native event. This command will click an element no matter if this element is visible or animating. It will send JavaScript "click" event to it.
151
+
150
152
  ### Filling Fields
151
153
 
152
154
  Clicking the links is not what takes the most time during testing a web site. If your site consists only of links you can skip test automation. The most waste of time goes into the testing of forms. CodeceptJS provides several ways of doing that.
@@ -234,6 +236,8 @@ I.seeInTitle('My Website');
234
236
 
235
237
  To see all possible assertions, check the helper's reference.
236
238
 
239
+ > ℹ If you need custom assertions, you can install an assertion libarary like `chai`, use grabbers to obtain information from a browser and perform assertions. However, it is recommended to put custom assertions into a helper for further reuse.
240
+
237
241
  ### Grabbing
238
242
 
239
243
  Sometimes you need to retrieve data from a page to use it in the following steps of a scenario.
@@ -435,7 +439,7 @@ This launches the interactive console where you can call any action from the `I`
435
439
  - Press ENTER to run the next step
436
440
  - Press TAB twice to see all available commands
437
441
  - Type exit + Enter to exit the interactive shell
438
- - Prefix => to run js commands
442
+ - Prefix => to run js commands
439
443
  I.
440
444
  ```
441
445
 
@@ -1,5 +1,7 @@
1
1
  const requireg = require('requireg');
2
2
  const path = require('path');
3
+ const fs = require('fs');
4
+ const fsExtra = require('fs-extra');
3
5
 
4
6
  const Helper = require('../helper');
5
7
  const Locator = require('../locator');
@@ -69,7 +71,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
69
71
  * * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to false.
70
72
  * * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
71
73
  * * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
72
- * * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Playwright API](https://github.com/GoogleChrome/Playwright/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
74
+ * * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. Choose one of those options is possible. See [Playwright API](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagewaitfornavigationoptions).
73
75
  * * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Playwrights page.type(...) in fillField/appendField
74
76
  * * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
75
77
  * * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
@@ -94,7 +96,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
94
96
  * }
95
97
  * ```
96
98
  *
97
- * #### Example #2: Wait for DOMContentLoaded event and 0 network connections
99
+ * #### Example #2: Wait for DOMContentLoaded event
98
100
  *
99
101
  * ```js
100
102
  * {
@@ -102,7 +104,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
102
104
  * Playwright : {
103
105
  * url: "http://localhost",
104
106
  * restart: false,
105
- * waitForNavigation: [ "domcontentloaded", "networkidle0" ],
107
+ * waitForNavigation: "domcontentloaded",
106
108
  * waitForAction: 500
107
109
  * }
108
110
  * }
@@ -540,7 +542,7 @@ class Playwright extends Helper {
540
542
  this.browser.on('targetchanged', (target) => {
541
543
  this.debugSection('Url', target.url());
542
544
  });
543
- this.browserContext = await this.browser.newContext(this.options.emulate);
545
+ this.browserContext = await this.browser.newContext({ acceptDownloads: true, ...this.options.emulate });
544
546
 
545
547
  const existingPages = await this.browserContext.pages();
546
548
 
@@ -1113,6 +1115,36 @@ class Playwright extends Helper {
1113
1115
  return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
1114
1116
  }
1115
1117
 
1118
+ /**
1119
+ * Handles a file download.Aa file name is required to save the file on disk.
1120
+ * Files are saved to "output" directory.
1121
+ *
1122
+ * Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
1123
+ *
1124
+ * ```js
1125
+ * I.handleDownloads('downloads/avatar.jpg');
1126
+ * I.click('Download Avatar');
1127
+ * I.amInPath('output/downloads');
1128
+ * I.waitForFile('downloads/avatar.jpg', 5);
1129
+ *
1130
+ * ```
1131
+ *
1132
+ * @param {string} [fileName] set filename for downloaded file
1133
+ */
1134
+ async handleDownloads(fileName = 'downloads') {
1135
+ this.page.waitForEvent('download').then(async (download) => {
1136
+ const filePath = await download.path();
1137
+ const downloadPath = path.join(global.output_dir, fileName || path.basename(filePath));
1138
+ if (!fs.existsSync(path.dirname(downloadPath))) {
1139
+ fs.mkdirSync(path.dirname(downloadPath), '0777');
1140
+ }
1141
+ fs.copyFileSync(filePath, downloadPath);
1142
+ this.debug('Download completed');
1143
+ this.debugSection('Downloaded From', await download.url());
1144
+ this.debugSection('Downloaded To', downloadPath);
1145
+ });
1146
+ }
1147
+
1116
1148
  /**
1117
1149
  * Perform a click on a link or a button, given by a locator.
1118
1150
  * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
@@ -232,7 +232,7 @@ class Puppeteer extends Helper {
232
232
  try {
233
233
  requireg('puppeteer');
234
234
  } catch (e) {
235
- return ['puppeteer@^1.6.0'];
235
+ return ['puppeteer@^3.0.1'];
236
236
  }
237
237
  }
238
238
 
@@ -1087,6 +1087,63 @@ class Puppeteer extends Helper {
1087
1087
  return proceedClick.call(this, locator, context);
1088
1088
  }
1089
1089
 
1090
+ /**
1091
+ * Perform an emulated click on a link or a button, given by a locator.
1092
+ * Unlike normal click instead of sending native event, emulates a click with JavaScript.
1093
+ * This works on hidden, animated or inactive elements as well.
1094
+ *
1095
+ * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
1096
+ * For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched.
1097
+ * For images, the "alt" attribute and inner text of any parent links are searched.
1098
+ *
1099
+ * The second parameter is a context (CSS or XPath locator) to narrow the search.
1100
+ *
1101
+ * ```js
1102
+ * // simple link
1103
+ * I.forceClick('Logout');
1104
+ * // button of form
1105
+ * I.forceClick('Submit');
1106
+ * // CSS button
1107
+ * I.forceClick('#form input[type=submit]');
1108
+ * // XPath
1109
+ * I.forceClick('//form/*[@type=submit]');
1110
+ * // link in context
1111
+ * I.forceClick('Logout', '#nav');
1112
+ * // using strict locator
1113
+ * I.forceClick({css: 'nav a.login'});
1114
+ * ```
1115
+ *
1116
+ * @param {CodeceptJS.LocatorOrString} locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
1117
+ * @param {?CodeceptJS.LocatorOrString} [context=null] (optional, `null` by default) element to search in CSS|XPath|Strict locator.
1118
+ *
1119
+ *
1120
+ * {{ react }}
1121
+ */
1122
+ async forceClick(locator, context = null) {
1123
+ let matcher = await this.context;
1124
+ if (context) {
1125
+ const els = await this._locate(context);
1126
+ assertElementExists(els, context);
1127
+ matcher = els[0];
1128
+ }
1129
+
1130
+ const els = await findClickable.call(this, matcher, locator);
1131
+ if (context) {
1132
+ assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`);
1133
+ } else {
1134
+ assertElementExists(els, locator, 'Clickable element');
1135
+ }
1136
+ const elem = els[0];
1137
+ return this.executeScript((el) => {
1138
+ if (document.activeElement instanceof HTMLElement) {
1139
+ document.activeElement.blur();
1140
+ }
1141
+ const event = document.createEvent('MouseEvent');
1142
+ event.initEvent('click', true, true);
1143
+ return el.dispatchEvent(event);
1144
+ }, elem);
1145
+ }
1146
+
1090
1147
  /**
1091
1148
  * Performs a click on a link and waits for navigation before moving on.
1092
1149
  *
@@ -516,6 +516,11 @@ class WebDriver extends Helper {
516
516
  if (this.options.multiremote) {
517
517
  this.browser = await webdriverio.multiremote(this.options.multiremote);
518
518
  } else {
519
+ // remove non w3c capabilities
520
+ delete this.options.capabilities.protocol;
521
+ delete this.options.capabilities.hostname;
522
+ delete this.options.capabilities.port;
523
+ delete this.options.capabilities.path;
519
524
  this.browser = await webdriverio.remote(this.options);
520
525
  }
521
526
  } catch (err) {
@@ -765,13 +770,15 @@ class WebDriver extends Helper {
765
770
  * Find a clickable element by providing human readable text:
766
771
  *
767
772
  * ```js
768
- * this.helpers['WebDriver']._locateClickable('Next page').then // ...
773
+ * const els = await this.helpers.WebDriver._locateClickable('Next page');
774
+ * const els = await this.helpers.WebDriver._locateClickable('Next page', '.pages');
769
775
  * ```
770
776
  *
771
777
  * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
772
778
  */
773
- async _locateClickable(locator) {
774
- return findClickable.call(this, locator, this.$$.bind(this)).then(res => res);
779
+ async _locateClickable(locator, context) {
780
+ const locateFn = prepareLocateFn.call(this, context);
781
+ return findClickable.call(this, locator, locateFn);
775
782
  }
776
783
 
777
784
  /**
@@ -875,6 +882,59 @@ class WebDriver extends Helper {
875
882
  return this.browser[clickMethod](getElementId(elem));
876
883
  }
877
884
 
885
+ /**
886
+ * Perform an emulated click on a link or a button, given by a locator.
887
+ * Unlike normal click instead of sending native event, emulates a click with JavaScript.
888
+ * This works on hidden, animated or inactive elements as well.
889
+ *
890
+ * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
891
+ * For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched.
892
+ * For images, the "alt" attribute and inner text of any parent links are searched.
893
+ *
894
+ * The second parameter is a context (CSS or XPath locator) to narrow the search.
895
+ *
896
+ * ```js
897
+ * // simple link
898
+ * I.forceClick('Logout');
899
+ * // button of form
900
+ * I.forceClick('Submit');
901
+ * // CSS button
902
+ * I.forceClick('#form input[type=submit]');
903
+ * // XPath
904
+ * I.forceClick('//form/*[@type=submit]');
905
+ * // link in context
906
+ * I.forceClick('Logout', '#nav');
907
+ * // using strict locator
908
+ * I.forceClick({css: 'nav a.login'});
909
+ * ```
910
+ *
911
+ * @param {CodeceptJS.LocatorOrString} locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
912
+ * @param {?CodeceptJS.LocatorOrString} [context=null] (optional, `null` by default) element to search in CSS|XPath|Strict locator.
913
+ *
914
+ *
915
+ * {{ react }}
916
+ */
917
+ async forceClick(locator, context = null) {
918
+ const locateFn = prepareLocateFn.call(this, context);
919
+
920
+ const res = await findClickable.call(this, locator, locateFn);
921
+ if (context) {
922
+ assertElementExists(res, locator, 'Clickable element', `was not found inside element ${new Locator(context)}`);
923
+ } else {
924
+ assertElementExists(res, locator, 'Clickable element');
925
+ }
926
+ const elem = usingFirstElement(res);
927
+
928
+ return this.executeScript((el) => {
929
+ if (document.activeElement instanceof HTMLElement) {
930
+ document.activeElement.blur();
931
+ }
932
+ const event = document.createEvent('MouseEvent');
933
+ event.initEvent('click', true, true);
934
+ return el.dispatchEvent(event);
935
+ }, elem);
936
+ }
937
+
878
938
  /**
879
939
  * Performs a double-click on an element matched by link|button|label|CSS or XPath.
880
940
  * Context can be specified as second parameter to narrow search.
@@ -2498,6 +2558,19 @@ class WebDriver extends Helper {
2498
2558
  */
2499
2559
  async waitForEnabled(locator, sec = null) {
2500
2560
  const aSec = sec || this.options.waitForTimeout;
2561
+ if (isWebDriver5()) {
2562
+ return this.browser.waitUntil(async () => {
2563
+ const res = await this.$$(withStrictLocator(locator));
2564
+ if (!res || res.length === 0) {
2565
+ return false;
2566
+ }
2567
+ const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)));
2568
+ if (Array.isArray(selected)) {
2569
+ return selected.filter(val => val === true).length > 0;
2570
+ }
2571
+ return selected;
2572
+ }, aSec * 1000, `element (${new Locator(locator)}) still not enabled after ${aSec} sec`);
2573
+ }
2501
2574
  return this.browser.waitUntil(async () => {
2502
2575
  const res = await this.$$(withStrictLocator(locator));
2503
2576
  if (!res || res.length === 0) {
@@ -2508,7 +2581,10 @@ class WebDriver extends Helper {
2508
2581
  return selected.filter(val => val === true).length > 0;
2509
2582
  }
2510
2583
  return selected;
2511
- }, aSec * 1000, `element (${new Locator(locator)}) still not enabled after ${aSec} sec`);
2584
+ }, {
2585
+ timeout: aSec * 1000,
2586
+ timeoutMsg: `element (${new Locator(locator)}) still not enabled after ${aSec} sec`,
2587
+ });
2512
2588
  }
2513
2589
 
2514
2590
  /**
package/docs/changelog.md CHANGED
@@ -7,6 +7,19 @@ layout: Section
7
7
 
8
8
  # Releases
9
9
 
10
+ ## 2.6.2
11
+
12
+ * [WebDriver][Puppeteer] Added `forceClick` method to emulate click event instead of using native events.
13
+ * **[Playwright]** Updated to 0.14
14
+ * **[Puppeteer]** Updated to Puppeteer v3.0
15
+ * **[wdio]** Fixed undefined output directory for wdio plugns. Fix By **[PeterNgTr](https://github.com/PeterNgTr)**
16
+ * **[Playwright]** Introduced `handleDownloads` method to download file. Please note, this method has slightly different API than the same one in Puppeteer.
17
+ * **[allure]** Fixed undefined output directory for allure plugin on using custom runner. Fix by **[charliepradeep](https://github.com/charliepradeep)**
18
+ * **[WebDriver]** Fixed `waitForEnabled` fix for webdriver 6. Fix by **[dsharapkou](https://github.com/dsharapkou)**
19
+ * Workers: Fixed negative failure result if use scenario with the same names. Fix by **[Vorobeyko](https://github.com/Vorobeyko)**
20
+ * **[MockRequest]** Updated documentation to match new helper version
21
+ * Fixed: skipped tests are not reported if a suite failed in `before`. Refer [#2349](https://github.com/Codeception/CodeceptJS/issues/2349) & [#2354](https://github.com/Codeception/CodeceptJS/issues/2354). Fix by **[koushikmohan1996](https://github.com/koushikmohan1996)**
22
+
10
23
  ## 2.6.1
11
24
 
12
25
  * [screenshotOnFail plugin] Fixed saving screenshot of active session.
@@ -1162,12 +1162,14 @@ this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').
1162
1162
  Find a clickable element by providing human readable text:
1163
1163
 
1164
1164
  ```js
1165
- this.helpers['WebDriver']._locateClickable('Next page').then // ...
1165
+ const els = await this.helpers.WebDriver._locateClickable('Next page');
1166
+ const els = await this.helpers.WebDriver._locateClickable('Next page', '.pages');
1166
1167
  ```
1167
1168
 
1168
1169
  #### Parameters
1169
1170
 
1170
1171
  - `locator` **([string][4] \| [object][6])** element located by CSS|XPath|strict locator.
1172
+ - `context`
1171
1173
 
1172
1174
  ### \_locateFields
1173
1175
 
@@ -1211,6 +1213,38 @@ I.amOnPage('/login'); // opens a login page
1211
1213
 
1212
1214
  - `url` **[string][4]** url path or global url.
1213
1215
 
1216
+ ### forceClick
1217
+
1218
+ Perform an emulated click on a link or a button, given by a locator.
1219
+ Unlike normal click instead of sending native event, emulates a click with JavaScript.
1220
+ This works on hidden, animated or inactive elements as well.
1221
+
1222
+ If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
1223
+ For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched.
1224
+ For images, the "alt" attribute and inner text of any parent links are searched.
1225
+
1226
+ The second parameter is a context (CSS or XPath locator) to narrow the search.
1227
+
1228
+ ```js
1229
+ // simple link
1230
+ I.forceClick('Logout');
1231
+ // button of form
1232
+ I.forceClick('Submit');
1233
+ // CSS button
1234
+ I.forceClick('#form input[type=submit]');
1235
+ // XPath
1236
+ I.forceClick('//form/*[@type=submit]');
1237
+ // link in context
1238
+ I.forceClick('Logout', '#nav');
1239
+ // using strict locator
1240
+ I.forceClick({css: 'nav a.login'});
1241
+ ```
1242
+
1243
+ #### Parameters
1244
+
1245
+ - `locator` **([string][4] \| [object][6])** clickable link or button located by text, or any element located by CSS|XPath|strict locator.
1246
+ - `context` **([string][4]? | [object][6])** (optional, `null` by default) element to search in CSS|XPath|Strict locator.{{ react }} (optional, default `null`)
1247
+
1214
1248
  ### doubleClick
1215
1249
 
1216
1250
  Performs a double-click on an element matched by link|button|label|CSS or XPath.