codeceptjs 3.0.3 → 3.0.7

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 (64) hide show
  1. package/CHANGELOG.md +114 -18
  2. package/bin/codecept.js +1 -0
  3. package/docs/basics.md +2 -2
  4. package/docs/bdd.md +12 -1
  5. package/docs/build/Appium.js +2 -1
  6. package/docs/build/GraphQL.js +9 -10
  7. package/docs/build/Nightmare.js +4 -5
  8. package/docs/build/Playwright.js +164 -37
  9. package/docs/build/Protractor.js +1 -1
  10. package/docs/build/Puppeteer.js +1 -1
  11. package/docs/build/REST.js +24 -4
  12. package/docs/build/TestCafe.js +1 -1
  13. package/docs/build/WebDriver.js +85 -17
  14. package/docs/changelog.md +114 -18
  15. package/docs/data.md +5 -5
  16. package/docs/detox.md +2 -2
  17. package/docs/docker.md +11 -11
  18. package/docs/email.md +8 -8
  19. package/docs/helpers/Appium.md +1 -1
  20. package/docs/helpers/Nightmare.md +4 -5
  21. package/docs/helpers/Playwright.md +94 -64
  22. package/docs/helpers/Protractor.md +1 -1
  23. package/docs/helpers/Puppeteer.md +1 -1
  24. package/docs/helpers/REST.md +9 -0
  25. package/docs/helpers/TestCafe.md +1 -1
  26. package/docs/helpers/WebDriver.md +2 -1
  27. package/docs/locators.md +29 -2
  28. package/docs/mobile-react-native-locators.md +2 -2
  29. package/docs/mobile.md +3 -3
  30. package/docs/nightmare.md +0 -5
  31. package/docs/pageobjects.md +3 -1
  32. package/docs/parallel.md +35 -10
  33. package/docs/playwright.md +55 -8
  34. package/docs/plugins.md +73 -29
  35. package/docs/reports.md +8 -7
  36. package/docs/typescript.md +47 -5
  37. package/docs/webapi/fillField.mustache +1 -1
  38. package/lib/cli.js +25 -10
  39. package/lib/codecept.js +9 -1
  40. package/lib/command/interactive.js +10 -9
  41. package/lib/command/run.js +1 -1
  42. package/lib/command/workers/runTests.js +11 -6
  43. package/lib/config.js +8 -3
  44. package/lib/event.js +2 -0
  45. package/lib/helper/Appium.js +1 -0
  46. package/lib/helper/GraphQL.js +9 -10
  47. package/lib/helper/Nightmare.js +1 -1
  48. package/lib/helper/Playwright.js +131 -38
  49. package/lib/helper/REST.js +24 -4
  50. package/lib/helper/WebDriver.js +84 -16
  51. package/lib/interfaces/gherkin.js +11 -4
  52. package/lib/output.js +7 -4
  53. package/lib/plugin/allure.js +3 -7
  54. package/lib/plugin/fakerTransform.js +51 -0
  55. package/lib/plugin/screenshotOnFail.js +6 -2
  56. package/lib/recorder.js +9 -0
  57. package/lib/step.js +2 -1
  58. package/lib/transform.js +26 -0
  59. package/lib/ui.js +6 -2
  60. package/lib/within.js +1 -1
  61. package/lib/workers.js +39 -25
  62. package/package.json +14 -9
  63. package/typings/index.d.ts +49 -21
  64. package/typings/types.d.ts +72 -26
package/lib/cli.js CHANGED
@@ -3,6 +3,7 @@ const ms = require('ms');
3
3
  const event = require('./event');
4
4
  const AssertionFailedError = require('./assert/error');
5
5
  const output = require('./output');
6
+ const { MetaStep } = require('./step');
6
7
 
7
8
  const cursor = Base.cursor;
8
9
  let currentMetaStep = [];
@@ -77,17 +78,22 @@ class Cli extends Base {
77
78
  });
78
79
 
79
80
  event.dispatcher.on(event.step.started, (step) => {
80
- output.stepShift = 3;
81
- const printMetaStep = (metaStep) => {
82
- if (!metaStep) return currentMetaStep.shift();
83
- if (currentMetaStep.indexOf(metaStep.toString()) >= 0) return; // step is the same
84
- if (metaStep.metaStep) {
85
- printMetaStep(metaStep.metaStep);
81
+ let processingStep = step;
82
+ const metaSteps = [];
83
+ while (processingStep.metaStep) {
84
+ metaSteps.unshift(processingStep.metaStep);
85
+ processingStep = processingStep.metaStep;
86
+ }
87
+ const shift = metaSteps.length;
88
+
89
+ for (let i = 0; i < Math.max(currentMetaStep.length, metaSteps.length); i++) {
90
+ if (currentMetaStep[i] !== metaSteps[i]) {
91
+ output.stepShift = 3 + 2 * i;
92
+ if (metaSteps[i]) output.step(metaSteps[i]);
86
93
  }
87
- currentMetaStep.unshift(metaStep.toString());
88
- output.step(metaStep);
89
- };
90
- printMetaStep(step.metaStep);
94
+ }
95
+ currentMetaStep = metaSteps;
96
+ output.stepShift = 3 + 2 * shift;
91
97
  output.step(step);
92
98
  });
93
99
 
@@ -131,6 +137,8 @@ class Cli extends Base {
131
137
  output.print(output.styles.bold('-- FAILURES:'));
132
138
  }
133
139
 
140
+ const failuresLog = [];
141
+
134
142
  // failures
135
143
  if (stats.failures) {
136
144
  // append step traces
@@ -178,10 +186,17 @@ class Cli extends Base {
178
186
  return test;
179
187
  });
180
188
 
189
+ const originalLog = Base.consoleLog;
190
+ Base.consoleLog = (...data) => {
191
+ failuresLog.push([...data]);
192
+ originalLog(...data);
193
+ };
181
194
  Base.list(this.failures);
195
+ Base.consoleLog = originalLog;
182
196
  console.log();
183
197
  }
184
198
 
199
+ event.emit(event.all.failures, { failuresLog, stats });
185
200
  output.result(stats.passes, stats.failures, stats.pending, ms(stats.duration));
186
201
 
187
202
  if (stats.failures && output.level() < 3) {
package/lib/codecept.js CHANGED
@@ -131,7 +131,15 @@ class Codecept {
131
131
  if (!pattern) {
132
132
  patterns = [];
133
133
  if (this.config.tests && !this.opts.features) patterns.push(this.config.tests);
134
- if (this.config.gherkin.features && !this.opts.tests) patterns.push(this.config.gherkin.features);
134
+ if (this.config.gherkin.features && !this.opts.tests) {
135
+ if (Array.isArray(this.config.gherkin.features)) {
136
+ this.config.gherkin.features.forEach(feature => {
137
+ patterns.push(feature);
138
+ });
139
+ } else {
140
+ patterns.push(this.config.gherkin.features);
141
+ }
142
+ }
135
143
  }
136
144
 
137
145
  for (pattern of patterns) {
@@ -4,21 +4,20 @@ const Codecept = require('../codecept');
4
4
  const event = require('../event');
5
5
  const output = require('../output');
6
6
 
7
- module.exports = function (path, options) {
7
+ module.exports = async function (path, options) {
8
8
  // Backward compatibility for --profile
9
9
  process.profile = options.profile;
10
10
  process.env.profile = options.profile;
11
+ const configFile = options.config;
12
+
13
+ const config = getConfig(configFile);
14
+ const testsPath = getTestRoot(configFile);
11
15
 
12
- const testsPath = getTestRoot(path);
13
- const config = getConfig(testsPath);
14
16
  const codecept = new Codecept(config, options);
15
17
  codecept.init(testsPath);
16
18
 
17
- codecept.runBootstrap((err) => {
18
- if (err) {
19
- output.error(`Error while running bootstrap file :${err}`);
20
- return;
21
- }
19
+ try {
20
+ await codecept.bootstrap();
22
21
 
23
22
  if (options.verbose) output.level(3);
24
23
 
@@ -36,5 +35,7 @@ module.exports = function (path, options) {
36
35
  recorder.add(() => event.emit(event.suite.after, {}));
37
36
  recorder.add(() => event.emit(event.all.result, {}));
38
37
  recorder.add(() => codecept.teardown());
39
- });
38
+ } catch (err) {
39
+ output.error(`Error while running bootstrap file :${err}`);
40
+ }
40
41
  };
@@ -19,9 +19,9 @@ module.exports = async function (test, options) {
19
19
  createOutputDir(config, testRoot);
20
20
 
21
21
  const codecept = new Codecept(config, options);
22
- codecept.init(testRoot);
23
22
 
24
23
  try {
24
+ codecept.init(testRoot);
25
25
  await codecept.bootstrap();
26
26
  codecept.loadTests();
27
27
  await codecept.run(test);
@@ -39,9 +39,12 @@ codecept.init(testRoot);
39
39
  codecept.loadTests();
40
40
  const mocha = container.mocha();
41
41
  filterTests();
42
- if (mocha.suite.total()) {
43
- runTests();
44
- }
42
+
43
+ (async function () {
44
+ if (mocha.suite.total()) {
45
+ await runTests();
46
+ }
47
+ }());
45
48
 
46
49
  async function runTests() {
47
50
  try {
@@ -116,14 +119,16 @@ function initializeListeners() {
116
119
  // tests
117
120
  event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }));
118
121
  event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }));
119
- event.dispatcher.on(event.test.finished, test => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test) }));
120
- event.dispatcher.on(event.test.failed, test => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test) }));
121
- event.dispatcher.on(event.test.passed, test => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test) }));
122
+ // we should force-send correct errors to prevent race condition
123
+ event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }));
124
+ event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }));
125
+ event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }));
122
126
  event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }));
123
127
  event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }));
124
128
 
125
129
  event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }));
126
130
  event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }));
131
+ event.dispatcher.on(event.all.failures, (data) => sendToParentThread({ event: event.all.failures, workerIndex, data }));
127
132
 
128
133
  // all
129
134
  event.dispatcher.once(event.all.result, () => parentPort.close());
package/lib/config.js CHANGED
@@ -77,13 +77,18 @@ class Config {
77
77
  return loadConfigFile(jsonConfig);
78
78
  }
79
79
 
80
- throw new Error(`Can not load config from ${jsConfig} or ${jsonConfig}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`);
80
+ const tsConfig = path.join(configFile, 'codecept.conf.ts');
81
+ if (isFile(tsConfig)) {
82
+ return loadConfigFile(tsConfig);
83
+ }
84
+
85
+ throw new Error(`Can not load config from ${jsConfig} or ${jsonConfig} or ${tsConfig}\nCodeceptJS is not initialized in this dir. Execute 'codeceptjs init' to start`);
81
86
  }
82
87
 
83
88
  /**
84
89
  * Get current config.
85
- * @param {string} key
86
- * @param {*} val
90
+ * @param {string} [key]
91
+ * @param {*} [val]
87
92
  * @return {*}
88
93
  */
89
94
  static get(key, val) {
package/lib/event.js CHANGED
@@ -99,11 +99,13 @@ module.exports = {
99
99
  * @property {'global.before'} before
100
100
  * @property {'global.after'} after
101
101
  * @property {'global.result'} result
102
+ * @property {'global.failures'} failures
102
103
  */
103
104
  all: {
104
105
  before: 'global.before',
105
106
  after: 'global.after',
106
107
  result: 'global.result',
108
+ failures: 'global.failures',
107
109
  },
108
110
  /**
109
111
  * @type {object}
@@ -183,6 +183,7 @@ class Appium extends Webdriver {
183
183
  config.capabilities.browserName = config.browser || config.capabilities.browserName;
184
184
  config.capabilities.app = config.app || config.capabilities.app;
185
185
  config.capabilities.platformName = config.platform || config.capabilities.platformName;
186
+ config.capabilities.tunnelIdentifier = config.tunnelIdentifier || config.capabilities.tunnelIdentifier; // Adding the code to connect to sauce labs via sauce tunnel
186
187
  config.waitForTimeout /= 1000; // convert to seconds
187
188
 
188
189
  // [CodeceptJS compatible] transform host to hostname
@@ -1,8 +1,6 @@
1
- let axios = require('axios');
1
+ const axios = require('axios').default;
2
2
  const Helper = require('../helper');
3
3
 
4
- let headers = {};
5
-
6
4
  /**
7
5
  * GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests.
8
6
  * [Axios](https://github.com/axios/axios) library is used to perform requests.
@@ -41,15 +39,16 @@ let headers = {};
41
39
  class GraphQL extends Helper {
42
40
  constructor(config) {
43
41
  super(config);
44
- axios = require('axios');
42
+ this.axios = axios.create();
43
+ this.headers = {};
45
44
  this.options = {
46
45
  timeout: 10000,
47
46
  defaultHeaders: {},
48
47
  endpoint: '',
49
48
  };
50
49
  this.options = Object.assign(this.options, config);
51
- headers = { ...this.options.defaultHeaders };
52
- axios.defaults.headers = this.options.defaultHeaders;
50
+ this.headers = { ...this.options.defaultHeaders };
51
+ this.axios.defaults.headers = this.options.defaultHeaders;
53
52
  }
54
53
 
55
54
  static _checkRequirements() {
@@ -66,10 +65,10 @@ class GraphQL extends Helper {
66
65
  * @param {object} request
67
66
  */
68
67
  async _executeQuery(request) {
69
- axios.defaults.timeout = request.timeout || this.options.timeout;
68
+ this.axios.defaults.timeout = request.timeout || this.options.timeout;
70
69
 
71
- if (headers && headers.auth) {
72
- request.auth = headers.auth;
70
+ if (this.headers && this.headers.auth) {
71
+ request.auth = this.headers.auth;
73
72
  }
74
73
 
75
74
  request.headers = Object.assign(request.headers, {
@@ -84,7 +83,7 @@ class GraphQL extends Helper {
84
83
 
85
84
  let response;
86
85
  try {
87
- response = await axios(request);
86
+ response = await this.axios(request);
88
87
  } catch (err) {
89
88
  if (!err.response) throw err;
90
89
  this.debugSection(
@@ -803,7 +803,7 @@ class Nightmare extends Helper {
803
803
  }
804
804
 
805
805
  /**
806
- * {{> grabValueFrom }}
806
+ * {{> grabValueFromAll }}
807
807
  */
808
808
  async grabValueFromAll(locator) {
809
809
  locator = new Locator(locator, 'css');