codeceptjs 2.2.0 → 2.2.1

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 (44) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +15 -22
  3. package/bin/codecept.js +3 -1
  4. package/docs/advanced.md +1 -1
  5. package/docs/angular.md +6 -9
  6. package/docs/basics.md +388 -86
  7. package/docs/bdd.md +4 -3
  8. package/docs/build/Nightmare.js +3 -0
  9. package/docs/build/Polly.js +26 -12
  10. package/docs/build/Puppeteer.js +14 -13
  11. package/docs/build/TestCafe.js +101 -2
  12. package/docs/build/WebDriver.js +53 -52
  13. package/docs/changelog.md +86 -57
  14. package/docs/detox.md +235 -0
  15. package/docs/helpers/Detox.md +579 -0
  16. package/docs/helpers/Polly.md +13 -3
  17. package/docs/helpers/Puppeteer.md +155 -156
  18. package/docs/helpers/TestCafe.md +53 -0
  19. package/docs/helpers/WebDriver.md +209 -204
  20. package/docs/locators.md +2 -0
  21. package/docs/mobile.md +5 -1
  22. package/docs/puppeteer.md +59 -13
  23. package/docs/quickstart.md +47 -12
  24. package/docs/testcafe.md +157 -0
  25. package/docs/webdriver.md +453 -0
  26. package/lib/command/definitions.js +152 -7
  27. package/lib/command/gherkin/snippets.js +19 -8
  28. package/lib/command/init.js +30 -22
  29. package/lib/command/utils.js +1 -1
  30. package/lib/container.js +36 -10
  31. package/lib/data/dataScenarioConfig.js +18 -0
  32. package/lib/helper/Nightmare.js +3 -0
  33. package/lib/helper/Polly.js +26 -12
  34. package/lib/helper/Puppeteer.js +14 -13
  35. package/lib/helper/TestCafe.js +72 -2
  36. package/lib/helper/WebDriver.js +53 -52
  37. package/lib/helper/testcafe/testcafe-utils.js +3 -2
  38. package/lib/interfaces/scenarioConfig.js +2 -2
  39. package/lib/listener/config.js +2 -2
  40. package/lib/plugin/allure.js +3 -0
  41. package/lib/step.js +5 -2
  42. package/lib/ui.js +1 -1
  43. package/lib/utils.js +13 -21
  44. package/package.json +14 -12
@@ -15,7 +15,6 @@ const {
15
15
  chunkArray,
16
16
  convertCssPropertiesToCamelCase,
17
17
  screenshotOutputFolder,
18
- fileToBase64Zip,
19
18
  } = require('../utils');
20
19
  const {
21
20
  isColorProperty,
@@ -61,15 +60,15 @@ const webRoot = 'body';
61
60
  *
62
61
  * Example:
63
62
  *
64
- * ```json
63
+ * ```js
65
64
  * {
66
- * "helpers": {
67
- * "WebDriver" : {
68
- * "smartWait": 5000,
69
- * "browser": "chrome",
70
- * "restart": false,
71
- * "windowSize": "maximize",
72
- * "timeouts": {
65
+ * helpers: {
66
+ * WebDriver : {
67
+ * smartWait: 5000,
68
+ * browser: "chrome",
69
+ * restart: false,
70
+ * windowSize: "maximize",
71
+ * timeouts: {
73
72
  * "script": 60000,
74
73
  * "page load": 10000
75
74
  * }
@@ -83,15 +82,15 @@ const webRoot = 'body';
83
82
  *
84
83
  * ### Headless Chrome
85
84
  *
86
- * ```json
85
+ * ```js
87
86
  * {
88
- * "helpers": {
89
- * "WebDriver" : {
90
- * "url": "http://localhost",
91
- * "browser": "chrome",
92
- * "desiredCapabilities": {
93
- * "chromeOptions": {
94
- * "args": [ "--headless", "--disable-gpu", "--window-size=800,600" ]
87
+ * helpers: {
88
+ * WebDriver : {
89
+ * url: "http://localhost",
90
+ * browser: "chrome",
91
+ * desiredCapabilities: {
92
+ * chromeOptions: {
93
+ * args: [ "--headless", "--disable-gpu", "--window-size=800,600" ]
95
94
  * }
96
95
  * }
97
96
  * }
@@ -103,14 +102,14 @@ const webRoot = 'body';
103
102
  *
104
103
  * Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
105
104
  *
106
- * ```json
105
+ * ```js
107
106
  * {
108
- * "helpers": {
109
- * "WebDriver" : {
110
- * "url": "http://localhost",
111
- * "browser": "internet explorer",
112
- * "desiredCapabilities": {
113
- * "ieOptions": {
107
+ * helpers: {
108
+ * WebDriver : {
109
+ * url: "http://localhost",
110
+ * browser: "internet explorer",
111
+ * desiredCapabilities: {
112
+ * ieOptions: {
114
113
  * "ie.browserCommandLineSwitches": "-private",
115
114
  * "ie.usePerProcessProxy": true,
116
115
  * "ie.ensureCleanSession": true,
@@ -123,15 +122,18 @@ const webRoot = 'body';
123
122
  *
124
123
  * ### Selenoid Options
125
124
  *
126
- * ```json
125
+ * [Selenoid](https://aerokube.com/selenoid/latest/) is a modern way to run Selenium inside Docker containers.
126
+ * Selenoid is easy to set up and provides more features than original Selenium Server. Use `selenoidOptions` to set Selenoid capabilities
127
+ *
128
+ * ```js
127
129
  * {
128
- * "helpers": {
129
- * "WebDriver" : {
130
- * "url": "http://localhost",
131
- * "browser": "chrome",
132
- * "desiredCapabilities": {
133
- * "selenoidOptions": {
134
- * "enableVNC": true,
130
+ * helpers: {
131
+ * WebDriver : {
132
+ * url: "http://localhost",
133
+ * browser: "chrome",
134
+ * desiredCapabilities: {
135
+ * selenoidOptions: {
136
+ * enableVNC: true,
135
137
  * }
136
138
  * }
137
139
  * }
@@ -139,17 +141,17 @@ const webRoot = 'body';
139
141
  * }
140
142
  * ```
141
143
  *
142
- * ### Connect through proxy
144
+ * ### Connect Through proxy
143
145
  *
144
146
  * CodeceptJS also provides flexible options when you want to execute tests to Selenium servers through proxy. You will
145
147
  * need to update the `helpers.WebDriver.capabilities.proxy` key.
146
148
  *
147
149
  * ```js
148
150
  * {
149
- * "helpers": {
150
- * "WebDriver": {
151
- * "capabilities": {
152
- * "proxy": {
151
+ * helpers: {
152
+ * WebDriver: {
153
+ * capabilities: {
154
+ * proxy: {
153
155
  * "proxyType": "manual|pac",
154
156
  * "proxyAutoconfigUrl": "URL TO PAC FILE",
155
157
  * "httpProxy": "PROXY SERVER",
@@ -169,10 +171,10 @@ const webRoot = 'body';
169
171
  *
170
172
  * ```js
171
173
  * {
172
- * "helpers": {
173
- * "WebDriver": {
174
- * "capabilities": {
175
- * "proxy": {
174
+ * helpers: {
175
+ * WebDriver: {
176
+ * capabilities: {
177
+ * proxy: {
176
178
  * "proxyType": "manual",
177
179
  * "httpProxy": "http://corporate.proxy:8080",
178
180
  * "socksUsername": "codeceptjs",
@@ -199,12 +201,12 @@ const webRoot = 'body';
199
201
  *
200
202
  * ```js
201
203
  * {
202
- * "helpers":{
203
- * "WebDriver": {
204
- * "url": "YOUR_DESIRED_HOST",
205
- * "user": "YOUR_BROWSERSTACK_USER",
206
- * "key": "YOUR_BROWSERSTACK_KEY",
207
- * "capabilities": {
204
+ * helpers:{
205
+ * WebDriver: {
206
+ * url: "YOUR_DESIRED_HOST",
207
+ * user: "YOUR_BROWSERSTACK_USER",
208
+ * key: "YOUR_BROWSERSTACK_KEY",
209
+ * capabilities: {
208
210
  * "browserName": "chrome",
209
211
  *
210
212
  * // only set this if you're using BrowserStackLocal to test a local domain
@@ -318,8 +320,8 @@ const webRoot = 'body';
318
320
  *
319
321
  * ```js
320
322
  * {
321
- * "helpers": {
322
- * "WebDriver": {
323
+ * helpers: {
324
+ * WebDriver: {
323
325
  * "multiremote": {
324
326
  * "MyChrome": {
325
327
  * "desiredCapabilities": {
@@ -852,12 +854,11 @@ class WebDriver extends Helper {
852
854
  assertElementExists(res, locator, 'File field');
853
855
  const el = usingFirstElement(res);
854
856
 
855
- // Remote Uplaod (when running Selenium Server)
857
+ // Remote Upload (when running Selenium Server)
856
858
  if (this.options.remoteFileUpload) {
857
- const fileCompressed = await fileToBase64Zip(file);
858
859
  try {
859
860
  this.debugSection('File', 'Uploading file to remote server');
860
- file = await this.browser.uploadFile(fileCompressed);
861
+ file = await this.browser.uploadFile(file);
861
862
  } catch (err) {
862
863
  throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`);
863
864
  }
@@ -9,7 +9,7 @@ const createTestFile = () => {
9
9
  assert(global.output_dir, 'global.output_dir must be set');
10
10
 
11
11
  const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
12
- const testControllerHolderDir = __dirname;
12
+ const testControllerHolderDir = __dirname.replace(/\\/g, '/');
13
13
 
14
14
  fs.writeFileSync(
15
15
  testFile,
@@ -28,7 +28,8 @@ const mapError = (testcafeError) => {
28
28
  if (testcafeError.errMsg) {
29
29
  throw new Error(testcafeError.errMsg);
30
30
  }
31
- throw new Error(`Unknown TestCafe error ${testcafeError.toString()}`);
31
+ const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`;
32
+ throw new Error(`TestCafe Error: ${errorInfo}`);
32
33
  };
33
34
 
34
35
 
@@ -58,13 +58,13 @@ class ScenarioConfig {
58
58
  * Configures a helper.
59
59
  * Helper name can be omitted and values will be applied to first helper.
60
60
  */
61
- config(helper, obj) {
61
+ async config(helper, obj) {
62
62
  if (!obj) {
63
63
  obj = helper;
64
64
  helper = 0;
65
65
  }
66
66
  if (typeof obj === 'function') {
67
- obj = obj(this.test);
67
+ obj = await obj(this.test);
68
68
  }
69
69
  if (!this.test.config) {
70
70
  this.test.config = {};
@@ -1,7 +1,7 @@
1
1
  const event = require('../event');
2
2
  const container = require('../container');
3
3
  const recorder = require('../recorder');
4
- const { deepMerge, ucfirst } = require('../utils');
4
+ const { deepMerge, deepClone, ucfirst } = require('../utils');
5
5
  const { debug } = require('../output');
6
6
  /**
7
7
  * Enable Helpers to listen to test events
@@ -17,7 +17,7 @@ module.exports = function () {
17
17
  function updateHelperConfig(helper, config) {
18
18
  const oldConfig = Object.assign({}, helper.options);
19
19
  try {
20
- helper._setConfig(deepMerge(Object.assign({}, oldConfig), config));
20
+ helper._setConfig(deepMerge(deepClone(oldConfig), config));
21
21
  debug(`[${ucfirst(type)} Config] ${helper.constructor.name} ${JSON.stringify(config)}`);
22
22
  } catch (err) {
23
23
  recorder.throw(err);
@@ -1,6 +1,7 @@
1
1
  const event = require('../event');
2
2
  const Allure = require('allure-js-commons');
3
3
  const logger = require('../output');
4
+ const ansiRegExp = require('../utils').ansiRegExp;
4
5
 
5
6
  const defaultConfig = {
6
7
  outputDir: global.output_dir,
@@ -193,6 +194,8 @@ module.exports = (config) => {
193
194
  currentMetaStep.forEach(() => reporter.endStep('failed'));
194
195
  currentMetaStep = [];
195
196
  }
197
+
198
+ err.message = err.message.replace(ansiRegExp(), '');
196
199
  reporter.endCase('failed', err);
197
200
  });
198
201
 
package/lib/step.js CHANGED
@@ -154,8 +154,11 @@ function detectMetaStep(stack) {
154
154
  if (isTest(line) || isBDD(line)) break;
155
155
  const fnName = line.match(/^at (\w+)\.(\w+)\s\(/);
156
156
  if (!fnName) continue;
157
- if (fnName[1] === 'Generator') return; // don't track meta steps inside generators
158
- if (fnName[1] === 'recorder') return; // don't track meta steps inside generators
157
+ if (fnName[1] === 'Generator'
158
+ || fnName[1] === 'recorder'
159
+ || fnName[1] === 'Runner'
160
+ ) { return; } // don't track meta steps inside generators
161
+
159
162
  if (fnName[1] === 'Object') {
160
163
  // detect PO name from includes
161
164
  for (const name in support) {
package/lib/ui.js CHANGED
@@ -138,7 +138,7 @@ module.exports = function (suite) {
138
138
  * Pending test case.
139
139
  */
140
140
  context.xScenario = context.Scenario.skip = function (title) {
141
- context.Scenario(title, {});
141
+ return context.Scenario(title, {});
142
142
  };
143
143
 
144
144
  addDataContext(context);
package/lib/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const getFunctionArguments = require('fn-args');
4
+ const deepClone = require('lodash.clonedeep');
4
5
  const { convertColorToRGBA, isColorProperty } = require('./colorUtils');
5
6
 
6
7
  function isObject(item) {
@@ -26,6 +27,8 @@ function deepMerge(target, source) {
26
27
 
27
28
  module.exports.deepMerge = deepMerge;
28
29
 
30
+ module.exports.deepClone = deepClone;
31
+
29
32
  const isGenerator = module.exports.isGenerator = function (fn) {
30
33
  return fn.constructor.name === 'GeneratorFunction';
31
34
  };
@@ -281,26 +284,6 @@ module.exports.screenshotOutputFolder = function (fileName) {
281
284
  return path.join(global.codecept_dir, fileName);
282
285
  };
283
286
 
284
- module.exports.fileToBase64Zip = async function (localPath) {
285
- const archiver = require('archiver');
286
-
287
- const zipData = [];
288
- const source = fs.createReadStream(localPath);
289
-
290
- return new Promise((resolve, reject) => {
291
- archiver('zip')
292
- .on('error', (e) => { throw new Error(e); })
293
- .on('data', data => zipData.push(data))
294
- .on('end', () => resolve(Buffer.concat(zipData).toString('base64')))
295
- .append(source, { name: path.basename(localPath) })
296
- .finalize((err) => {
297
- if (err) {
298
- reject(err);
299
- }
300
- });
301
- });
302
- };
303
-
304
287
  module.exports.beautify = function (code) {
305
288
  const format = require('js-beautify').js;
306
289
  return format(code, { indent_size: 2, space_in_empty_paren: true });
@@ -322,7 +305,7 @@ function joinUrl(baseUrl, url) {
322
305
  return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url;
323
306
  }
324
307
 
325
- module.exports.appendBaseUrl = function (baseUrl, oneOrMoreUrls) {
308
+ module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
326
309
  // Remove '/' if it's at the end of baseUrl
327
310
  const lastChar = baseUrl.substr(-1);
328
311
  if (lastChar === '/') {
@@ -363,3 +346,12 @@ module.exports.replaceValueDeep = function replaceValueDeep(obj, key, value) {
363
346
  }
364
347
  return obj;
365
348
  };
349
+
350
+ module.exports.ansiRegExp = function ({ onlyFirst = false } = {}) {
351
+ const pattern = [
352
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
353
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
354
+ ].join('|');
355
+
356
+ return new RegExp(pattern, onlyFirst ? undefined : 'g');
357
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Modern Era Acceptance Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -37,25 +37,27 @@
37
37
  "test": "mocha test/unit --recursive && mocha test/runner --recursive"
38
38
  },
39
39
  "dependencies": {
40
+ "@codeceptjs/detox-helper": "^1.0.1",
40
41
  "allure-js-commons": "^1.3.2",
41
42
  "archiver": "^3.0.0",
42
43
  "axios": "^0.19.0",
43
44
  "chalk": "^1.1.3",
44
45
  "commander": "^2.20.0",
45
46
  "css-to-xpath": "^0.1.0",
46
- "cucumber-expressions": "^6.0.1",
47
+ "cucumber-expressions": "^6.6.2",
47
48
  "escape-string-regexp": "^1.0.3",
48
49
  "figures": "^2.0.0",
49
50
  "fn-args": "^4.0.0",
50
51
  "fs-extra": "^8.0.1",
51
52
  "gherkin": "^5.1.0",
52
53
  "glob": "^6.0.1",
53
- "inquirer": "^6.3.1",
54
- "js-beautify": "^1.9.1",
54
+ "inquirer": "^6.4.1",
55
+ "js-beautify": "^1.10.0",
56
+ "lodash.clonedeep": "^4.5.0",
55
57
  "lodash.merge": "^4.6.1",
56
58
  "mkdirp": "^0.5.1",
57
59
  "mocha": "^4.1.0",
58
- "mocha-junit-reporter": "^1.22.0",
60
+ "mocha-junit-reporter": "^1.23.0",
59
61
  "parse-function": "^5.2.10",
60
62
  "promise-retry": "^1.1.1",
61
63
  "requireg": "^0.1.8",
@@ -66,31 +68,31 @@
66
68
  "@pollyjs/adapter-puppeteer": "^2.5.0",
67
69
  "@pollyjs/core": "^2.5.0",
68
70
  "@types/inquirer": "^0.0.35",
69
- "@types/node": "^8.10.48",
70
- "@wdio/sauce-service": "^5.8.0",
71
- "@wdio/selenium-standalone-service": "^5.8.0",
72
- "@wdio/utils": "^5.8.0",
71
+ "@types/node": "^8.10.49",
72
+ "@wdio/sauce-service": "^5.10.8",
73
+ "@wdio/selenium-standalone-service": "^5.9.3",
74
+ "@wdio/utils": "^5.9.3",
73
75
  "chai": "^3.4.1",
74
76
  "chai-as-promised": "^5.2.0",
75
77
  "co-mocha": "^1.2",
76
78
  "documentation": "^8.1.2",
77
79
  "eslint": "^4.17.0",
78
80
  "eslint-config-airbnb-base": "^12.1.0",
79
- "eslint-plugin-import": "^2.17.2",
81
+ "eslint-plugin-import": "^2.18.0",
80
82
  "eslint-plugin-mocha": "^5.3.0",
81
83
  "faker": "^4.1.0",
82
84
  "husky": "^1.2.1",
83
85
  "json-server": "^0.10.1",
84
86
  "nightmare": "^3.0.2",
85
87
  "protractor": "^5.4.1",
86
- "puppeteer": "^1.15.0",
88
+ "puppeteer": "^1.18.1",
87
89
  "rosie": "^1.6.0",
88
90
  "sinon": "^1.17.2",
89
91
  "sinon-chai": "^2.14.0",
90
92
  "testcafe": "^1.2.1",
91
93
  "typescript": "^2.9.2",
92
94
  "wdio-docker-service": "^1.5.0",
93
- "webdriverio": "^5.8.0",
95
+ "webdriverio": "^5.10.9",
94
96
  "xmldom": "^0.1.27",
95
97
  "xpath": "0.0.27"
96
98
  },