codeceptjs 3.6.7 → 4.0.0-beta.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.
Files changed (149) hide show
  1. package/bin/codecept.js +81 -84
  2. package/lib/actor.js +13 -13
  3. package/lib/ai.js +13 -10
  4. package/lib/assert/empty.js +21 -20
  5. package/lib/assert/equal.js +39 -37
  6. package/lib/assert/error.js +14 -14
  7. package/lib/assert/include.js +47 -46
  8. package/lib/assert/throws.js +11 -13
  9. package/lib/assert/truth.js +22 -19
  10. package/lib/assert.js +2 -4
  11. package/lib/cli.js +49 -57
  12. package/lib/codecept.js +155 -142
  13. package/lib/colorUtils.js +3 -3
  14. package/lib/command/configMigrate.js +52 -58
  15. package/lib/command/definitions.js +89 -88
  16. package/lib/command/dryRun.js +68 -71
  17. package/lib/command/generate.js +188 -197
  18. package/lib/command/gherkin/init.js +16 -27
  19. package/lib/command/gherkin/snippets.js +20 -20
  20. package/lib/command/gherkin/steps.js +8 -8
  21. package/lib/command/info.js +38 -40
  22. package/lib/command/init.js +288 -290
  23. package/lib/command/interactive.js +32 -32
  24. package/lib/command/list.js +26 -26
  25. package/lib/command/run-multiple/chunk.js +5 -5
  26. package/lib/command/run-multiple/collection.js +3 -3
  27. package/lib/command/run-multiple/run.js +2 -6
  28. package/lib/command/run-multiple.js +93 -113
  29. package/lib/command/run-rerun.js +25 -20
  30. package/lib/command/run-workers.js +66 -64
  31. package/lib/command/run.js +29 -26
  32. package/lib/command/utils.js +65 -80
  33. package/lib/command/workers/runTests.js +10 -10
  34. package/lib/config.js +9 -10
  35. package/lib/container.js +48 -40
  36. package/lib/data/context.js +59 -60
  37. package/lib/data/dataScenarioConfig.js +47 -47
  38. package/lib/data/dataTableArgument.js +29 -29
  39. package/lib/data/table.js +20 -26
  40. package/lib/dirname.js +5 -0
  41. package/lib/event.js +167 -163
  42. package/lib/heal.js +17 -13
  43. package/lib/helper/AI.js +41 -130
  44. package/lib/helper/ApiDataFactory.js +69 -73
  45. package/lib/helper/Appium.js +381 -412
  46. package/lib/helper/Expect.js +425 -0
  47. package/lib/helper/ExpectHelper.js +48 -40
  48. package/lib/helper/FileSystem.js +79 -80
  49. package/lib/helper/GraphQL.js +43 -44
  50. package/lib/helper/GraphQLDataFactory.js +50 -50
  51. package/lib/helper/JSONResponse.js +62 -65
  52. package/lib/helper/Mochawesome.js +28 -28
  53. package/lib/helper/MockServer.js +14 -12
  54. package/lib/helper/Nightmare.js +566 -662
  55. package/lib/helper/Playwright.js +1216 -1361
  56. package/lib/helper/Protractor.js +627 -663
  57. package/lib/helper/Puppeteer.js +1128 -1231
  58. package/lib/helper/REST.js +68 -159
  59. package/lib/helper/SoftExpectHelper.js +2 -2
  60. package/lib/helper/TestCafe.js +484 -490
  61. package/lib/helper/WebDriver.js +1156 -1297
  62. package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
  63. package/lib/helper/errors/ConnectionRefused.js +1 -1
  64. package/lib/helper/errors/ElementAssertion.js +2 -2
  65. package/lib/helper/errors/ElementNotFound.js +2 -2
  66. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
  67. package/lib/helper/extras/Console.js +1 -1
  68. package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +18 -21
  71. package/lib/helper/extras/Popup.js +1 -1
  72. package/lib/helper/extras/React.js +3 -3
  73. package/lib/helper/network/actions.js +7 -14
  74. package/lib/helper/network/utils.js +2 -3
  75. package/lib/helper/scripts/blurElement.js +1 -1
  76. package/lib/helper/scripts/focusElement.js +1 -1
  77. package/lib/helper/scripts/highlightElement.js +1 -1
  78. package/lib/helper/scripts/isElementClickable.js +1 -1
  79. package/lib/helper/testcafe/testControllerHolder.js +1 -1
  80. package/lib/helper/testcafe/testcafe-utils.js +7 -6
  81. package/lib/helper.js +3 -1
  82. package/lib/history.js +5 -6
  83. package/lib/hooks.js +6 -6
  84. package/lib/html.js +7 -7
  85. package/lib/index.js +41 -25
  86. package/lib/interfaces/bdd.js +64 -47
  87. package/lib/interfaces/featureConfig.js +19 -19
  88. package/lib/interfaces/gherkin.js +118 -124
  89. package/lib/interfaces/scenarioConfig.js +29 -29
  90. package/lib/listener/artifacts.js +9 -9
  91. package/lib/listener/config.js +24 -24
  92. package/lib/listener/exit.js +12 -12
  93. package/lib/listener/helpers.js +42 -42
  94. package/lib/listener/mocha.js +11 -11
  95. package/lib/listener/retry.js +30 -32
  96. package/lib/listener/steps.js +53 -50
  97. package/lib/listener/timeout.js +54 -54
  98. package/lib/locator.js +10 -6
  99. package/lib/mochaFactory.js +15 -18
  100. package/lib/output.js +10 -6
  101. package/lib/parser.js +12 -15
  102. package/lib/pause.js +33 -40
  103. package/lib/plugin/allure.js +15 -15
  104. package/lib/plugin/autoDelay.js +37 -29
  105. package/lib/plugin/autoLogin.js +65 -70
  106. package/lib/plugin/commentStep.js +18 -18
  107. package/lib/plugin/coverage.js +67 -115
  108. package/lib/plugin/customLocator.js +20 -21
  109. package/lib/plugin/debugErrors.js +24 -24
  110. package/lib/plugin/eachElement.js +38 -38
  111. package/lib/plugin/fakerTransform.js +6 -6
  112. package/lib/plugin/heal.js +108 -67
  113. package/lib/plugin/pauseOnFail.js +11 -11
  114. package/lib/plugin/retryFailedStep.js +39 -32
  115. package/lib/plugin/retryTo.js +40 -46
  116. package/lib/plugin/screenshotOnFail.js +87 -109
  117. package/lib/plugin/selenoid.js +118 -131
  118. package/lib/plugin/standardActingHelpers.js +8 -2
  119. package/lib/plugin/stepByStepReport.js +91 -110
  120. package/lib/plugin/stepTimeout.js +23 -24
  121. package/lib/plugin/subtitles.js +35 -34
  122. package/lib/plugin/tryTo.js +30 -40
  123. package/lib/plugin/wdio.js +75 -78
  124. package/lib/recorder.js +17 -14
  125. package/lib/rerun.js +10 -11
  126. package/lib/scenario.js +23 -25
  127. package/lib/secret.js +2 -4
  128. package/lib/session.js +10 -10
  129. package/lib/step.js +9 -12
  130. package/lib/store.js +3 -2
  131. package/lib/transform.js +1 -1
  132. package/lib/translation.js +8 -7
  133. package/lib/ui.js +14 -12
  134. package/lib/utils.js +72 -70
  135. package/lib/within.js +10 -10
  136. package/lib/workerStorage.js +25 -27
  137. package/lib/workers.js +32 -29
  138. package/package.json +53 -51
  139. package/translations/de-DE.js +1 -1
  140. package/translations/fr-FR.js +1 -1
  141. package/translations/index.js +13 -9
  142. package/translations/it-IT.js +1 -1
  143. package/translations/ja-JP.js +1 -1
  144. package/translations/pl-PL.js +1 -1
  145. package/translations/pt-BR.js +1 -1
  146. package/translations/ru-RU.js +1 -1
  147. package/translations/zh-CN.js +1 -1
  148. package/translations/zh-TW.js +1 -1
  149. package/typings/index.d.ts +65 -415
@@ -1,14 +1,19 @@
1
- const debug = require('debug')('codeceptjs:heal')
2
- const colors = require('chalk')
3
- const recorder = require('../recorder')
4
- const event = require('../event')
5
- const output = require('../output')
6
- const heal = require('../heal')
7
- const store = require('../store')
1
+ import debug from 'debug';
2
+ import colors from 'chalk';
3
+ import Container from '../container.js';
4
+ import AiAssistant from '../ai.js';
5
+ import recorder from '../recorder.js';
6
+ import * as event from '../event.js';
7
+ import * as output from '../output.js';
8
+ import supportedHelpers from './standardActingHelpers.js';
9
+
10
+ debug('codeceptjs:heal');
11
+ const heal = require('../heal');
12
+ const store = require('../store');
8
13
 
9
14
  const defaultConfig = {
10
15
  healLimit: 2,
11
- }
16
+ };
12
17
 
13
18
  /**
14
19
  * Self-healing tests with AI.
@@ -28,94 +33,130 @@ const defaultConfig = {
28
33
  * * `healLimit` - how many steps can be healed in a single test (default: 2)
29
34
  *
30
35
  */
31
- module.exports = function (config = {}) {
32
- if (store.debugMode && !process.env.DEBUG) {
36
+
37
+ export default function (config = {}) {
38
+ if (store.store.debugMode && !process.env.DEBUG) {
33
39
  event.dispatcher.on(event.test.failed, () => {
34
- output.plugin(
35
- 'heal',
36
- 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode',
37
- )
38
- })
39
- return
40
+ output.output.plugin('heal', 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode');
41
+ });
42
+ return;
40
43
  }
41
44
 
42
- let currentTest = null
43
- let currentStep = null
44
- let healedSteps = 0
45
- let caughtError
46
- let healTries = 0
47
- let isHealing = false
45
+ let currentTest = null;
46
+ let currentStep = null;
47
+ let healedSteps = 0;
48
+ let caughtError;
49
+ let healTries = 0;
50
+ let isHealing = false;
48
51
 
49
- config = Object.assign(defaultConfig, config)
52
+ config = Object.assign(defaultConfig, config);
50
53
 
51
54
  event.dispatcher.on(event.test.before, (test) => {
52
- currentTest = test
53
- healedSteps = 0
54
- caughtError = null
55
- })
55
+ currentTest = test;
56
+ healedSteps = 0;
57
+ caughtError = null;
58
+ });
56
59
 
57
- event.dispatcher.on(event.step.started, (step) => (currentStep = step))
60
+ event.dispatcher.on(event.step.started, step => currentStep = step);
58
61
 
59
62
  event.dispatcher.on(event.step.after, (step) => {
60
- if (isHealing) return
61
- if (healTries >= config.healLimit) return // out of limit
63
+ if (isHealing) return;
64
+ if (store.store.debugMode) return;
65
+ if (healTries >= config.healLimit) return; // out of limit
62
66
 
63
- if (!heal.hasCorrespondingRecipes(step)) return
67
+ if (!heal.hasCorrespondingRecipes(step)) return;
64
68
 
65
69
  recorder.catchWithoutStop(async (err) => {
66
- isHealing = true
67
- if (caughtError === err) throw err // avoid double handling
68
- caughtError = err
70
+ isHealing = true;
71
+ if (caughtError === err) throw err; // avoid double handling
72
+ caughtError = err;
69
73
 
70
- const test = currentTest
74
+ const test = currentTest;
71
75
 
72
- recorder.session.start('heal')
76
+ recorder.session.start('heal');
73
77
 
74
- debug('Self-healing started', step.toCode())
78
+ debug('Self-healing started', step.toCode());
75
79
 
76
- await heal.healStep(step, err, { test })
80
+ await heal.healStep(step, err, { test });
77
81
 
78
- healTries++
82
+ healTries++;
79
83
 
80
84
  recorder.add('close healing session', () => {
81
- recorder.reset()
82
- recorder.session.restore('heal')
83
- recorder.ignoreErr(err)
84
- })
85
- await recorder.promise()
85
+ recorder.reset();
86
+ recorder.session.restore('heal');
87
+ recorder.ignoreErr(err);
88
+ });
89
+ await recorder.promise();
86
90
 
87
- isHealing = false
88
- })
89
- })
91
+ isHealing = false;
92
+ });
93
+ });
90
94
 
91
95
  event.dispatcher.on(event.all.result, () => {
92
- if (!heal.fixes?.length) return
96
+ if (!heal.fixes?.length) return;
93
97
 
94
- const { print } = output
98
+ const { print } = output;
95
99
 
96
- print('')
97
- print('===================')
98
- print(colors.bold.green('Self-Healing Report:'))
100
+ print('');
101
+ print('===================');
102
+ print(colors.bold.green('Self-Healing Report:'));
99
103
 
100
- print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`)
104
+ print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`);
101
105
 
102
- const suggestions = heal.fixes.filter((fix) => fix.recipe && heal.recipes[fix.recipe].suggest)
106
+ const suggestions = heal.fixes.filter(fix => fix.recipe && heal.recipes[fix.recipe].suggest);
103
107
 
104
- if (!suggestions.length) return
108
+ if (!suggestions.length) return;
105
109
 
106
- let i = 1
107
- print('')
108
- print('Suggested changes:')
109
- print('')
110
+ let i = 1;
111
+ print('');
112
+ print('Suggested changes:');
113
+ print('');
110
114
 
111
115
  for (const suggestion of suggestions) {
112
- print(`${i}. To fix ${colors.bold.magenta(suggestion.test?.title)}`)
113
- print(' Replace the failed code:', colors.gray(`(suggested by ${colors.bold(suggestion.recipe)})`))
114
- print(colors.red(`- ${suggestion.step.toCode()}`))
115
- print(colors.green(`+ ${suggestion.snippet}`))
116
- print(suggestion.step.line())
117
- print('')
118
- i++
116
+ print(`${i}. To fix ${colors.bold.magenta(suggestion.test?.title)}`);
117
+ print(' Replace the failed code:', colors.gray(`(suggested by ${colors.bold(suggestion.recipe)})`));
118
+ print(colors.red(`- ${suggestion.step.toCode()}`));
119
+ print(colors.green(`+ ${suggestion.snippet}`));
120
+ print(suggestion.step.line());
121
+ print('');
122
+ i++;
123
+ }
124
+ });
125
+
126
+ async function tryToHeal(failedStep, err) {
127
+ output.output.debug(`Running OpenAI to heal ${failedStep.toCode()} step`);
128
+
129
+ const codeSnippets = await AiAssistant.healFailedStep(failedStep, err, currentTest);
130
+
131
+ output.output.debug(`Received ${codeSnippets.length} suggestions from OpenAI`);
132
+ const I = Container.support('I'); // eslint-disable-line
133
+
134
+ for (const codeSnippet of codeSnippets) {
135
+ try {
136
+ debug('Executing', codeSnippet);
137
+ recorder.catch((e) => {
138
+ console.log(e);
139
+ });
140
+ await eval(codeSnippet); // eslint-disable-line
141
+
142
+ let healSuggestions = [];
143
+ healSuggestions.push({
144
+ test: currentTest,
145
+ step: failedStep,
146
+ snippet: codeSnippet,
147
+ });
148
+
149
+ recorder.add('healed', () => output.print(colors.bold.green(' Code healed successfully')));
150
+ healedSteps++;
151
+ return;
152
+ } catch (err) {
153
+ debug('Failed to execute code', err);
154
+ recorder.ignoreErr(err); // healing ded not help
155
+ // recorder.catch(() => output.print(colors.bold.red(' Failed healing code')));
156
+ }
119
157
  }
120
- })
158
+
159
+ output.output.debug(`Couldn't heal the code for ${failedStep.toCode()}`);
160
+ }
161
+ return recorder.promise();
121
162
  }
@@ -1,5 +1,5 @@
1
- const event = require('../event')
2
- const pause = require('../pause')
1
+ import * as event from '../event.js';
2
+ import pause from '../pause';
3
3
 
4
4
  /**
5
5
  * Automatically launches [interactive pause](/basics/#pause) when a test fails.
@@ -21,18 +21,18 @@ const pause = require('../pause')
21
21
  * ```
22
22
  *
23
23
  */
24
- module.exports = () => {
25
- let failed = false
24
+ export default () => {
25
+ let failed = false;
26
26
 
27
27
  event.dispatcher.on(event.test.started, () => {
28
- failed = false
29
- })
28
+ failed = false;
29
+ });
30
30
 
31
31
  event.dispatcher.on(event.step.failed, () => {
32
- failed = true
33
- })
32
+ failed = true;
33
+ });
34
34
 
35
35
  event.dispatcher.on(event.test.after, () => {
36
- if (failed) pause()
37
- })
38
- }
36
+ if (failed) pause();
37
+ });
38
+ };
@@ -1,14 +1,22 @@
1
- const event = require('../event')
2
- const recorder = require('../recorder')
3
- const container = require('../container')
4
- const { log } = require('../output')
1
+ import * as event from '../event.js';
2
+ import recorder from '../recorder.js';
3
+ import * as output from '../output.js';
4
+
5
+ import { store } from '../store.js';
5
6
 
6
7
  const defaultConfig = {
7
8
  retries: 3,
8
- defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
9
+ defaultIgnoredSteps: [
10
+ 'amOnPage',
11
+ 'wait*',
12
+ 'send*',
13
+ 'execute*',
14
+ 'run*',
15
+ 'have*',
16
+ ],
9
17
  factor: 1.5,
10
18
  ignoredSteps: [],
11
- }
19
+ };
12
20
 
13
21
  /**
14
22
  * Retries each failed step in a test.
@@ -76,46 +84,45 @@ const defaultConfig = {
76
84
  * ```
77
85
  *
78
86
  */
79
- module.exports = (config) => {
80
- config = Object.assign(defaultConfig, config)
81
- config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
82
- const customWhen = config.when
87
+ export default (config) => {
88
+ config = Object.assign(defaultConfig, config);
89
+ config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps);
90
+ const customWhen = config.when;
83
91
 
84
- let enableRetry = false
92
+ let enableRetry = false;
85
93
 
86
94
  const when = (err) => {
87
- if (!enableRetry) return
88
- const store = require('../store')
89
- if (store.debugMode) return false
90
- if (customWhen) return customWhen(err)
91
- return true
92
- }
93
- config.when = when
95
+ if (!enableRetry) return;
96
+ if (store.debugMode) return false;
97
+ if (customWhen) return customWhen(err);
98
+ return true;
99
+ };
100
+ config.when = when;
94
101
 
95
102
  event.dispatcher.on(event.step.started, (step) => {
96
103
  if (process.env.TRY_TO === 'true') {
97
- log('Info: RetryFailedStep plugin is disabled inside tryTo block')
98
- return
104
+ output.output.log('Info: RetryFailedStep plugin is disabled inside tryTo block');
105
+ return;
99
106
  }
100
107
 
101
108
  // if a step is ignored - return
102
109
  for (const ignored of config.ignoredSteps) {
103
- if (step.name === ignored) return
110
+ if (step.name === ignored) return;
104
111
  if (ignored instanceof RegExp) {
105
- if (step.name.match(ignored)) return
106
- } else if (ignored.indexOf('*') && step.name.startsWith(ignored.slice(0, -1))) return
112
+ if (step.name.match(ignored)) return;
113
+ } else if (ignored.indexOf('*') && step.name.startsWith(ignored.slice(0, -1))) return;
107
114
  }
108
- enableRetry = true // enable retry for a step
109
- })
115
+ enableRetry = true; // enable retry for a step
116
+ });
110
117
 
111
118
  event.dispatcher.on(event.step.finished, () => {
112
- enableRetry = false
113
- })
119
+ enableRetry = false;
120
+ });
114
121
 
115
122
  event.dispatcher.on(event.test.before, (test) => {
116
- if (test && test.disableRetryFailedStep) return // disable retry when a test is not active
123
+ if (test && test.disableRetryFailedStep) return; // disable retry when a test is not active
117
124
  // this env var is used to set the retries inside _before() block of helpers
118
- process.env.FAILED_STEP_RETRIES = config.retries
119
- recorder.retry(config)
120
- })
121
- }
125
+ process.env.FAILED_STEP_RETRIES = config.retries;
126
+ recorder.retry(config);
127
+ });
128
+ };
@@ -1,10 +1,10 @@
1
- const recorder = require('../recorder')
2
- const { debug } = require('../output')
1
+ import recorder from '../recorder.js';
2
+ import * as output from '../output.js';
3
3
 
4
4
  const defaultConfig = {
5
5
  registerGlobal: true,
6
6
  pollInterval: 200,
7
- }
7
+ };
8
8
 
9
9
  /**
10
10
  *
@@ -72,56 +72,50 @@ const defaultConfig = {
72
72
  * const retryTo = codeceptjs.container.plugins('retryTo');
73
73
  * ```
74
74
  *
75
- */
76
- module.exports = function (config) {
77
- config = Object.assign(defaultConfig, config)
78
- function retryTo(callback, maxTries, pollInterval = config.pollInterval) {
79
- return new Promise((done, reject) => {
80
- let tries = 1
75
+ */
76
+ export default function (config) {
77
+ config = Object.assign(defaultConfig, config);
81
78
 
82
- function handleRetryException(err) {
83
- recorder.throw(err)
84
- reject(err)
85
- }
79
+ if (config.registerGlobal) {
80
+ // @ts-ignore
81
+ global.retryTo = retryTo;
82
+ }
83
+ return retryTo;
86
84
 
87
- const tryBlock = async () => {
88
- tries++
89
- recorder.session.start(`retryTo ${tries}`)
90
- try {
91
- await callback(tries)
92
- } catch (err) {
93
- handleRetryException(err)
94
- }
85
+ function retryTo(callback, maxTries, pollInterval = undefined) {
86
+ let tries = 1;
87
+ if (!pollInterval) pollInterval = config.pollInterval;
95
88
 
96
- // Call done if no errors
97
- recorder.add(() => {
98
- recorder.session.restore(`retryTo ${tries}`)
99
- done(null)
100
- })
89
+ let err = null;
101
90
 
102
- // Catch errors and retry
103
- recorder.session.catch((err) => {
104
- recorder.session.restore(`retryTo ${tries}`)
91
+ return new Promise((done) => {
92
+ const tryBlock = async () => {
93
+ recorder.session.start(`retryTo ${tries}`);
94
+ await callback(tries);
95
+ recorder.add(() => {
96
+ recorder.session.restore(`retryTo ${tries}`);
97
+ done(null);
98
+ });
99
+ recorder.session.catch((e) => {
100
+ err = e;
101
+ recorder.session.restore(`retryTo ${tries}`);
102
+ tries++;
105
103
  if (tries <= maxTries) {
106
- debug(`Error ${err}... Retrying`)
107
- recorder.add(`retryTo ${tries}`, () => setTimeout(tryBlock, pollInterval))
104
+ output.output.debug(`Error ${err}... Retrying`);
105
+ err = null;
106
+
107
+ recorder.add(`retryTo ${tries}`, () => setTimeout(tryBlock, pollInterval));
108
108
  } else {
109
- // if maxTries reached
110
- handleRetryException(err)
109
+ done(null);
111
110
  }
112
- })
113
- }
114
-
115
- recorder.add('retryTo', tryBlock).catch((err) => {
116
- console.error('An error occurred:', err)
117
- done(null)
118
- })
119
- })
120
- }
111
+ });
112
+ };
121
113
 
122
- if (config.registerGlobal) {
123
- global.retryTo = retryTo
114
+ recorder.add('retryTo', async () => {
115
+ tryBlock();
116
+ });
117
+ }).then(() => {
118
+ if (err) recorder.throw(err);
119
+ });
124
120
  }
125
-
126
- return retryTo
127
121
  }