codeceptjs 4.0.0-beta.2 → 4.0.0-beta.20

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 (209) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +71 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +262 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +301 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +109 -50
  39. package/lib/container.js +641 -261
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +47 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/loaderCheck.js +124 -0
  157. package/lib/utils/mask_data.js +47 -0
  158. package/lib/utils/typescript.js +237 -0
  159. package/lib/utils.js +411 -228
  160. package/lib/workerStorage.js +37 -34
  161. package/lib/workers.js +532 -296
  162. package/package.json +124 -95
  163. package/translations/de-DE.js +5 -3
  164. package/translations/fr-FR.js +5 -4
  165. package/translations/index.js +22 -12
  166. package/translations/it-IT.js +4 -3
  167. package/translations/ja-JP.js +4 -3
  168. package/translations/nl-NL.js +76 -0
  169. package/translations/pl-PL.js +4 -3
  170. package/translations/pt-BR.js +4 -3
  171. package/translations/ru-RU.js +4 -3
  172. package/translations/utils.js +10 -0
  173. package/translations/zh-CN.js +4 -3
  174. package/translations/zh-TW.js +4 -3
  175. package/typings/index.d.ts +546 -185
  176. package/typings/promiseBasedTypes.d.ts +150 -875
  177. package/typings/types.d.ts +547 -992
  178. package/lib/cli.js +0 -249
  179. package/lib/dirname.js +0 -5
  180. package/lib/helper/Expect.js +0 -425
  181. package/lib/helper/ExpectHelper.js +0 -399
  182. package/lib/helper/MockServer.js +0 -223
  183. package/lib/helper/Nightmare.js +0 -1411
  184. package/lib/helper/Protractor.js +0 -1835
  185. package/lib/helper/SoftExpectHelper.js +0 -381
  186. package/lib/helper/TestCafe.js +0 -1410
  187. package/lib/helper/clientscripts/nightmare.js +0 -213
  188. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  189. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  190. package/lib/interfaces/bdd.js +0 -98
  191. package/lib/interfaces/featureConfig.js +0 -69
  192. package/lib/interfaces/gherkin.js +0 -195
  193. package/lib/listener/artifacts.js +0 -19
  194. package/lib/listener/retry.js +0 -68
  195. package/lib/listener/timeout.js +0 -109
  196. package/lib/mochaFactory.js +0 -110
  197. package/lib/plugin/allure.js +0 -15
  198. package/lib/plugin/commentStep.js +0 -136
  199. package/lib/plugin/debugErrors.js +0 -67
  200. package/lib/plugin/eachElement.js +0 -127
  201. package/lib/plugin/fakerTransform.js +0 -49
  202. package/lib/plugin/retryTo.js +0 -121
  203. package/lib/plugin/selenoid.js +0 -371
  204. package/lib/plugin/standardActingHelpers.js +0 -9
  205. package/lib/plugin/tryTo.js +0 -105
  206. package/lib/plugin/wdio.js +0 -246
  207. package/lib/scenario.js +0 -222
  208. package/lib/ui.js +0 -238
  209. package/lib/within.js +0 -70
@@ -1,213 +0,0 @@
1
- if (!window.codeceptjs) {
2
- /**
3
- * @alias CodeceptJS.browserCodecept
4
- * @namespace
5
- */
6
- const codeceptjs = {};
7
-
8
- /**
9
- * all found elements are stored here for reuse
10
- * @inner
11
- * @type {Node[]}
12
- */
13
- codeceptjs.elements = [];
14
-
15
- /**
16
- * global context changer
17
- * @inner
18
- * @type {?Node}
19
- */
20
- codeceptjs.within = null;
21
-
22
- // save
23
- const storeElement = function (el) {
24
- if (!el) return;
25
- return codeceptjs.elements.push(el) - 1; // return index
26
- };
27
-
28
- const storeElements = function (els) {
29
- return els.map(el => storeElement(el));
30
- };
31
-
32
- /**
33
- * finders
34
- * @param {number} id
35
- * @return {Node}
36
- */
37
- codeceptjs.fetchElement = function (id) {
38
- if (!this.elements[id]) throw new Error(`Element (${id}) is not accessible`);
39
- return this.elements[id];
40
- };
41
-
42
- /**
43
- * @param {string} by
44
- * @param {CodeceptJS.ILocator} locator
45
- * @param {*} [contextEl]
46
- * @return {number[]}
47
- */
48
- codeceptjs.findAndStoreElements = function (by, locator, contextEl) {
49
- return storeElements(this.findElements(by, locator, contextEl));
50
- };
51
-
52
- /**
53
- * @param {string} by
54
- * @param {CodeceptJS.ILocator} locator
55
- * @param {*} [contextEl]
56
- * @return {number | undefined}
57
- */
58
- codeceptjs.findAndStoreElement = function (by, locator, contextEl) {
59
- return storeElement(this.findElement(by, locator, contextEl));
60
- };
61
-
62
- /**
63
- * @param {string} by
64
- * @param {CodeceptJS.ILocator} locator
65
- */
66
- codeceptjs.setWithin = function (by, locator) {
67
- this.within = this.findElement(by, locator);
68
- };
69
-
70
- /**
71
- * @param {string} by
72
- * @param {CodeceptJS.ILocator} locator
73
- * @param {*} [contextEl]
74
- * @return {Node[]}
75
- */
76
- codeceptjs.findElements = function (by, locator, contextEl) {
77
- let context;
78
- if (typeof contextEl !== 'number') {
79
- context = this.within || document;
80
- } else {
81
- context = this.fetchElement(contextEl);
82
- }
83
-
84
- if (by === 'name') {
85
- by = 'css';
86
- locator = `*[name="${locator}"]`;
87
- }
88
- if (by === 'id') {
89
- by = 'css';
90
- locator = `#${locator}`;
91
- }
92
-
93
- if (by === 'css') {
94
- const found = context.querySelectorAll(locator);
95
- const res = [];
96
- for (let i = 0; i < found.length; i++) {
97
- res.push(found[i]);
98
- }
99
- return res;
100
- }
101
-
102
- if (by === 'xpath') {
103
- const found = document.evaluate(locator, context, null, 5, null);
104
- const res = [];
105
- let current = null;
106
- while (current = found.iterateNext()) {
107
- res.push(current);
108
- }
109
- return res;
110
- }
111
-
112
- if (by === 'frame') {
113
- if (!Array.isArray(locator)) locator = [locator];
114
- return [locator.reduce((parent, child) => parent.querySelector(child).contentDocument, window.document).querySelector('body')];
115
- }
116
- return [];
117
- };
118
-
119
- /**
120
- * @param {string} by
121
- * @param {CodeceptJS.ILocator} locator
122
- * @param {*} [context]
123
- * @return {?Node}
124
- */
125
- codeceptjs.findElement = function (by, locator, context) {
126
- return this.findElements(by, locator, context)[0] || null;
127
- };
128
-
129
- // actions
130
- /**
131
- * @param {number} el
132
- * @return {boolean}
133
- */
134
- codeceptjs.clickEl = function (el) {
135
- if (document.activeElement instanceof HTMLElement) {
136
- document.activeElement.blur();
137
- }
138
- const event = document.createEvent('MouseEvent');
139
- event.initEvent('click', true, true);
140
- return this.fetchElement(el).dispatchEvent(event);
141
- };
142
-
143
- /** @param {number} el */
144
- codeceptjs.doubleClickEl = function (el) {
145
- if (document.activeElement instanceof HTMLElement) {
146
- document.activeElement.blur();
147
- }
148
- const event = document.createEvent('MouseEvent');
149
- event.initEvent('dblclick', true, true);
150
- this.fetchElement(el).dispatchEvent(event);
151
- };
152
-
153
- /**
154
- * @param {number} el
155
- * @param {number | undefined} x
156
- * @param {number | undefined} y
157
- */
158
- codeceptjs.hoverEl = function (el, x, y) {
159
- if (document.activeElement instanceof HTMLElement) {
160
- document.activeElement.blur();
161
- }
162
-
163
- const event = new MouseEvent('mouseover', {
164
- bubbles: true,
165
- cancelable: true,
166
- screenX: x,
167
- screenY: y,
168
- clientX: x,
169
- clientY: y,
170
- });
171
-
172
- this.fetchElement(el).dispatchEvent(event);
173
- };
174
-
175
- /** @param {number} el */
176
- codeceptjs.rightClickEl = function (el) {
177
- const event = new MouseEvent('contextmenu', {
178
- bubbles: true,
179
- cancelable: true,
180
- view: window,
181
- buttons: 2,
182
- });
183
-
184
- this.fetchElement(el).dispatchEvent(event);
185
- };
186
-
187
- /**
188
- * @param {number} el
189
- * @return {boolean | undefined}
190
- */
191
- codeceptjs.checkEl = function (el) {
192
- const element = this.fetchElement(el);
193
- const event = document.createEvent('HTMLEvents');
194
- if (element.checked) return;
195
- element.checked = true;
196
- event.initEvent('change', true, true);
197
- return element.dispatchEvent(event);
198
- };
199
-
200
- /**
201
- * @param {number} el
202
- * @return {boolean}
203
- */
204
- codeceptjs.unCheckEl = function (el) {
205
- const element = this.fetchElement(el);
206
- const event = document.createEvent('HTMLEvents');
207
- element.checked = false;
208
- event.initEvent('change', true, true);
209
- return element.dispatchEvent(event);
210
- };
211
-
212
- window.codeceptjs = codeceptjs;
213
- }
@@ -1,42 +0,0 @@
1
- const testControllerHolder = {
2
-
3
- testController: undefined,
4
- captureResolver: undefined,
5
- getResolver: undefined,
6
-
7
- capture(t) {
8
- testControllerHolder.testController = t;
9
-
10
- if (testControllerHolder.getResolver) {
11
- // @ts-ignore
12
- testControllerHolder.getResolver(t);
13
- }
14
-
15
- return new Promise((resolve) => {
16
- // @ts-ignore
17
- testControllerHolder.captureResolver = resolve;
18
- });
19
- },
20
-
21
- free() {
22
- testControllerHolder.testController = undefined;
23
-
24
- if (testControllerHolder.captureResolver) {
25
- // @ts-ignore
26
- testControllerHolder.captureResolver();
27
- }
28
- },
29
-
30
- get() {
31
- return new Promise((resolve) => {
32
- if (testControllerHolder.testController) {
33
- resolve(testControllerHolder.testController);
34
- } else {
35
- // @ts-ignore
36
- testControllerHolder.getResolver = resolve;
37
- }
38
- });
39
- },
40
- };
41
-
42
- export default testControllerHolder;
@@ -1,63 +0,0 @@
1
- import { ClientFunction } from 'testcafe';
2
- import assert from 'assert';
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { getParamNames } from '../../utils.js';
6
-
7
- const __dirname = path.resolve();
8
-
9
- const createTestFile = () => {
10
- assert(global.output_dir, 'global.output_dir must be set');
11
-
12
- const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
13
- const testControllerHolderDir = __dirname.replace(/\\/g, '/');
14
-
15
- fs.writeFileSync(
16
- testFile,
17
- `import testControllerHolder from "${testControllerHolderDir}/testControllerHolder.js";\n\n
18
- fixture("fixture")\n
19
- test\n
20
- ("test", testControllerHolder.capture)`,
21
- );
22
-
23
- return testFile;
24
- };
25
-
26
- // TODO Better error mapping (actual, expected)
27
- const mapError = (testcafeError) => {
28
- // console.log('TODO map error better', JSON.stringify(testcafeError, null, 2));
29
- if (testcafeError.errMsg) {
30
- throw new Error(testcafeError.errMsg);
31
- }
32
- const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`;
33
- throw new Error(`TestCafe Error: ${errorInfo}`);
34
- };
35
-
36
- function createClientFunction(func, args) {
37
- if (!args || !args.length) {
38
- return ClientFunction(func);
39
- }
40
- const paramNames = getParamNames(func);
41
- const dependencies = {};
42
- paramNames.forEach((param, i) => dependencies[param] = args[i]);
43
-
44
- return ClientFunction(getFuncBody(func), { dependencies });
45
- }
46
-
47
- function getFuncBody(func) {
48
- let fnStr = func.toString();
49
- const arrowIndex = fnStr.indexOf('=>');
50
- if (arrowIndex >= 0) {
51
- fnStr = fnStr.slice(arrowIndex + 2);
52
- // eslint-disable-next-line no-new-func
53
- // eslint-disable-next-line no-eval
54
- return eval(`() => ${fnStr}`);
55
- }
56
- // TODO: support general functions
57
- }
58
-
59
- export default {
60
- createTestFile,
61
- mapError,
62
- createClientFunction,
63
- };
@@ -1,98 +0,0 @@
1
- import { CucumberExpression, ParameterTypeRegistry, ParameterType } from '@cucumber/cucumber-expressions';
2
- import Config from '../config.js';
3
-
4
- let steps = {};
5
- const codecept_dir = '';
6
-
7
- const STACK_POSITION = 2;
8
-
9
- /**
10
- * @param {*} step
11
- * @param {*} fn
12
- */
13
- const addStep = (step, fn) => {
14
- const avoidDuplicateSteps = Config.get('gherkin', {}).avoidDuplicateSteps || false;
15
- const stack = (new Error()).stack;
16
- if (avoidDuplicateSteps && steps[step]) {
17
- throw new Error(`Step '${step}' is already defined`);
18
- }
19
- steps[step] = fn;
20
- fn.line = stack && stack.split('\n')[STACK_POSITION];
21
- if (fn.line) {
22
- fn.line = fn.line
23
- .trim()
24
- .replace(/^at (.*?)\(/, '(')
25
- .replace(codecept_dir, '.');
26
- }
27
- };
28
-
29
- const parameterTypeRegistry = new ParameterTypeRegistry();
30
-
31
- export const matchStep = (step) => {
32
- for (const stepName in steps) {
33
- if (stepName.indexOf('/') === 0) {
34
- const regExpArr = stepName.match(/^\/(.*?)\/([gimy]*)$/) || [];
35
- const res = step.match(new RegExp(regExpArr[1], regExpArr[2]));
36
- if (res) {
37
- const fn = steps[stepName];
38
- fn.params = res.slice(1);
39
- return fn;
40
- }
41
- continue;
42
- }
43
- const expression = new CucumberExpression(stepName, parameterTypeRegistry);
44
- // console.log(expression)
45
- // console.log((step))
46
- const res = expression.match(step);
47
- if (res) {
48
- const fn = steps[stepName];
49
- fn.params = res.map(arg => arg.getValue());
50
- return fn;
51
- }
52
- }
53
- throw new Error(`No steps matching "${step.toString()}"`);
54
- };
55
-
56
- export const clearSteps = () => {
57
- steps = {};
58
- };
59
-
60
- export const getSteps = () => {
61
- return steps;
62
- };
63
-
64
- export const defineParameterType = (options) => {
65
- const parameterType = buildParameterType(options);
66
- parameterTypeRegistry.defineParameterType(parameterType);
67
- };
68
-
69
- export const buildParameterType = ({
70
- name,
71
- regexp,
72
- transformer,
73
- useForSnippets,
74
- preferForRegexpMatch,
75
- }) => {
76
- if (typeof useForSnippets !== 'boolean') useForSnippets = true;
77
- if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false;
78
- return new ParameterType(
79
- name,
80
- regexp,
81
- null,
82
- transformer,
83
- useForSnippets,
84
- preferForRegexpMatch,
85
- );
86
- };
87
-
88
- export { addStep as Given };
89
- export { addStep as When };
90
- export { addStep as Then };
91
- export { addStep as And };
92
-
93
- export default {
94
- matchStep,
95
- getSteps,
96
- clearSteps,
97
- defineParameterType,
98
- };
@@ -1,69 +0,0 @@
1
- /** @class */
2
- class FeatureConfig {
3
- constructor(suite) {
4
- this.suite = suite;
5
- }
6
-
7
- /**
8
- * Retry this suite for x times
9
- *
10
- * @param {number} retries
11
- * @returns {this}
12
- */
13
- retry(retries) {
14
- this.suite.retries(retries);
15
- return this;
16
- }
17
-
18
- /**
19
- * Set timeout for this suite
20
- * @param {number} timeout
21
- * @returns {this}
22
- * @deprecated
23
- */
24
- timeout(timeout) {
25
- console.log(`Feature('${this.suite.title}').timeout(${timeout}) is deprecated!`);
26
- console.log(`Please use Feature('${this.suite.title}', { timeout: ${timeout / 1000} }) instead`);
27
- console.log('Timeout should be set in seconds');
28
- this.suite.timeout(timeout);
29
- return this;
30
- }
31
-
32
- /**
33
- * Configures a helper.
34
- * Helper name can be omitted and values will be applied to first helper.
35
- * @param {string | Object<string, *>} helper
36
- * @param {Object<string, *>} [obj]
37
- * @returns {this}
38
- */
39
- config(helper, obj) {
40
- if (!obj) {
41
- obj = helper;
42
- helper = 0;
43
- }
44
- if (typeof obj === 'function') {
45
- obj = obj(this.suite);
46
- }
47
- if (!this.suite.config) {
48
- this.suite.config = {};
49
- }
50
- this.suite.config[helper] = obj;
51
- return this;
52
- }
53
-
54
- /**
55
- * Append a tag name to scenario title
56
- * @param {string} tagName
57
- * @returns {this}
58
- */
59
- tag(tagName) {
60
- if (tagName[0] !== '@') {
61
- tagName = `@${tagName}`;
62
- }
63
- this.suite.tags.push(tagName);
64
- this.suite.title = `${this.suite.title.trim()} ${tagName}`;
65
- return this;
66
- }
67
- }
68
-
69
- export default FeatureConfig;
@@ -1,195 +0,0 @@
1
- import * as Gherkin from '@cucumber/gherkin';
2
- import * as Messages from '@cucumber/messages';
3
- import { Context, Suite, Test } from 'mocha';
4
-
5
- import debug from 'debug';
6
-
7
- import { matchStep } from './bdd.js';
8
- import * as event from '../event.js';
9
- import * as scenario from '../scenario.js';
10
- import * as Step from '../step.js';
11
- import DataTableArgument from '../data/dataTableArgument.js';
12
- import transform from '../transform.js';
13
-
14
- import * as translations0 from '../../translations/index.js';
15
-
16
- debug('codeceptjs:bdd');
17
-
18
- const uuidFn = Messages.IdGenerator.uuid();
19
- const builder = new Gherkin.AstBuilder(uuidFn);
20
- const matcher = new Gherkin.GherkinClassicTokenMatcher();
21
- const parser = new Gherkin.Parser(builder, matcher);
22
- parser.stopAtFirstError = false;
23
-
24
- export default (text, file) => {
25
- const ast = parser.parse(text);
26
- let currentLanguage;
27
-
28
- if (ast.feature) {
29
- currentLanguage = getTranslation(ast.feature.language);
30
- }
31
-
32
- if (!ast.feature) {
33
- throw new Error(`No 'Features' available in Gherkin '${file}' provided!`);
34
- }
35
- const suite = new Suite(ast.feature.name, new Context());
36
- const tags = ast.feature.tags.map(t => t.name);
37
- suite.title = `${suite.title} ${tags.join(' ')}`.trim();
38
- suite.tags = tags || [];
39
- suite.comment = ast.feature.description;
40
- suite.feature = ast.feature;
41
- suite.file = file;
42
- suite.timeout(0);
43
-
44
- suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
45
- suite.afterEach('codeceptjs.after', () => scenario.teardown(suite));
46
- suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite));
47
- suite.afterAll('codeceptjs.afterSuite', () => scenario.suiteTeardown(suite));
48
-
49
- const runSteps = async (steps) => {
50
- for (const step of steps) {
51
- const metaStep = new Step.MetaStep(null, step.text);
52
- metaStep.actor = step.keyword.trim();
53
- let helperStep;
54
- const setMetaStep = (step) => {
55
- helperStep = step;
56
- if (step.metaStep) {
57
- if (step.metaStep === metaStep) {
58
- return;
59
- }
60
- setMetaStep(step.metaStep);
61
- return;
62
- }
63
- step.metaStep = metaStep;
64
- };
65
- const fn = matchStep(step.text);
66
-
67
- if (step.dataTable) {
68
- fn.params.push({
69
- ...step.dataTable,
70
- parse: () => new DataTableArgument(step.dataTable),
71
- });
72
- metaStep.comment = `\n${transformTable(step.dataTable)}`;
73
- }
74
-
75
- if (step.docString) {
76
- fn.params.push(step.docString);
77
- metaStep.comment = `\n"""\n${step.docString.content}\n"""`;
78
- }
79
-
80
- step.startTime = Date.now();
81
- step.match = fn.line;
82
- event.emit(event.bddStep.before, step);
83
- event.emit(event.bddStep.started, metaStep);
84
- event.dispatcher.prependListener(event.step.before, setMetaStep);
85
- try {
86
- debug(`Step '${step.text}' started...`);
87
- await fn(...fn.params);
88
- debug('Step passed');
89
- step.status = 'passed';
90
- } catch (err) {
91
- debug(`Step failed: ${err?.message}`);
92
- step.status = 'failed';
93
- step.err = err;
94
- throw err;
95
- } finally {
96
- step.endTime = Date.now();
97
- event.dispatcher.removeListener(event.step.before, setMetaStep);
98
- }
99
- event.emit(event.bddStep.finished, metaStep);
100
- event.emit(event.bddStep.after, step);
101
- }
102
- };
103
-
104
- for (const child of ast.feature.children) {
105
- if (child.background) {
106
- suite.beforeEach('Before', scenario.injected(async () => runSteps(child.background.steps), suite, 'before'));
107
- continue;
108
- }
109
- if (child.scenario && (currentLanguage ? child.scenario.keyword === currentLanguage.contexts.ScenarioOutline : child.scenario.keyword === 'Scenario Outline')) {
110
- for (const examples of child.scenario.examples) {
111
- const fields = examples.tableHeader.cells.map(c => c.value);
112
- for (const example of examples.tableBody) {
113
- let exampleSteps = [...child.scenario.steps];
114
- const current = {};
115
- for (const index in example.cells) {
116
- const placeholder = fields[index];
117
- const value = transform('gherkin.examples', example.cells[index].value);
118
- example.cells[index].value = value;
119
- current[placeholder] = value;
120
- exampleSteps = exampleSteps.map((step) => {
121
- step = { ...step };
122
- step.text = step.text.split(`<${placeholder}>`).join(value);
123
- return step;
124
- });
125
- }
126
- const tags = child.scenario.tags.map(t => t.name).concat(examples.tags.map(t => t.name));
127
- let title = `${child.scenario.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim();
128
-
129
- for (const [key, value] of Object.entries(current)) {
130
- if (title.includes(`<${key}>`)) {
131
- title = title.replace(JSON.stringify(current), '').replace(`<${key}>`, value);
132
- }
133
- }
134
-
135
- const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)));
136
- test.tags = suite.tags.concat(tags);
137
- test.file = file;
138
- suite.addTest(scenario.test(test));
139
- }
140
- }
141
- continue;
142
- }
143
-
144
- if (child.scenario) {
145
- const tags = child.scenario.tags.map(t => t.name);
146
- const title = `${child.scenario.name} ${tags.join(' ')}`.trim();
147
- const test = new Test(title, async () => runSteps(child.scenario.steps));
148
- test.tags = suite.tags.concat(tags);
149
- test.file = file;
150
- suite.addTest(scenario.test(test));
151
- }
152
- }
153
-
154
- return suite;
155
- };
156
-
157
- function transformTable(table) {
158
- let str = '';
159
- for (const id in table.rows) {
160
- const cells = table.rows[id].cells;
161
- str += cells.map(c => c.value).map(c => c.padEnd(15)).join(' | ');
162
- str += '\n';
163
- }
164
- return str;
165
- }
166
- function addExampleInTable(exampleSteps, placeholders) {
167
- const steps = JSON.parse(JSON.stringify(exampleSteps));
168
- for (const placeholder in placeholders) {
169
- steps.map((step) => {
170
- step = { ...step };
171
- if (step.dataTable) {
172
- for (const id in step.dataTable.rows) {
173
- const cells = step.dataTable.rows[id].cells;
174
- cells.map(c => (c.value = c.value.replace(`<${placeholder}>`, placeholders[placeholder])));
175
- }
176
- }
177
- return step;
178
- });
179
- }
180
- return steps;
181
- }
182
-
183
- function getTranslation(language) {
184
- const translations = Object.keys(translations0);
185
-
186
- for (const availableTranslation of translations) {
187
- if (!language) {
188
- break;
189
- }
190
-
191
- if (availableTranslation.includes(language)) {
192
- return translations0[availableTranslation];
193
- }
194
- }
195
- }
@@ -1,19 +0,0 @@
1
- import * as event from '../event.js';
2
- import recorder from '../recorder.js';
3
-
4
- /**
5
- * Create and clean up empty artifacts
6
- */
7
- export default function () {
8
- event.dispatcher.on(event.test.before, (test) => {
9
- test.artifacts = {};
10
- });
11
-
12
- event.dispatcher.on(event.test.after, (test) => {
13
- recorder.add('clean up empty artifacts', () => {
14
- for (const key in (test.artifacts || {})) {
15
- if (!test.artifacts[key]) delete test.artifacts[key];
16
- }
17
- });
18
- });
19
- }