codeceptjs 3.6.10 → 3.7.0-beta.10

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 (127) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +9 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +66 -102
  5. package/lib/ai.js +130 -121
  6. package/lib/assert/empty.js +3 -5
  7. package/lib/assert/equal.js +4 -7
  8. package/lib/assert/include.js +4 -6
  9. package/lib/assert/throws.js +2 -4
  10. package/lib/assert/truth.js +2 -2
  11. package/lib/codecept.js +87 -83
  12. package/lib/command/check.js +186 -0
  13. package/lib/command/configMigrate.js +2 -4
  14. package/lib/command/definitions.js +8 -26
  15. package/lib/command/generate.js +10 -14
  16. package/lib/command/gherkin/snippets.js +10 -8
  17. package/lib/command/gherkin/steps.js +1 -1
  18. package/lib/command/info.js +1 -3
  19. package/lib/command/init.js +8 -12
  20. package/lib/command/interactive.js +2 -2
  21. package/lib/command/list.js +1 -1
  22. package/lib/command/run-multiple.js +12 -35
  23. package/lib/command/run-workers.js +5 -57
  24. package/lib/command/utils.js +5 -6
  25. package/lib/command/workers/runTests.js +68 -232
  26. package/lib/container.js +354 -237
  27. package/lib/data/context.js +10 -13
  28. package/lib/data/dataScenarioConfig.js +8 -8
  29. package/lib/data/dataTableArgument.js +6 -6
  30. package/lib/data/table.js +5 -11
  31. package/lib/effects.js +218 -0
  32. package/lib/els.js +158 -0
  33. package/lib/event.js +19 -17
  34. package/lib/heal.js +88 -80
  35. package/lib/helper/AI.js +2 -1
  36. package/lib/helper/ApiDataFactory.js +3 -6
  37. package/lib/helper/Appium.js +45 -51
  38. package/lib/helper/FileSystem.js +3 -3
  39. package/lib/helper/GraphQLDataFactory.js +3 -3
  40. package/lib/helper/JSONResponse.js +57 -37
  41. package/lib/helper/Nightmare.js +35 -53
  42. package/lib/helper/Playwright.js +211 -252
  43. package/lib/helper/Protractor.js +54 -77
  44. package/lib/helper/Puppeteer.js +139 -232
  45. package/lib/helper/REST.js +5 -17
  46. package/lib/helper/TestCafe.js +21 -44
  47. package/lib/helper/WebDriver.js +131 -169
  48. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  49. package/lib/listener/emptyRun.js +55 -0
  50. package/lib/listener/exit.js +7 -10
  51. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  52. package/lib/listener/globalTimeout.js +165 -0
  53. package/lib/listener/helpers.js +15 -15
  54. package/lib/listener/mocha.js +1 -1
  55. package/lib/listener/result.js +12 -0
  56. package/lib/listener/steps.js +20 -18
  57. package/lib/listener/store.js +20 -0
  58. package/lib/mocha/asyncWrapper.js +216 -0
  59. package/lib/{interfaces → mocha}/bdd.js +3 -3
  60. package/lib/mocha/cli.js +308 -0
  61. package/lib/mocha/factory.js +104 -0
  62. package/lib/{interfaces → mocha}/featureConfig.js +24 -12
  63. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  64. package/lib/mocha/hooks.js +112 -0
  65. package/lib/mocha/index.js +12 -0
  66. package/lib/mocha/inject.js +29 -0
  67. package/lib/{interfaces → mocha}/scenarioConfig.js +21 -6
  68. package/lib/mocha/suite.js +81 -0
  69. package/lib/mocha/test.js +159 -0
  70. package/lib/mocha/types.d.ts +42 -0
  71. package/lib/mocha/ui.js +219 -0
  72. package/lib/output.js +82 -62
  73. package/lib/pause.js +155 -138
  74. package/lib/plugin/analyze.js +349 -0
  75. package/lib/plugin/autoDelay.js +6 -6
  76. package/lib/plugin/autoLogin.js +6 -7
  77. package/lib/plugin/commentStep.js +6 -1
  78. package/lib/plugin/coverage.js +10 -19
  79. package/lib/plugin/customLocator.js +3 -3
  80. package/lib/plugin/customReporter.js +52 -0
  81. package/lib/plugin/eachElement.js +1 -1
  82. package/lib/plugin/fakerTransform.js +1 -1
  83. package/lib/plugin/heal.js +36 -9
  84. package/lib/plugin/pageInfo.js +140 -0
  85. package/lib/plugin/retryFailedStep.js +4 -4
  86. package/lib/plugin/retryTo.js +18 -118
  87. package/lib/plugin/screenshotOnFail.js +17 -49
  88. package/lib/plugin/selenoid.js +15 -35
  89. package/lib/plugin/standardActingHelpers.js +4 -1
  90. package/lib/plugin/stepByStepReport.js +56 -17
  91. package/lib/plugin/stepTimeout.js +5 -12
  92. package/lib/plugin/subtitles.js +4 -4
  93. package/lib/plugin/tryTo.js +17 -107
  94. package/lib/plugin/wdio.js +8 -10
  95. package/lib/recorder.js +146 -125
  96. package/lib/rerun.js +43 -42
  97. package/lib/result.js +161 -0
  98. package/lib/secret.js +1 -1
  99. package/lib/step/base.js +228 -0
  100. package/lib/step/config.js +50 -0
  101. package/lib/step/func.js +46 -0
  102. package/lib/step/helper.js +50 -0
  103. package/lib/step/meta.js +99 -0
  104. package/lib/step/record.js +74 -0
  105. package/lib/step/retry.js +11 -0
  106. package/lib/step/section.js +55 -0
  107. package/lib/step.js +21 -332
  108. package/lib/steps.js +50 -0
  109. package/lib/store.js +10 -2
  110. package/lib/template/heal.js +2 -11
  111. package/lib/timeout.js +66 -0
  112. package/lib/utils.js +317 -216
  113. package/lib/within.js +73 -55
  114. package/lib/workers.js +259 -275
  115. package/package.json +56 -54
  116. package/typings/index.d.ts +175 -186
  117. package/typings/promiseBasedTypes.d.ts +164 -17
  118. package/typings/types.d.ts +284 -115
  119. package/lib/cli.js +0 -256
  120. package/lib/helper/ExpectHelper.js +0 -391
  121. package/lib/helper/SoftExpectHelper.js +0 -381
  122. package/lib/listener/artifacts.js +0 -19
  123. package/lib/listener/timeout.js +0 -109
  124. package/lib/mochaFactory.js +0 -113
  125. package/lib/plugin/debugErrors.js +0 -67
  126. package/lib/scenario.js +0 -224
  127. package/lib/ui.js +0 -236
package/lib/scenario.js DELETED
@@ -1,224 +0,0 @@
1
- const promiseRetry = require('promise-retry');
2
- const event = require('./event');
3
- const recorder = require('./recorder');
4
- const assertThrown = require('./assert/throws');
5
- const { ucfirst, isAsyncFunction } = require('./utils');
6
- const parser = require('./parser');
7
-
8
- const injectHook = function (inject, suite) {
9
- try {
10
- inject();
11
- } catch (err) {
12
- recorder.throw(err);
13
- }
14
- recorder.catch((err) => {
15
- event.emit(event.test.failed, suite, err);
16
- throw err;
17
- });
18
- return recorder.promise();
19
- };
20
-
21
- function makeDoneCallableOnce(done) {
22
- let called = false;
23
- return function (err) {
24
- if (called) {
25
- return;
26
- }
27
- called = true;
28
- return done(err);
29
- };
30
- }
31
- /**
32
- * Wraps test function, injects support objects from container,
33
- * starts promise chain with recorder, performs before/after hooks
34
- * through event system.
35
- */
36
- module.exports.test = (test) => {
37
- const testFn = test.fn;
38
- if (!testFn) {
39
- return test;
40
- }
41
-
42
- test.steps = [];
43
- test.timeout(0);
44
- test.async = true;
45
-
46
- test.fn = function (done) {
47
- const doneFn = makeDoneCallableOnce(done);
48
- recorder.errHandler((err) => {
49
- recorder.session.start('teardown');
50
- recorder.cleanAsyncErr();
51
- if (test.throws) {
52
- // check that test should actually fail
53
- try {
54
- assertThrown(err, test.throws);
55
- event.emit(event.test.passed, test);
56
- event.emit(event.test.finished, test);
57
- recorder.add(doneFn);
58
- return;
59
- } catch (newErr) {
60
- err = newErr;
61
- }
62
- }
63
- event.emit(event.test.failed, test, err);
64
- event.emit(event.test.finished, test);
65
- recorder.add(() => doneFn(err));
66
- });
67
-
68
- if (isAsyncFunction(testFn)) {
69
- event.emit(event.test.started, test);
70
- testFn
71
- .call(test, getInjectedArguments(testFn, test))
72
- .then(() => {
73
- recorder.add('fire test.passed', () => {
74
- event.emit(event.test.passed, test);
75
- event.emit(event.test.finished, test);
76
- });
77
- recorder.add('finish test', doneFn);
78
- })
79
- .catch((err) => {
80
- recorder.throw(err);
81
- })
82
- .finally(() => {
83
- recorder.catch();
84
- });
85
- return;
86
- }
87
-
88
- try {
89
- event.emit(event.test.started, test);
90
- testFn.call(test, getInjectedArguments(testFn, test));
91
- } catch (err) {
92
- recorder.throw(err);
93
- } finally {
94
- recorder.add('fire test.passed', () => {
95
- event.emit(event.test.passed, test);
96
- event.emit(event.test.finished, test);
97
- });
98
- recorder.add('finish test', doneFn);
99
- recorder.catch();
100
- }
101
- };
102
- return test;
103
- };
104
-
105
- /**
106
- * Injects arguments to function from controller
107
- */
108
- module.exports.injected = function (fn, suite, hookName) {
109
- return function (done) {
110
- const doneFn = makeDoneCallableOnce(done);
111
- const errHandler = (err) => {
112
- recorder.session.start('teardown');
113
- recorder.cleanAsyncErr();
114
- event.emit(event.test.failed, suite, err);
115
- if (hookName === 'after') event.emit(event.test.after, suite);
116
- if (hookName === 'afterSuite') event.emit(event.suite.after, suite);
117
- recorder.add(() => doneFn(err));
118
- };
119
-
120
- recorder.errHandler((err) => {
121
- errHandler(err);
122
- });
123
-
124
- if (!fn) throw new Error('fn is not defined');
125
-
126
- event.emit(event.hook.started, suite);
127
-
128
- this.test.body = fn.toString();
129
-
130
- if (!recorder.isRunning()) {
131
- recorder.errHandler((err) => {
132
- errHandler(err);
133
- });
134
- }
135
-
136
- const opts = suite.opts || {};
137
- const retries = opts[`retry${ucfirst(hookName)}`] || 0;
138
-
139
- promiseRetry(
140
- async (retry, number) => {
141
- try {
142
- recorder.startUnlessRunning();
143
- await fn.call(this, getInjectedArguments(fn));
144
- await recorder.promise().catch((err) => retry(err));
145
- } catch (err) {
146
- retry(err);
147
- } finally {
148
- if (number < retries) {
149
- recorder.stop();
150
- recorder.start();
151
- }
152
- }
153
- },
154
- { retries },
155
- )
156
- .then(() => {
157
- recorder.add('fire hook.passed', () => event.emit(event.hook.passed, suite));
158
- recorder.add(`finish ${hookName} hook`, doneFn);
159
- recorder.catch();
160
- })
161
- .catch((e) => {
162
- recorder.throw(e);
163
- recorder.catch((e) => {
164
- const err = recorder.getAsyncErr() === null ? e : recorder.getAsyncErr();
165
- errHandler(err);
166
- });
167
- recorder.add('fire hook.failed', () => event.emit(event.hook.failed, suite, e));
168
- });
169
- };
170
- };
171
-
172
- /**
173
- * Starts promise chain, so helpers could enqueue their hooks
174
- */
175
- module.exports.setup = function (suite) {
176
- return injectHook(() => {
177
- recorder.startUnlessRunning();
178
- event.emit(event.test.before, suite && suite.ctx && suite.ctx.currentTest);
179
- }, suite);
180
- };
181
-
182
- module.exports.teardown = function (suite) {
183
- return injectHook(() => {
184
- recorder.startUnlessRunning();
185
- event.emit(event.test.after, suite && suite.ctx && suite.ctx.currentTest);
186
- }, suite);
187
- };
188
-
189
- module.exports.suiteSetup = function (suite) {
190
- return injectHook(() => {
191
- recorder.startUnlessRunning();
192
- event.emit(event.suite.before, suite);
193
- }, suite);
194
- };
195
-
196
- module.exports.suiteTeardown = function (suite) {
197
- return injectHook(() => {
198
- recorder.startUnlessRunning();
199
- event.emit(event.suite.after, suite);
200
- }, suite);
201
- };
202
-
203
- const getInjectedArguments = (fn, test) => {
204
- const container = require('./container');
205
- const testArgs = {};
206
- const params = parser.getParams(fn) || [];
207
- const objects = container.support();
208
- for (const key of params) {
209
- testArgs[key] = {};
210
- if (test && test.inject && test.inject[key]) {
211
- // @FIX: need fix got inject
212
- testArgs[key] = test.inject[key];
213
- continue;
214
- }
215
- if (!objects[key]) {
216
- throw new Error(`Object of type ${key} is not defined in container`);
217
- }
218
- testArgs[key] = container.support(key);
219
- }
220
-
221
- return testArgs;
222
- };
223
-
224
- module.exports.getInjectedArguments = getInjectedArguments;
package/lib/ui.js DELETED
@@ -1,236 +0,0 @@
1
- const escapeRe = require('escape-string-regexp');
2
- const Suite = require('mocha/lib/suite');
3
- const Test = require('mocha/lib/test');
4
-
5
- const scenario = require('./scenario');
6
- const ScenarioConfig = require('./interfaces/scenarioConfig');
7
- const FeatureConfig = require('./interfaces/featureConfig');
8
- const addDataContext = require('./data/context');
9
- const container = require('./container');
10
-
11
- const setContextTranslation = (context) => {
12
- const contexts = container.translation().value('contexts');
13
-
14
- if (contexts) {
15
- for (const key of Object.keys(contexts)) {
16
- if (context[key]) {
17
- context[contexts[key]] = context[key];
18
- }
19
- }
20
- }
21
- };
22
-
23
- /**
24
- * Codecept-style interface:
25
- *
26
- * Feature('login');
27
- *
28
- * Scenario('login as regular user', ({I}) {
29
- * I.fillField();
30
- * I.click();
31
- * I.see('Hello, '+data.login);
32
- * });
33
- *
34
- * @param {Mocha.Suite} suite Root suite.
35
- * @ignore
36
- */
37
- module.exports = function (suite) {
38
- const suites = [suite];
39
- suite.timeout(0);
40
- let afterAllHooks;
41
- let afterEachHooks;
42
- let afterAllHooksAreLoaded;
43
- let afterEachHooksAreLoaded;
44
-
45
- suite.on('pre-require', (context, file, mocha) => {
46
- const common = require('mocha/lib/interfaces/common')(suites, context, mocha);
47
-
48
- const addScenario = function (title, opts = {}, fn) {
49
- const suite = suites[0];
50
-
51
- if (typeof opts === 'function' && !fn) {
52
- fn = opts;
53
- opts = {};
54
- }
55
- if (suite.pending) {
56
- fn = null;
57
- }
58
- const test = new Test(title, fn);
59
- test.fullTitle = () => `${suite.title}: ${test.title}`;
60
-
61
- test.tags = (suite.tags || []).concat(title.match(/(\@[a-zA-Z0-9-_]+)/g) || []); // match tags from title
62
- test.file = file;
63
- if (!test.inject) {
64
- test.inject = {};
65
- }
66
-
67
- suite.addTest(scenario.test(test));
68
- if (opts.retries) test.retries(opts.retries);
69
- if (opts.timeout) test.totalTimeout = opts.timeout;
70
- test.opts = opts;
71
-
72
- return new ScenarioConfig(test);
73
- };
74
-
75
- // create dispatcher
76
-
77
- context.BeforeAll = common.before;
78
- context.AfterAll = common.after;
79
-
80
- context.run = mocha.options.delay && common.runWithSuite(suite);
81
- /**
82
- * Describe a "suite" with the given `title`
83
- * and callback `fn` containing nested suites
84
- * and/or tests.
85
- * @global
86
- * @param {string} title
87
- * @param {Object<string, *>} [opts]
88
- * @returns {FeatureConfig}
89
- */
90
-
91
- context.Feature = function (title, opts) {
92
- if (suites.length > 1) {
93
- suites.shift();
94
- }
95
-
96
- afterAllHooks = [];
97
- afterEachHooks = [];
98
- afterAllHooksAreLoaded = false;
99
- afterEachHooksAreLoaded = false;
100
-
101
- const suite = Suite.create(suites[0], title);
102
- if (!opts) opts = {};
103
- suite.opts = opts;
104
- suite.timeout(0);
105
-
106
- if (opts.retries) suite.retries(opts.retries);
107
- if (opts.timeout) suite.totalTimeout = opts.timeout;
108
-
109
- suite.tags = title.match(/(\@[a-zA-Z0-9-_]+)/g) || []; // match tags from title
110
- suite.file = file;
111
- suite.fullTitle = () => `${suite.title}:`;
112
- suites.unshift(suite);
113
- suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
114
- afterEachHooks.push(['finalize codeceptjs', () => scenario.teardown(suite)]);
115
-
116
- suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite));
117
- afterAllHooks.push(['codeceptjs.afterSuite', () => scenario.suiteTeardown(suite)]);
118
-
119
- if (opts.skipInfo && opts.skipInfo.skipped) {
120
- suite.pending = true;
121
- suite.opts = { ...suite.opts, skipInfo: opts.skipInfo };
122
- }
123
-
124
- return new FeatureConfig(suite);
125
- };
126
-
127
- /**
128
- * Pending test suite.
129
- * @global
130
- * @kind constant
131
- * @type {CodeceptJS.IFeature}
132
- */
133
- context.xFeature = context.Feature.skip = function (title, opts) {
134
- const skipInfo = {
135
- skipped: true,
136
- message: 'Skipped due to "skip" on Feature.',
137
- };
138
- return context.Feature(title, { ...opts, skipInfo });
139
- };
140
-
141
- context.BeforeSuite = function (fn) {
142
- suites[0].beforeAll('BeforeSuite', scenario.injected(fn, suites[0], 'beforeSuite'));
143
- };
144
-
145
- context.AfterSuite = function (fn) {
146
- afterAllHooks.unshift(['AfterSuite', scenario.injected(fn, suites[0], 'afterSuite')]);
147
- };
148
-
149
- context.Background = context.Before = function (fn) {
150
- suites[0].beforeEach('Before', scenario.injected(fn, suites[0], 'before'));
151
- };
152
-
153
- context.After = function (fn) {
154
- afterEachHooks.unshift(['After', scenario.injected(fn, suites[0], 'after')]);
155
- };
156
-
157
- /**
158
- * Describe a specification or test-case
159
- * with the given `title` and callback `fn`
160
- * acting as a thunk.
161
- * @ignore
162
- */
163
- context.Scenario = addScenario;
164
- /**
165
- * Exclusive test-case.
166
- * @ignore
167
- */
168
- context.Scenario.only = function (title, opts, fn) {
169
- const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`;
170
- mocha.grep(new RegExp(reString));
171
- process.env.SCENARIO_ONLY = true;
172
- return addScenario(title, opts, fn);
173
- };
174
-
175
- /**
176
- * Pending test case.
177
- * @global
178
- * @kind constant
179
- * @type {CodeceptJS.IScenario}
180
- */
181
- context.xScenario = context.Scenario.skip = function (title, opts = {}, fn) {
182
- if (typeof opts === 'function' && !fn) {
183
- opts = {};
184
- }
185
-
186
- return context.Scenario(title, opts);
187
- };
188
-
189
- /**
190
- * Pending test case with message: 'Test not implemented!'.
191
- * @global
192
- * @kind constant
193
- * @type {CodeceptJS.IScenario}
194
- */
195
- context.Scenario.todo = function (title, opts = {}, fn) {
196
- if (typeof opts === 'function' && !fn) {
197
- fn = opts;
198
- opts = {};
199
- }
200
-
201
- const skipInfo = {
202
- message: 'Test not implemented!',
203
- description: fn ? fn.toString() : '',
204
- };
205
-
206
- return context.Scenario(title, { ...opts, skipInfo });
207
- };
208
-
209
- /**
210
- * For translation
211
- */
212
-
213
- setContextTranslation(context);
214
-
215
- addDataContext(context);
216
- });
217
-
218
- suite.on('post-require', () => {
219
- /**
220
- * load hooks from arrays to suite to prevent reordering
221
- */
222
- if (!afterEachHooksAreLoaded && Array.isArray(afterEachHooks)) {
223
- afterEachHooks.forEach((hook) => {
224
- suites[0].afterEach(hook[0], hook[1]);
225
- });
226
- afterEachHooksAreLoaded = true;
227
- }
228
-
229
- if (!afterAllHooksAreLoaded && Array.isArray(afterAllHooks)) {
230
- afterAllHooks.forEach((hook) => {
231
- suites[0].afterAll(hook[0], hook[1]);
232
- });
233
- afterAllHooksAreLoaded = true;
234
- }
235
- });
236
- };