codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21

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 +73 -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 +765 -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 +54 -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
@@ -0,0 +1,206 @@
1
+ import { getConfig, getTestRoot } from './utils.js'
2
+ import Codecept from '../codecept.js'
3
+ import output from '../output.js'
4
+ import store from '../store.js'
5
+ import Container from '../container.js'
6
+ import figures from 'figures'
7
+ import chalk from 'chalk'
8
+ import { createTest } from '../mocha/test.js'
9
+ import { getMachineInfo } from './info.js'
10
+ import definitions from './definitions.js'
11
+
12
+ export default async function (options) {
13
+ const configFile = options.config
14
+
15
+ setTimeout(() => {
16
+ output.error("Something went wrong. Checks didn't pass and timed out. Please check your config and helpers.")
17
+ process.exit(1)
18
+ }, options.timeout || 50000)
19
+
20
+ const checks = {
21
+ config: false,
22
+ container: false,
23
+ pageObjects: false,
24
+ plugins: false,
25
+ ai: true, // we don't need to check AI
26
+ helpers: false,
27
+ setup: false,
28
+ teardown: false,
29
+ tests: false,
30
+ def: false,
31
+ }
32
+
33
+ const testRoot = getTestRoot(configFile)
34
+ let config = await getConfig(configFile)
35
+
36
+ try {
37
+ config = await getConfig(configFile)
38
+ checks['config'] = true
39
+ } catch (err) {
40
+ checks['config'] = err
41
+ }
42
+
43
+ printCheck('config', checks['config'], config.name)
44
+
45
+ let codecept
46
+ try {
47
+ codecept = new Codecept(config, options)
48
+ await codecept.init(testRoot)
49
+ await Container.started()
50
+ checks.container = true
51
+ } catch (err) {
52
+ checks.container = err
53
+ }
54
+
55
+ const standardActingHelpers = Container.STANDARD_ACTING_HELPERS
56
+
57
+ printCheck('container', checks['container'])
58
+
59
+ if (codecept) {
60
+ try {
61
+ if (options.bootstrap) await codecept.bootstrap()
62
+ checks.bootstrap = true
63
+ } catch (err) {
64
+ checks.bootstrap = err
65
+ }
66
+ printCheck('bootstrap', checks['bootstrap'], options.bootstrap ? 'Bootstrap was executed' : 'No bootstrap command')
67
+ }
68
+
69
+ let numTests = 0
70
+ if (codecept) {
71
+ try {
72
+ codecept.loadTests()
73
+ const files = codecept.testFiles
74
+ const mocha = Container.mocha()
75
+ mocha.files = files
76
+ mocha.loadFiles()
77
+
78
+ for (const suite of mocha.suite.suites) {
79
+ if (suite && suite.tests) {
80
+ numTests += suite.tests.length
81
+ }
82
+ }
83
+
84
+ if (numTests > 0) {
85
+ checks.tests = true
86
+ } else {
87
+ throw new Error('No tests found')
88
+ }
89
+ } catch (err) {
90
+ checks.tests = err
91
+ }
92
+ }
93
+
94
+ if (config?.ai?.request) {
95
+ checks.ai = true
96
+ printCheck('ai', checks['ai'], 'Configuration is enabled, request function is set')
97
+ } else {
98
+ printCheck('ai', checks['ai'], 'Disabled')
99
+ }
100
+
101
+ printCheck('tests', checks['tests'], `Total: ${numTests} tests`)
102
+
103
+ store.dryRun = true
104
+
105
+ const helpers = Container.helpers()
106
+
107
+ try {
108
+ if (!Object.keys(helpers).length) throw new Error('No helpers found')
109
+ // load helpers
110
+ for (const helper of Object.values(helpers)) {
111
+ if (helper._init) await helper._init()
112
+ }
113
+ checks.helpers = true
114
+ } catch (err) {
115
+ checks.helpers = err
116
+ }
117
+
118
+ printCheck('helpers', checks['helpers'], `${Object.keys(helpers).join(', ')}`)
119
+
120
+ const pageObjects = Container.support()
121
+
122
+ try {
123
+ if (Object.keys(pageObjects).length) {
124
+ for (const pageObject of Object.values(pageObjects)) {
125
+ pageObject.name
126
+ }
127
+ }
128
+ checks.pageObjects = true
129
+ } catch (err) {
130
+ checks.pageObjects = err
131
+ }
132
+ printCheck('page objects', checks['pageObjects'], `Total: ${Object.keys(pageObjects).length} support objects`)
133
+
134
+ checks.plugins = true // how to check plugins?
135
+ printCheck('plugins', checks['plugins'], Object.keys(Container.plugins()).join(', '))
136
+
137
+ if (Object.keys(helpers).length) {
138
+ const suite = Container.mocha().suite
139
+ const test = createTest('test', () => {})
140
+ checks.setup = true
141
+ for (const helper of Object.values(helpers)) {
142
+ try {
143
+ if (helper._beforeSuite) await helper._beforeSuite(suite)
144
+ if (helper._before) await helper._before(test)
145
+ } catch (err) {
146
+ err.message = `${helper.constructor.name} helper: ${err.message}`
147
+ if (checks.setup instanceof Error) err.message = `${err.message}\n\n${checks.setup?.message || ''}`.trim()
148
+ checks.setup = err
149
+ }
150
+ }
151
+
152
+ printCheck('Helpers Before', checks['setup'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Initializing browser' : '')
153
+
154
+ checks.teardown = true
155
+ for (const helper of Object.values(helpers).reverse()) {
156
+ try {
157
+ if (helper._passed) await helper._passed(test)
158
+ if (helper._after) await helper._after(test)
159
+ if (helper._finishTest) await helper._finishTest(suite)
160
+ if (helper._afterSuite) await helper._afterSuite(suite)
161
+ } catch (err) {
162
+ err.message = `${helper.constructor.name} helper: ${err.message}`
163
+ if (checks.teardown instanceof Error) err.message = `${err.message}\n\n${checks.teardown?.message || ''}`.trim()
164
+ checks.teardown = err
165
+ }
166
+ }
167
+
168
+ printCheck('Helpers After', checks['teardown'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Closing browser' : '')
169
+ }
170
+
171
+ try {
172
+ definitions(configFile, { dryRun: true })
173
+ checks.def = true
174
+ } catch (err) {
175
+ checks.def = err
176
+ }
177
+
178
+ printCheck('TypeScript Definitions', checks['def'])
179
+
180
+ output.print('')
181
+
182
+ if (!Object.values(checks).every(check => check === true)) {
183
+ output.error("Something went wrong. Checks didn't pass.")
184
+ output.print()
185
+ await getMachineInfo()
186
+ process.exit(1)
187
+ }
188
+
189
+ output.print(output.styles.success('All checks passed'.toUpperCase()), 'Ready to run your tests 🚀')
190
+ process.exit(0)
191
+ }
192
+
193
+ function printCheck(name, value, comment = '') {
194
+ let status = ''
195
+ if (value == true) {
196
+ status += chalk.bold.green(figures.tick)
197
+ } else {
198
+ status += chalk.bold.red(figures.cross)
199
+ }
200
+
201
+ if (value instanceof Error) {
202
+ comment = `${comment} ${chalk.red(value.message)}`.trim()
203
+ }
204
+
205
+ output.print(status, name.toUpperCase(), chalk.dim(comment))
206
+ }
@@ -1,70 +1,75 @@
1
- import colors from 'chalk';
2
- import fs from 'fs';
3
- import inquirer from 'inquirer';
4
- import mkdirp from 'mkdirp';
5
- import path from 'path';
6
- import util from 'util';
7
- import * as outputLib from '../output.js';
8
- import { fileExists } from '../utils.js';
9
- import { getTestRoot } from './utils.js';
1
+ import colors from 'chalk'
2
+ import fs from 'fs'
3
+ import inquirer from 'inquirer'
4
+ import { mkdirp } from 'mkdirp'
5
+ import path from 'path'
6
+ import util from 'util'
7
+
8
+ import output from '../output.js'
9
+ const { print, success, error } = output
10
+ import { fileExists } from '../utils.js'
11
+ import { getTestRoot } from './utils.js'
10
12
 
11
13
  export default function (initPath) {
12
- const testsPath = getTestRoot(initPath);
14
+ const testsPath = getTestRoot(initPath)
13
15
 
14
- outputLib.print();
15
- outputLib.print(` Welcome to ${colors.magenta.bold('CodeceptJS')} configuration migration tool`);
16
- outputLib.print(` It will help you switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format at ease`);
17
- outputLib.print();
16
+ print()
17
+ print(` Welcome to ${colors.magenta.bold('CodeceptJS')} configuration migration tool`)
18
+ print(` It will help you switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format at ease`)
19
+ print()
18
20
 
19
21
  if (!path) {
20
- outputLib.print('No config file is specified.');
21
- outputLib.print(`Test root is assumed to be ${colors.yellow.bold(testsPath)}`);
22
- outputLib.print('----------------------------------');
22
+ print('No config file is specified.')
23
+ print(`Test root is assumed to be ${colors.yellow.bold(testsPath)}`)
24
+ print('----------------------------------')
23
25
  } else {
24
- outputLib.print(`Migrating ${colors.magenta.bold('.js')} config to ${colors.bold(testsPath)}`);
26
+ print(`Migrating ${colors.magenta.bold('.js')} config to ${colors.bold(testsPath)}`)
25
27
  }
26
28
 
27
29
  if (!fileExists(testsPath)) {
28
- outputLib.print(`Directory ${testsPath} does not exist, creating...`);
29
- mkdirp.sync(testsPath);
30
+ print(`Directory ${testsPath} does not exist, creating...`)
31
+ mkdirp.sync(testsPath)
30
32
  }
31
33
 
32
- const configFile = path.join(testsPath, 'codecept.conf.js');
34
+ const configFile = path.join(testsPath, 'codecept.conf.js')
33
35
  if (fileExists(configFile)) {
34
- outputLib.output.output.error(`Config is already created at ${configFile}`);
35
- return;
36
+ error(`Config is already created at ${configFile}`)
37
+ return
36
38
  }
37
39
 
38
- inquirer.prompt([{
39
- name: 'configFile',
40
- type: 'confirm',
41
- message: `Would you like to switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format?`,
42
- default: true,
43
- },
44
- {
45
- name: 'delete',
46
- type: 'confirm',
47
- message: `Would you like to delete ${colors.cyan.bold('.json')} config format afterwards?`,
48
- default: true,
49
- },
50
- ]).then((result) => {
51
- if (result.configFile) {
52
- const jsonConfigFile = path.join(testsPath, 'codecept.js');
53
- const config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'));
54
- config.name = testsPath.split(path.sep).pop();
40
+ inquirer
41
+ .prompt([
42
+ {
43
+ name: 'configFile',
44
+ type: 'confirm',
45
+ message: `Would you like to switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format?`,
46
+ default: true,
47
+ },
48
+ {
49
+ name: 'delete',
50
+ type: 'confirm',
51
+ message: `Would you like to delete ${colors.cyan.bold('.json')} config format afterwards?`,
52
+ default: true,
53
+ },
54
+ ])
55
+ .then(result => {
56
+ if (result.configFile) {
57
+ const jsonConfigFile = path.join(testsPath, 'codecept.js')
58
+ const config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'))
59
+ config.name = testsPath.split(path.sep).pop()
55
60
 
56
- const finish = () => {
57
- fs.writeFileSync(configFile, `exports.config = ${util.inspect(config, false, 4, false)}`, 'utf-8');
58
- outputLib.output.success(`Config is successfully migrated at ${configFile}`);
61
+ const finish = () => {
62
+ fs.writeFileSync(configFile, `exports.config = ${util.inspect(config, false, 4, false)}`, 'utf-8')
63
+ success(`Config is successfully migrated at ${configFile}`)
59
64
 
60
- if (result.delete) {
61
- if (fileExists(jsonConfigFile)) {
62
- fs.unlinkSync(jsonConfigFile);
63
- outputLib.output.success('JSON config file is deleted!');
65
+ if (result.delete) {
66
+ if (fileExists(jsonConfigFile)) {
67
+ fs.unlinkSync(jsonConfigFile)
68
+ success('JSON config file is deleted!')
69
+ }
64
70
  }
65
71
  }
66
- };
67
- finish();
68
- }
69
- });
72
+ finish()
73
+ }
74
+ })
70
75
  }
@@ -1,12 +1,11 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { getConfig, getTestRoot } from './utils.js';
4
- import Codecept from '../codecept.js';
5
- import container from '../container.js';
6
- import * as output from '../output.js';
7
- import plugin from '../plugin/standardActingHelpers.js';
1
+ import fs from 'fs'
2
+ import path from 'path'
8
3
 
9
- const actingHelpers = [...(plugin), 'REST'];
4
+ import { getConfig, getTestRoot } from './utils.js'
5
+ import Codecept from '../codecept.js'
6
+ import container from '../container.js'
7
+ import output from '../output.js'
8
+ const actingHelpers = [...container.STANDARD_ACTING_HELPERS, 'REST']
10
9
 
11
10
  /**
12
11
  * Prepare data and generate content of definitions file
@@ -22,40 +21,28 @@ const actingHelpers = [...(plugin), 'REST'];
22
21
  *
23
22
  * @returns {string}
24
23
  */
25
- const getDefinitionsFileContent = ({
26
- hasCustomHelper,
27
- hasCustomStepsFile,
28
- helperNames,
29
- supportObject,
30
- importPaths,
31
- translations,
32
- }) => {
33
- const getHelperListFragment = ({
34
- hasCustomHelper,
35
- hasCustomStepsFile,
36
- }) => {
24
+ const getDefinitionsFileContent = ({ hasCustomHelper, hasCustomStepsFile, helperNames, supportObject, importPaths, translations }) => {
25
+ const getHelperListFragment = ({ hasCustomHelper, hasCustomStepsFile }) => {
37
26
  if (hasCustomHelper && hasCustomStepsFile) {
38
- return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}`;
27
+ return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}`
39
28
  }
40
29
 
41
30
  if (hasCustomStepsFile) {
42
- return 'ReturnType<steps_file>';
31
+ return 'ReturnType<steps_file>'
43
32
  }
44
33
 
45
- return 'WithTranslation<Methods>';
46
- };
34
+ return 'WithTranslation<Methods>'
35
+ }
47
36
 
48
37
  const helpersListFragment = getHelperListFragment({
49
38
  hasCustomHelper,
50
39
  hasCustomStepsFile,
51
- });
40
+ })
52
41
 
53
- const importPathsFragment = importPaths.join('\n');
54
- const supportObjectsTypeFragment = convertMapToType(supportObject);
55
- const methodsTypeFragment = helperNames.length > 0
56
- ? `interface Methods extends ${helperNames.join(', ')} {}`
57
- : '';
58
- const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2);
42
+ const importPathsFragment = importPaths.join('\n')
43
+ const supportObjectsTypeFragment = convertMapToType(supportObject)
44
+ const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : ''
45
+ const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2)
59
46
 
60
47
  return generateDefinitionsContent({
61
48
  helpersListFragment,
@@ -63,8 +50,8 @@ const getDefinitionsFileContent = ({
63
50
  supportObjectsTypeFragment,
64
51
  methodsTypeFragment,
65
52
  translatedActionsFragment,
66
- });
67
- };
53
+ })
54
+ }
68
55
 
69
56
  /**
70
57
  * Generate content for definitions file from fragments
@@ -79,13 +66,7 @@ const getDefinitionsFileContent = ({
79
66
  *
80
67
  * @returns {string}
81
68
  */
82
- const generateDefinitionsContent = ({
83
- importPathsFragment,
84
- supportObjectsTypeFragment,
85
- methodsTypeFragment,
86
- helpersListFragment,
87
- translatedActionsFragment,
88
- }) => {
69
+ const generateDefinitionsContent = ({ importPathsFragment, supportObjectsTypeFragment, methodsTypeFragment, helpersListFragment, translatedActionsFragment }) => {
89
70
  return `/// <reference types='codeceptjs' />
90
71
  ${importPathsFragment}
91
72
 
@@ -97,83 +78,84 @@ declare namespace CodeceptJS {
97
78
  interface Actions ${translatedActionsFragment}
98
79
  }
99
80
  }
100
- `;
101
- };
81
+ `
82
+ }
102
83
 
103
84
  /** @type {Array<string>} */
104
- const helperNames = [];
85
+ const helperNames = []
105
86
  /** @type {Array<string>} */
106
- const customHelpers = [];
87
+ const customHelpers = []
107
88
 
108
- export default function (genPath, options) {
109
- const configFile = options.config || genPath;
89
+ export default async function (genPath, options) {
90
+ const configFile = options.config || genPath
110
91
  /** @type {string} */
111
- const testsPath = getTestRoot(configFile);
112
- const config = getConfig(configFile);
113
- if (!config) return;
92
+ const testsPath = getTestRoot(configFile)
93
+ const config = await getConfig(configFile)
94
+ if (!config) return
114
95
 
115
96
  /** @type {Object<string, string>} */
116
- const helperPaths = {};
97
+ const helperPaths = {}
117
98
  /** @type {Object<string, string>} */
118
- const supportPaths = {};
99
+ const supportPaths = {}
119
100
  /** @type {boolean} */
120
- let hasCustomStepsFile = false;
101
+ let hasCustomStepsFile = false
121
102
  /** @type {boolean} */
122
- let hasCustomHelper = false;
103
+ let hasCustomHelper = false
123
104
 
124
105
  /** @type {string} */
125
- const targetFolderPath = options.output && getTestRoot(options.output) || testsPath;
106
+ const targetFolderPath = (options.output && getTestRoot(options.output)) || testsPath
126
107
 
127
- const codecept = new Codecept(config, {});
128
- codecept.init(testsPath);
108
+ const codecept = new Codecept(config, {})
109
+ await codecept.init(testsPath)
110
+ await container.started()
129
111
 
130
- const helpers = container.helpers();
131
- const translations = container.translation();
112
+ const helpers = container.helpers()
113
+ const translations = container.translation()
132
114
  for (const name in helpers) {
133
- const require = codecept.config.helpers[name].require;
115
+ const require = codecept.config.helpers[name].require
134
116
  if (require) {
135
- helperPaths[name] = require;
136
- helperNames.push(name);
117
+ helperPaths[name] = require
118
+ helperNames.push(name)
137
119
  } else {
138
- const fullBasedPromised = codecept.config.fullPromiseBased;
139
- helperNames.push(fullBasedPromised === true ? `${name}Ts` : name);
120
+ const fullBasedPromised = codecept.config.fullPromiseBased
121
+ helperNames.push(fullBasedPromised === true ? `${name}Ts` : name)
140
122
  }
141
123
 
142
124
  if (!actingHelpers.includes(name)) {
143
- customHelpers.push(name);
125
+ customHelpers.push(name)
144
126
  }
145
127
  }
146
128
 
147
- let autoLogin;
129
+ let autoLogin
148
130
  if (config.plugins.autoLogin) {
149
- autoLogin = config.plugins.autoLogin.inject;
131
+ autoLogin = config.plugins.autoLogin.inject
150
132
  }
151
133
 
152
- const supportObject = new Map();
153
- supportObject.set('I', 'I');
154
- supportObject.set('current', 'any');
134
+ const supportObject = new Map()
135
+ supportObject.set('I', 'I')
136
+ supportObject.set('current', 'any')
155
137
 
156
138
  if (translations.loaded) {
157
- supportObject.set(translations.I, translations.I);
139
+ supportObject.set(translations.I, translations.I)
158
140
  }
159
141
 
160
142
  if (autoLogin) {
161
- supportObject.set(autoLogin, 'any');
143
+ supportObject.set(autoLogin, 'any')
162
144
  }
163
145
 
164
146
  if (customHelpers.length > 0) {
165
- hasCustomHelper = true;
147
+ hasCustomHelper = true
166
148
  }
167
149
 
168
150
  for (const name in codecept.config.include) {
169
- const includePath = codecept.config.include[name];
151
+ const includePath = codecept.config.include[name]
170
152
  if (name === 'I' || name === translations.I) {
171
- hasCustomStepsFile = true;
172
- supportPaths.steps_file = includePath;
173
- continue;
153
+ hasCustomStepsFile = true
154
+ supportPaths.steps_file = includePath
155
+ continue
174
156
  }
175
- supportPaths[name] = includePath;
176
- supportObject.set(name, name);
157
+ supportPaths[name] = includePath
158
+ supportObject.set(name, name)
177
159
  }
178
160
 
179
161
  let definitionsFileContent = getDefinitionsFileContent({
@@ -183,30 +165,32 @@ export default function (genPath, options) {
183
165
  translations,
184
166
  hasCustomStepsFile,
185
167
  hasCustomHelper,
186
- });
168
+ })
187
169
 
188
170
  // add aliases for translations
189
171
  if (translations.loaded) {
190
- const namespaceTranslationAliases = [];
191
- namespaceTranslationAliases.push(`interface ${translations.vocabulary.I} extends WithTranslation<Methods> {}`);
172
+ const namespaceTranslationAliases = []
173
+ namespaceTranslationAliases.push(`interface ${translations.vocabulary.I} extends WithTranslation<Methods> {}`)
192
174
 
193
- namespaceTranslationAliases.push(' namespace Translation {');
194
- definitionsFileContent = definitionsFileContent.replace('namespace Translation {', namespaceTranslationAliases.join('\n'));
175
+ namespaceTranslationAliases.push(' namespace Translation {')
176
+ definitionsFileContent = definitionsFileContent.replace('namespace Translation {', namespaceTranslationAliases.join('\n'))
195
177
 
196
- const translationAliases = [];
178
+ const translationAliases = []
197
179
 
198
180
  if (translations.vocabulary.contexts) {
199
181
  Object.keys(translations.vocabulary.contexts).forEach(k => {
200
- translationAliases.push(`declare const ${translations.vocabulary.contexts[k]}: typeof ${k};`);
201
- });
182
+ translationAliases.push(`declare const ${translations.vocabulary.contexts[k]}: typeof ${k};`)
183
+ })
202
184
  }
203
185
 
204
- definitionsFileContent += `\n${translationAliases.join('\n')}`;
186
+ definitionsFileContent += `\n${translationAliases.join('\n')}`
205
187
  }
206
188
 
207
- fs.writeFileSync(path.join(targetFolderPath, 'steps.d.ts'), definitionsFileContent);
208
- output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs');
209
- output.print('Definitions were generated in steps.d.ts');
189
+ if (options.dryRun) return
190
+
191
+ fs.writeFileSync(path.join(targetFolderPath, 'steps.d.ts'), definitionsFileContent)
192
+ output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs')
193
+ output.print('Definitions were generated in steps.d.ts')
210
194
  }
211
195
 
212
196
  /**
@@ -216,23 +200,19 @@ export default function (genPath, options) {
216
200
  * @param {string} testsPath
217
201
  */
218
202
  function getPath(originalPath, targetFolderPath, testsPath) {
219
- const parsedPath = path.parse(originalPath);
203
+ const parsedPath = path.parse(originalPath)
220
204
 
221
205
  // Remove typescript extension if exists.
222
- if (parsedPath.base.endsWith('.d.ts')) parsedPath.base = parsedPath.base.substring(0, parsedPath.base.length - 5);
223
- else if (parsedPath.ext === '.ts') parsedPath.base = parsedPath.name;
206
+ if (parsedPath.base.endsWith('.d.ts')) parsedPath.base = parsedPath.base.substring(0, parsedPath.base.length - 5)
207
+ else if (parsedPath.ext === '.ts') parsedPath.base = parsedPath.name
224
208
 
225
- if (!parsedPath.dir.startsWith('.')) return path.posix.join(parsedPath.dir, parsedPath.base);
209
+ if (!parsedPath.dir.startsWith('.')) return path.posix.join(parsedPath.dir, parsedPath.base)
226
210
  const relativePath = path.posix.relative(
227
211
  targetFolderPath.split(path.sep).join(path.posix.sep),
228
- path.posix.join(
229
- testsPath.split(path.sep).join(path.posix.sep),
230
- parsedPath.dir.split(path.sep).join(path.posix.sep),
231
- parsedPath.base.split(path.sep).join(path.posix.sep),
232
- ),
233
- );
234
-
235
- return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
212
+ path.posix.join(testsPath.split(path.sep).join(path.posix.sep), parsedPath.dir.split(path.sep).join(path.posix.sep), parsedPath.base.split(path.sep).join(path.posix.sep)),
213
+ )
214
+
215
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
236
216
  }
237
217
 
238
218
  /**
@@ -246,19 +226,24 @@ function getPath(originalPath, targetFolderPath, testsPath) {
246
226
  * @returns {Array<string>}
247
227
  */
248
228
  function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue) {
249
- const importStrings = [];
229
+ const importStrings = []
250
230
 
251
231
  for (const name in pathsToType) {
252
- const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath);
253
- importStrings.push(`type ${name} = typeof import('${relativePath}');`);
232
+ const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath)
233
+ // For ESM modules with default exports, we need to access the default export type
234
+ if (relativePath.endsWith('.js')) {
235
+ importStrings.push(`type ${name} = typeof import('${relativePath}')['default'];`)
236
+ } else {
237
+ importStrings.push(`type ${name} = typeof import('${relativePath}');`)
238
+ }
254
239
  }
255
240
 
256
241
  for (const name in pathsToValue) {
257
- const relativePath = getPath(pathsToValue[name], targetFolderPath, testsPath);
258
- importStrings.push(`type ${name} = import('${relativePath}');`);
242
+ const relativePath = getPath(pathsToValue[name], targetFolderPath, testsPath)
243
+ importStrings.push(`type ${name} = import('${relativePath}');`)
259
244
  }
260
245
 
261
- return importStrings;
246
+ return importStrings
262
247
  }
263
248
 
264
249
  /**
@@ -267,5 +252,7 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue)
267
252
  * @returns {string}
268
253
  */
269
254
  function convertMapToType(map) {
270
- return `{ ${Array.from(map).map(([key, value]) => `${key}: ${value}`).join(', ')} }`;
255
+ return `{ ${Array.from(map)
256
+ .map(([key, value]) => `${key}: ${value}`)
257
+ .join(', ')} }`
271
258
  }