codeceptjs 4.0.0-rc.18 → 4.0.0-rc.19

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 (220) hide show
  1. package/bin/codecept.js +5 -1
  2. package/bin/codeceptq.js +49 -0
  3. package/bin/mcp-server.js +250 -82
  4. package/docs/advanced.md +201 -0
  5. package/docs/agents.md +159 -0
  6. package/docs/ai.md +537 -0
  7. package/docs/aitrace.md +266 -0
  8. package/docs/api.md +332 -0
  9. package/docs/assertions.md +415 -0
  10. package/docs/auth.md +318 -0
  11. package/docs/basics.md +424 -0
  12. package/docs/bdd.md +539 -0
  13. package/docs/best.md +240 -0
  14. package/docs/bootstrap.md +132 -0
  15. package/docs/commands.md +352 -0
  16. package/docs/community-helpers.md +63 -0
  17. package/docs/configuration.md +230 -0
  18. package/docs/continuous-integration.md +497 -0
  19. package/docs/custom-helpers.md +297 -0
  20. package/docs/data.md +448 -0
  21. package/docs/debugging.md +332 -0
  22. package/docs/detox.md +235 -0
  23. package/docs/docker.md +136 -0
  24. package/docs/effects.md +179 -0
  25. package/docs/element-based-testing.md +295 -0
  26. package/docs/element-selection.md +125 -0
  27. package/docs/els.md +328 -0
  28. package/docs/examples.md +161 -0
  29. package/docs/heal.md +213 -0
  30. package/docs/helpers/ApiDataFactory.md +267 -0
  31. package/docs/helpers/Appium.md +1405 -0
  32. package/docs/helpers/Detox.md +665 -0
  33. package/docs/helpers/ExpectHelper.md +275 -0
  34. package/docs/helpers/FileSystem.md +152 -0
  35. package/docs/helpers/GraphQL.md +152 -0
  36. package/docs/helpers/GraphQLDataFactory.md +226 -0
  37. package/docs/helpers/JSONResponse.md +255 -0
  38. package/docs/helpers/Mochawesome.md +8 -0
  39. package/docs/helpers/MockRequest.md +377 -0
  40. package/docs/helpers/MockServer.md +212 -0
  41. package/docs/helpers/Playwright.md +2969 -0
  42. package/docs/helpers/Polly.md +44 -0
  43. package/docs/helpers/Protractor.md +1769 -0
  44. package/docs/helpers/Puppeteer-firefox.md +86 -0
  45. package/docs/helpers/Puppeteer.md +2690 -0
  46. package/docs/helpers/REST.md +289 -0
  47. package/docs/helpers/SoftExpectHelper.md +352 -0
  48. package/docs/helpers/WebDriver.md +2682 -0
  49. package/docs/hooks.md +339 -0
  50. package/docs/index.md +111 -0
  51. package/docs/installation.md +83 -0
  52. package/docs/internal-api.md +265 -0
  53. package/docs/internal-test-server.md +89 -0
  54. package/docs/locators.md +355 -0
  55. package/docs/mcp.md +485 -0
  56. package/docs/migration-4.md +556 -0
  57. package/docs/mobile.md +338 -0
  58. package/docs/pageobjects.md +399 -0
  59. package/docs/parallel.md +585 -0
  60. package/docs/playwright.md +714 -0
  61. package/docs/plugins.md +866 -0
  62. package/docs/puppeteer.md +314 -0
  63. package/docs/quickstart.md +120 -0
  64. package/docs/react.md +70 -0
  65. package/docs/reports.md +483 -0
  66. package/docs/retry.md +274 -0
  67. package/docs/secrets.md +150 -0
  68. package/docs/sessions.md +80 -0
  69. package/docs/shadow.md +68 -0
  70. package/docs/test-structure.md +275 -0
  71. package/docs/timeouts.md +183 -0
  72. package/docs/translation.md +247 -0
  73. package/docs/tutorial.md +271 -0
  74. package/docs/typescript.md +374 -0
  75. package/docs/web-element.md +251 -0
  76. package/docs/webdriver.md +708 -0
  77. package/docs/within.md +55 -0
  78. package/lib/command/dryRun.js +9 -3
  79. package/lib/command/init.js +247 -266
  80. package/lib/command/query.js +218 -0
  81. package/lib/config.js +9 -0
  82. package/lib/element/WebElement.js +37 -0
  83. package/lib/globals.js +11 -10
  84. package/lib/helper/Playwright.js +4 -1
  85. package/lib/html.js +3 -0
  86. package/lib/index.js +9 -1
  87. package/lib/locator.js +2 -2
  88. package/lib/mocha/factory.js +5 -1
  89. package/lib/mocha/inject.js +1 -1
  90. package/lib/parser.js +2 -2
  91. package/lib/plugin/browser.js +2 -1
  92. package/lib/plugin/expose.js +159 -0
  93. package/lib/workers.js +1 -15
  94. package/package.json +7 -5
  95. package/docs/webapi/amOnPage.mustache +0 -11
  96. package/docs/webapi/appendField.mustache +0 -16
  97. package/docs/webapi/attachFile.mustache +0 -24
  98. package/docs/webapi/blur.mustache +0 -18
  99. package/docs/webapi/checkOption.mustache +0 -13
  100. package/docs/webapi/clearCookie.mustache +0 -9
  101. package/docs/webapi/clearField.mustache +0 -14
  102. package/docs/webapi/click.mustache +0 -29
  103. package/docs/webapi/clickLink.mustache +0 -8
  104. package/docs/webapi/closeCurrentTab.mustache +0 -7
  105. package/docs/webapi/closeOtherTabs.mustache +0 -8
  106. package/docs/webapi/dontSee.mustache +0 -11
  107. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  108. package/docs/webapi/dontSeeCookie.mustache +0 -8
  109. package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
  110. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  111. package/docs/webapi/dontSeeElement.mustache +0 -12
  112. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  113. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  114. package/docs/webapi/dontSeeInField.mustache +0 -16
  115. package/docs/webapi/dontSeeInSource.mustache +0 -8
  116. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  117. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  118. package/docs/webapi/doubleClick.mustache +0 -13
  119. package/docs/webapi/downloadFile.mustache +0 -12
  120. package/docs/webapi/dragAndDrop.mustache +0 -9
  121. package/docs/webapi/dragSlider.mustache +0 -11
  122. package/docs/webapi/executeAsyncScript.mustache +0 -24
  123. package/docs/webapi/executeScript.mustache +0 -26
  124. package/docs/webapi/fillField.mustache +0 -21
  125. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  126. package/docs/webapi/focus.mustache +0 -13
  127. package/docs/webapi/forceClick.mustache +0 -28
  128. package/docs/webapi/forceRightClick.mustache +0 -18
  129. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  130. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  131. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  132. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  133. package/docs/webapi/grabCookie.mustache +0 -11
  134. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  135. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  136. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  137. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  138. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  139. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  140. package/docs/webapi/grabGeoLocation.mustache +0 -8
  141. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  142. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  143. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  144. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  145. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  146. package/docs/webapi/grabPopupText.mustache +0 -5
  147. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  148. package/docs/webapi/grabSource.mustache +0 -8
  149. package/docs/webapi/grabTextFrom.mustache +0 -10
  150. package/docs/webapi/grabTextFromAll.mustache +0 -9
  151. package/docs/webapi/grabTitle.mustache +0 -8
  152. package/docs/webapi/grabValueFrom.mustache +0 -9
  153. package/docs/webapi/grabValueFromAll.mustache +0 -8
  154. package/docs/webapi/grabWebElement.mustache +0 -9
  155. package/docs/webapi/grabWebElements.mustache +0 -9
  156. package/docs/webapi/moveCursorTo.mustache +0 -16
  157. package/docs/webapi/openNewTab.mustache +0 -7
  158. package/docs/webapi/pressKey.mustache +0 -12
  159. package/docs/webapi/pressKeyDown.mustache +0 -12
  160. package/docs/webapi/pressKeyUp.mustache +0 -12
  161. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  162. package/docs/webapi/refreshPage.mustache +0 -6
  163. package/docs/webapi/resizeWindow.mustache +0 -6
  164. package/docs/webapi/rightClick.mustache +0 -14
  165. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  166. package/docs/webapi/saveScreenshot.mustache +0 -12
  167. package/docs/webapi/say.mustache +0 -10
  168. package/docs/webapi/scrollIntoView.mustache +0 -11
  169. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  170. package/docs/webapi/scrollPageToTop.mustache +0 -6
  171. package/docs/webapi/scrollTo.mustache +0 -12
  172. package/docs/webapi/see.mustache +0 -11
  173. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  174. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  175. package/docs/webapi/seeCookie.mustache +0 -8
  176. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  177. package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
  178. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  179. package/docs/webapi/seeElement.mustache +0 -12
  180. package/docs/webapi/seeElementInDOM.mustache +0 -8
  181. package/docs/webapi/seeFileDownloaded.mustache +0 -23
  182. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  183. package/docs/webapi/seeInField.mustache +0 -17
  184. package/docs/webapi/seeInPopup.mustache +0 -8
  185. package/docs/webapi/seeInSource.mustache +0 -7
  186. package/docs/webapi/seeInTitle.mustache +0 -8
  187. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  188. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  189. package/docs/webapi/seeTextEquals.mustache +0 -9
  190. package/docs/webapi/seeTitleEquals.mustache +0 -8
  191. package/docs/webapi/seeTraffic.mustache +0 -36
  192. package/docs/webapi/selectOption.mustache +0 -26
  193. package/docs/webapi/setCookie.mustache +0 -16
  194. package/docs/webapi/setGeoLocation.mustache +0 -12
  195. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  196. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  197. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  198. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  199. package/docs/webapi/switchTo.mustache +0 -9
  200. package/docs/webapi/switchToNextTab.mustache +0 -10
  201. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  202. package/docs/webapi/type.mustache +0 -21
  203. package/docs/webapi/uncheckOption.mustache +0 -13
  204. package/docs/webapi/wait.mustache +0 -8
  205. package/docs/webapi/waitForClickable.mustache +0 -11
  206. package/docs/webapi/waitForCookie.mustache +0 -9
  207. package/docs/webapi/waitForDetached.mustache +0 -10
  208. package/docs/webapi/waitForDisabled.mustache +0 -6
  209. package/docs/webapi/waitForElement.mustache +0 -11
  210. package/docs/webapi/waitForEnabled.mustache +0 -6
  211. package/docs/webapi/waitForFunction.mustache +0 -17
  212. package/docs/webapi/waitForInvisible.mustache +0 -10
  213. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  214. package/docs/webapi/waitForText.mustache +0 -13
  215. package/docs/webapi/waitForValue.mustache +0 -10
  216. package/docs/webapi/waitForVisible.mustache +0 -10
  217. package/docs/webapi/waitInUrl.mustache +0 -9
  218. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  219. package/docs/webapi/waitToHide.mustache +0 -10
  220. package/docs/webapi/waitUrlEquals.mustache +0 -10
package/docs/within.md ADDED
@@ -0,0 +1,55 @@
1
+ ---
2
+ permalink: /within
3
+ title: Within
4
+ ---
5
+
6
+ # Within
7
+
8
+ `within` scopes all actions inside it to a specific element on the page — useful when working with repeated UI components or narrowing interaction to a specific section.
9
+
10
+ ```js
11
+ within('.js-signup-form', () => {
12
+ I.fillField('user[login]', 'User')
13
+ I.fillField('user[email]', 'user@user.com')
14
+ I.fillField('user[password]', 'user@user.com')
15
+ I.click('button')
16
+ })
17
+ I.see('There were problems creating your account.')
18
+ ```
19
+
20
+ > ⚠ `within` can cause problems when used incorrectly. If you see unexpected behavior, refactor to use the context parameter on individual actions instead (e.g. `I.click('Login', '.nav')`). Keep `within` for the simplest cases.
21
+ > Since `within` returns a Promise, always `await` it when you need its return value.
22
+
23
+ ## IFrames
24
+
25
+ Use a `frame` locator to scope actions inside an iframe:
26
+
27
+ ```js
28
+ within({ frame: '#editor' }, () => {
29
+ I.see('Page')
30
+ I.fillField('Body', 'Hello world')
31
+ })
32
+ ```
33
+
34
+ Nested iframes _(WebDriver & Puppeteer only)_:
35
+
36
+ ```js
37
+ within({ frame: ['.content', '#editor'] }, () => {
38
+ I.see('Page')
39
+ })
40
+ ```
41
+
42
+ > ℹ IFrames can also be accessed via `I.switchTo` command.
43
+
44
+ ## Returning Values
45
+
46
+ `within` can return a value for use in the scenario:
47
+
48
+ ```js
49
+ const val = await within('#sidebar', () => {
50
+ return I.grabTextFrom({ css: 'h1' })
51
+ })
52
+ I.fillField('Description', val)
53
+ ```
54
+
55
+ When running steps inside a `within` block, they will be shown indented in the output.
@@ -10,6 +10,7 @@ import Container from '../container.js'
10
10
  export default async function (test, options) {
11
11
  if (options.grep) process.env.grep = options.grep
12
12
  if (options.ansi === false) chalk.level = 0
13
+ store.dryRun = true
13
14
  const configFile = options.config
14
15
  let codecept
15
16
 
@@ -20,9 +21,15 @@ export default async function (test, options) {
20
21
  }
21
22
 
22
23
  if (config.plugins) {
23
- // disable all plugins by default, they can be enabled with -p option
24
+ // Disable plugins that block (interactive) or perform external I/O (AI/network).
25
+ // Leave the rest enabled so they can register support objects (e.g. auth registers
26
+ // `login`); helper calls inside those support fns are already no-op'd by HelperStep
27
+ // when store.dryRun is true.
28
+ const disableInDryRun = new Set(['pause', 'pauseOnFail', 'analyze', 'aiTrace', 'pageInfo', 'heal'])
24
29
  for (const plugin in config.plugins) {
25
- config.plugins[plugin].enabled = false
30
+ if (disableInDryRun.has(plugin)) {
31
+ config.plugins[plugin].enabled = false
32
+ }
26
33
  }
27
34
  }
28
35
 
@@ -33,7 +40,6 @@ export default async function (test, options) {
33
40
  if (options.bootstrap) await codecept.bootstrap()
34
41
 
35
42
  codecept.loadTests()
36
- store.dryRun = true
37
43
 
38
44
  if (!options.steps && !options.verbose && !options.debug) {
39
45
  await printTests(codecept.testFiles)
@@ -15,7 +15,7 @@ import { test as generateTest } from './generate.js'
15
15
  const isLocal = installedLocally()
16
16
 
17
17
  const defaultConfig = {
18
- tests: './*_test.js',
18
+ tests: './tests/*_test.js',
19
19
  output: '',
20
20
  helpers: {},
21
21
  include: {},
@@ -25,14 +25,6 @@ const defaultConfig = {
25
25
  }
26
26
 
27
27
  const helpers = ['Playwright', 'WebDriver', 'Puppeteer', 'REST', 'GraphQL', 'Appium']
28
- const noTranslation = 'English (no localization)'
29
-
30
- async function getTranslations() {
31
- const translationsModule = await import('../../translations/index.js')
32
- const translations = Object.keys(translationsModule.default || translationsModule)
33
- translations.unshift(noTranslation)
34
- return translations
35
- }
36
28
 
37
29
  const packages = []
38
30
  let isTypeScript = false
@@ -51,6 +43,7 @@ setCommonPlugins();
51
43
  `
52
44
 
53
45
  const defaultActor = `// in this file you can append custom step methods to 'I' object
46
+ import { actor } from 'codeceptjs';
54
47
 
55
48
  export default function() {
56
49
  return actor({
@@ -63,6 +56,7 @@ export default function() {
63
56
  `
64
57
 
65
58
  const defaultActorTs = `// in this file you can append custom step methods to 'I' object
59
+ import { actor } from 'codeceptjs';
66
60
 
67
61
  export = function() {
68
62
  return actor({
@@ -74,9 +68,9 @@ export = function() {
74
68
  }
75
69
  `
76
70
 
77
- export default async function (initPath) {
71
+ export default async function (initPath, options = {}) {
78
72
  const testsPath = getTestRoot(initPath)
79
- const translations = await getTranslations()
73
+ const skipPrompts = !!options.yes
80
74
 
81
75
  print()
82
76
  print(` Welcome to ${colors.magenta.bold('CodeceptJS')} initialization tool`)
@@ -114,251 +108,265 @@ export default async function (initPath) {
114
108
  return
115
109
  }
116
110
 
117
- inquirer
118
- .prompt([
119
- {
120
- name: 'typescript',
121
- type: 'confirm',
122
- default: false,
123
- message: 'Do you plan to write tests in TypeScript?',
124
- },
125
- {
126
- name: 'tests',
127
- type: 'input',
128
- default: answers => `./*_test.${answers.typescript ? 'ts' : 'js'}`,
129
- message: 'Where are your tests located?',
130
- },
131
- {
132
- name: 'helper',
133
- type: 'list',
134
- choices: helpers,
135
- default: 'Playwright',
136
- message: 'What helpers do you want to use?',
137
- },
138
- {
139
- name: 'jsonResponse',
140
- type: 'confirm',
141
- default: true,
142
- message: 'Do you want to use JSONResponse helper for assertions on JSON responses? http://bit.ly/3ASVPy9',
143
- when: answers => ['GraphQL', 'REST'].includes(answers.helper) === true,
144
- },
145
- {
146
- name: 'output',
147
- default: './output',
148
- message: 'Where should logs, screenshots, and reports to be stored?',
149
- },
150
- {
151
- name: 'translation',
152
- type: 'list',
153
- message: 'Do you want to enable localization for tests? http://bit.ly/3GNUBbh',
154
- choices: translations,
155
- },
156
- ])
157
- .then(async result => {
158
- if (result.typescript === true) {
159
- isTypeScript = true
160
- extension = isTypeScript === true ? 'ts' : 'js'
161
- packages.push('typescript')
162
- packages.push('tsx') // Add tsx for TypeScript support
163
- packages.push('@types/node')
111
+ const result = skipPrompts
112
+ ? {
113
+ typescript: false,
114
+ tests: './tests/*_test.js',
115
+ helper: 'Playwright',
116
+ jsonResponse: false,
117
+ output: './output',
164
118
  }
119
+ : await inquirer.prompt([
120
+ {
121
+ name: 'typescript',
122
+ type: 'confirm',
123
+ default: false,
124
+ message: 'Do you plan to write tests in TypeScript?',
125
+ },
126
+ {
127
+ name: 'tests',
128
+ type: 'input',
129
+ default: answers => `./tests/*_test.${answers.typescript ? 'ts' : 'js'}`,
130
+ message: 'Where are your tests located?',
131
+ },
132
+ {
133
+ name: 'helper',
134
+ type: 'list',
135
+ choices: helpers,
136
+ default: 'Playwright',
137
+ message: 'What helpers do you want to use?',
138
+ },
139
+ {
140
+ name: 'jsonResponse',
141
+ type: 'confirm',
142
+ default: true,
143
+ message: 'Do you want to use JSONResponse helper for assertions on JSON responses? http://bit.ly/3ASVPy9',
144
+ when: answers => ['GraphQL', 'REST'].includes(answers.helper) === true,
145
+ },
146
+ {
147
+ name: 'output',
148
+ default: './output',
149
+ message: 'Where should logs, screenshots, and reports to be stored?',
150
+ },
151
+ ])
152
+
153
+ if (result.typescript === true) {
154
+ isTypeScript = true
155
+ extension = isTypeScript === true ? 'ts' : 'js'
156
+ packages.push('typescript')
157
+ packages.push('tsx')
158
+ packages.push('@types/node')
159
+ }
165
160
 
166
- const config = defaultConfig
167
- config.name = testsPath.split(path.sep).pop()
168
- config.output = result.output
161
+ const config = defaultConfig
162
+ config.name = testsPath.split(path.sep).pop()
163
+ config.output = result.output
169
164
 
170
- config.tests = result.tests
171
- if (isTypeScript) {
172
- config.tests = `${config.tests.replace(/\.js$/, `.${extension}`)}`
173
- config.require = ['tsx/cjs'] // Add tsx/cjs loader for TypeScript tests
174
- }
165
+ config.tests = result.tests
166
+ if (isTypeScript) {
167
+ config.tests = `${config.tests.replace(/\.js$/, `.${extension}`)}`
168
+ config.require = ['tsx/cjs']
169
+ }
170
+
171
+ const matchResults = config.tests.match(/[^*.]+/)
172
+ if (matchResults) {
173
+ mkdirp.sync(path.join(testsPath, matchResults[0]))
174
+ }
175
+
176
+ const helperName = result.helper
177
+ config.helpers[helperName] = {}
175
178
 
176
- // create a directory tests if it is included in tests path
177
- const matchResults = config.tests.match(/[^*.]+/)
178
- if (matchResults) {
179
- mkdirp.sync(path.join(testsPath, matchResults[0]))
179
+ if (result.jsonResponse === true) {
180
+ config.helpers.JSONResponse = {}
181
+ }
182
+
183
+ let helperConfigs = []
184
+
185
+ try {
186
+ const HelperModule = await import(`../helper/${helperName}.js`)
187
+ const Helper = HelperModule.default || HelperModule
188
+ if (Helper._checkRequirements) {
189
+ packages.concat(Helper._checkRequirements())
190
+ }
191
+
192
+ if (!Helper._config()) return
193
+ helperConfigs = helperConfigs.concat(
194
+ Helper._config().map(config => {
195
+ config.message = `[${helperName}] ${config.message}`
196
+ config.name = `${helperName}_${config.name}`
197
+ config.type = config.type || 'input'
198
+ return config
199
+ }),
200
+ )
201
+ } catch (err) {
202
+ error(err)
203
+ }
204
+
205
+ const finish = async () => {
206
+ const stepFile = `./steps_file.${extension}`
207
+ fs.writeFileSync(path.join(testsPath, stepFile), extension === 'ts' ? defaultActorTs : defaultActor)
208
+
209
+ config.include = { I: isTypeScript ? './steps_file' : stepFile }
210
+
211
+ print(`Steps file created at ${stepFile}`)
212
+
213
+ let configSource
214
+ const hasConfigure = isLocal && !initPath
215
+
216
+ if (isTypeScript) {
217
+ configSource = beautify(`export const config : CodeceptJS.MainConfig = ${inspect(config, false, 4, false)}`)
218
+
219
+ if (hasConfigure) configSource = importCodeceptConfigure + configHeader + configSource
220
+
221
+ fs.writeFileSync(typeScriptconfigFile, configSource, 'utf-8')
222
+ print(`Config created at ${typeScriptconfigFile}`)
223
+ } else {
224
+ configSource = beautify(`/** @type {CodeceptJS.MainConfig} */\nexport const config = ${inspect(config, false, 4, false)}`)
225
+
226
+ if (hasConfigure) configSource = importCodeceptConfigure + configHeader + configSource
227
+
228
+ fs.writeFileSync(configFile, configSource, 'utf-8')
229
+ print(`Config created at ${configFile}`)
230
+ }
231
+
232
+ if (config.output) {
233
+ if (!fileExists(config.output)) {
234
+ mkdirp.sync(path.join(testsPath, config.output))
235
+ print(`Directory for temporary output files created at '${config.output}'`)
236
+ } else {
237
+ print(`Directory for temporary output files is already created at '${config.output}'`)
180
238
  }
239
+ }
181
240
 
182
- if (result.translation !== noTranslation) config.translation = result.translation
241
+ const jsconfig = {
242
+ compilerOptions: {
243
+ allowJs: true,
244
+ },
245
+ }
183
246
 
184
- const helperName = result.helper
185
- config.helpers[helperName] = {}
247
+ const tsconfig = {
248
+ 'ts-node': {
249
+ files: true,
250
+ },
251
+ compilerOptions: {
252
+ target: 'es2018',
253
+ lib: ['es2018', 'DOM'],
254
+ esModuleInterop: true,
255
+ module: 'commonjs',
256
+ strictNullChecks: false,
257
+ types: ['codeceptjs', 'node'],
258
+ declaration: true,
259
+ skipLibCheck: true,
260
+ },
261
+ exclude: ['node_modules'],
262
+ }
186
263
 
187
- if (result.jsonResponse === true) {
188
- config.helpers.JSONResponse = {}
264
+ if (isTypeScript) {
265
+ const tsconfigJson = beautify(JSON.stringify(tsconfig))
266
+ const tsconfigFile = path.join(testsPath, 'tsconfig.json')
267
+ if (fileExists(tsconfigFile)) {
268
+ print(`tsconfig.json already exists at ${tsconfigFile}`)
269
+ } else {
270
+ fs.writeFileSync(tsconfigFile, tsconfigJson)
271
+ }
272
+ } else {
273
+ const jsconfigJson = beautify(JSON.stringify(jsconfig))
274
+ const jsconfigFile = path.join(testsPath, 'jsconfig.json')
275
+ if (fileExists(jsconfigFile)) {
276
+ print(`jsconfig.json already exists at ${jsconfigFile}`)
277
+ } else {
278
+ fs.writeFileSync(jsconfigFile, jsconfigJson)
279
+ print(`Intellisense enabled in ${jsconfigFile}`)
189
280
  }
281
+ }
190
282
 
191
- let helperConfigs = []
283
+ const generateDefinitionsManually = colors.bold(`To get auto-completion support, please generate type definitions: ${colors.green('npx codeceptjs def')}`)
192
284
 
285
+ if (packages) {
193
286
  try {
194
- const HelperModule = await import(`../helper/${helperName}.js`)
195
- const Helper = HelperModule.default || HelperModule
196
- if (Helper._checkRequirements) {
197
- packages.concat(Helper._checkRequirements())
198
- }
199
-
200
- if (!Helper._config()) return
201
- helperConfigs = helperConfigs.concat(
202
- Helper._config().map(config => {
203
- config.message = `[${helperName}] ${config.message}`
204
- config.name = `${helperName}_${config.name}`
205
- config.type = config.type || 'input'
206
- return config
207
- }),
208
- )
287
+ install(packages)
209
288
  } catch (err) {
210
- error(err)
289
+ print(colors.bold.red(err.toString()))
290
+ print()
291
+ print(colors.bold.red('Please install next packages manually:'))
292
+ print(`npm i ${packages.join(' ')} --save-dev`)
293
+ print()
294
+ print('Things to do after missing packages installed:')
295
+ print('☑', generateDefinitionsManually)
296
+ print('☑ Create first test:', colors.green('npx codeceptjs gt'))
297
+ print(colors.bold.magenta('Find more information at https://codecept.io'))
298
+ return
211
299
  }
300
+ }
212
301
 
213
- const finish = async () => {
214
- // create steps file by default
215
- // no extra step file for typescript (as it doesn't match TS conventions)
216
- const stepFile = `./steps_file.${extension}`
217
- fs.writeFileSync(path.join(testsPath, stepFile), extension === 'ts' ? defaultActorTs : defaultActor)
218
-
219
- if (isTypeScript) {
220
- config.include = await _actorTranslation('./steps_file', config.translation, translations)
221
- } else {
222
- config.include = await _actorTranslation(stepFile, config.translation, translations)
223
- }
224
-
225
- print(`Steps file created at ${stepFile}`)
226
-
227
- let configSource
228
- const hasConfigure = isLocal && !initPath
229
-
230
- if (isTypeScript) {
231
- configSource = beautify(`export const config : CodeceptJS.MainConfig = ${inspect(config, false, 4, false)}`)
232
-
233
- if (hasConfigure) configSource = importCodeceptConfigure + configHeader + configSource
234
-
235
- fs.writeFileSync(typeScriptconfigFile, configSource, 'utf-8')
236
- print(`Config created at ${typeScriptconfigFile}`)
237
- } else {
238
- configSource = beautify(`/** @type {CodeceptJS.MainConfig} */\nexport const config = ${inspect(config, false, 4, false)}`)
239
-
240
- if (hasConfigure) configSource = importCodeceptConfigure + configHeader + configSource
241
-
242
- fs.writeFileSync(configFile, configSource, 'utf-8')
243
- print(`Config created at ${configFile}`)
244
- }
245
-
246
- if (config.output) {
247
- if (!fileExists(config.output)) {
248
- mkdirp.sync(path.join(testsPath, config.output))
249
- print(`Directory for temporary output files created at '${config.output}'`)
250
- } else {
251
- print(`Directory for temporary output files is already created at '${config.output}'`)
252
- }
253
- }
254
-
255
- const jsconfig = {
256
- compilerOptions: {
257
- allowJs: true,
258
- },
259
- }
260
-
261
- const tsconfig = {
262
- 'ts-node': {
263
- files: true,
264
- },
265
- compilerOptions: {
266
- target: 'es2018',
267
- lib: ['es2018', 'DOM'],
268
- esModuleInterop: true,
269
- module: 'commonjs',
270
- strictNullChecks: false,
271
- types: ['codeceptjs', 'node'],
272
- declaration: true,
273
- skipLibCheck: true,
274
- },
275
- exclude: ['node_modules'],
276
- }
277
-
278
- if (isTypeScript) {
279
- const tsconfigJson = beautify(JSON.stringify(tsconfig))
280
- const tsconfigFile = path.join(testsPath, 'tsconfig.json')
281
- if (fileExists(tsconfigFile)) {
282
- print(`tsconfig.json already exists at ${tsconfigFile}`)
283
- } else {
284
- fs.writeFileSync(tsconfigFile, tsconfigJson)
285
- }
286
- } else {
287
- const jsconfigJson = beautify(JSON.stringify(jsconfig))
288
- const jsconfigFile = path.join(testsPath, 'jsconfig.json')
289
- if (fileExists(jsconfigFile)) {
290
- print(`jsconfig.json already exists at ${jsconfigFile}`)
291
- } else {
292
- fs.writeFileSync(jsconfigFile, jsconfigJson)
293
- print(`Intellisense enabled in ${jsconfigFile}`)
294
- }
295
- }
296
-
297
- const generateDefinitionsManually = colors.bold(`To get auto-completion support, please generate type definitions: ${colors.green('npx codeceptjs def')}`)
298
-
299
- if (packages) {
300
- try {
301
- install(packages)
302
- } catch (err) {
303
- print(colors.bold.red(err.toString()))
304
- print()
305
- print(colors.bold.red('Please install next packages manually:'))
306
- print(`npm i ${packages.join(' ')} --save-dev`)
307
- print()
308
- print('Things to do after missing packages installed:')
309
- print('☑', generateDefinitionsManually)
310
- print('☑ Create first test:', colors.green('npx codeceptjs gt'))
311
- print(colors.bold.magenta('Find more information at https://codecept.io'))
312
- return
313
- }
314
- }
315
-
316
- try {
317
- generateDefinitions(testsPath, {})
318
- } catch (err) {
319
- print(colors.bold.red("Couldn't generate type definitions"))
320
- print(colors.red(err.toString()))
321
- print('Skipping type definitions...')
322
- print(generateDefinitionsManually)
323
- }
324
-
325
- print('')
326
- success(' Almost ready... Next step:')
327
-
328
- const generatedTest = generateTest(testsPath)
329
- if (!generatedTest) return
330
- generatedTest.then(() => {
331
- print('\n--')
332
- print(colors.bold.green('CodeceptJS Installed! Enjoy supercharged testing! 🤩'))
333
- print(colors.bold.magenta('Find more information at https://codecept.io'))
334
- print()
335
- })
336
- }
302
+ try {
303
+ generateDefinitions(testsPath, {})
304
+ } catch (err) {
305
+ print(colors.bold.red("Couldn't generate type definitions"))
306
+ print(colors.red(err.toString()))
307
+ print('Skipping type definitions...')
308
+ print(generateDefinitionsManually)
309
+ }
310
+
311
+ print('')
312
+ success(' Almost ready... Next step:')
313
+
314
+ if (skipPrompts) {
315
+ print('\n--')
316
+ print(colors.bold.green('CodeceptJS Installed! Enjoy supercharged testing! 🤩'))
317
+ print(colors.bold.magenta('Find more information at https://codecept.io'))
318
+ print()
319
+ return
320
+ }
337
321
 
338
- print('Configure helpers...')
339
- inquirer.prompt(helperConfigs).then(async helperResult => {
340
- if (helperResult.Playwright_browser === 'electron') {
341
- delete helperResult.Playwright_url
342
- delete helperResult.Playwright_show
343
-
344
- helperResult.Playwright_electron = {
345
- executablePath: '// require("electron") or require("electron-forge")',
346
- args: ['path/to/your/main.js'],
347
- }
348
- }
349
-
350
- Object.keys(helperResult).forEach(key => {
351
- const parts = key.split('_')
352
- const helperName = parts[0]
353
- const configName = parts[1]
354
- if (!configName) return
355
- config.helpers[helperName][configName] = helperResult[key]
356
- })
357
-
358
- print('')
359
- await finish()
360
- })
322
+ const generatedTest = generateTest(testsPath)
323
+ if (!generatedTest) return
324
+ generatedTest.then(() => {
325
+ print('\n--')
326
+ print(colors.bold.green('CodeceptJS Installed! Enjoy supercharged testing! 🤩'))
327
+ print(colors.bold.magenta('Find more information at https://codecept.io'))
328
+ print()
361
329
  })
330
+ }
331
+
332
+ const helperResult = skipPrompts
333
+ ? defaultHelperAnswers(helperName)
334
+ : await (async () => {
335
+ print('Configure helpers...')
336
+ return inquirer.prompt(helperConfigs)
337
+ })()
338
+
339
+ if (helperResult.Playwright_browser === 'electron') {
340
+ delete helperResult.Playwright_url
341
+ delete helperResult.Playwright_show
342
+
343
+ helperResult.Playwright_electron = {
344
+ executablePath: '// require("electron") or require("electron-forge")',
345
+ args: ['path/to/your/main.js'],
346
+ }
347
+ }
348
+
349
+ Object.keys(helperResult).forEach(key => {
350
+ const parts = key.split('_')
351
+ const hName = parts[0]
352
+ const configName = parts[1]
353
+ if (!configName) return
354
+ config.helpers[hName][configName] = helperResult[key]
355
+ })
356
+
357
+ print('')
358
+ await finish()
359
+ }
360
+
361
+ function defaultHelperAnswers(helperName) {
362
+ if (helperName === 'Playwright') {
363
+ return {
364
+ Playwright_browser: 'chromium',
365
+ Playwright_url: process.env.BASE_URL || 'http://localhost',
366
+ Playwright_show: false,
367
+ }
368
+ }
369
+ return {}
362
370
  }
363
371
 
364
372
  function install(dependencies) {
@@ -399,30 +407,3 @@ function install(dependencies) {
399
407
  }
400
408
  return true
401
409
  }
402
-
403
- async function _actorTranslation(stepFile, translationSelected, translations) {
404
- let actor
405
-
406
- for (const translationAvailable of translations) {
407
- if (actor) {
408
- break
409
- }
410
-
411
- if (translationSelected === translationAvailable) {
412
- const translationsModule = await import('../../translations/index.js')
413
- const nameOfActor = (translationsModule.default || translationsModule)[translationAvailable].I
414
-
415
- actor = {
416
- [nameOfActor]: stepFile,
417
- }
418
- }
419
- }
420
-
421
- if (!actor) {
422
- actor = {
423
- I: stepFile,
424
- }
425
- }
426
-
427
- return actor
428
- }