codeceptjs 4.0.0-beta.1 → 4.0.0-beta.11.esm-aria

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 (207) 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 +238 -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 +300 -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 +124 -50
  39. package/lib/container.js +765 -260
  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/mask_data.js +47 -0
  157. package/lib/utils.js +411 -228
  158. package/lib/workerStorage.js +37 -34
  159. package/lib/workers.js +532 -296
  160. package/package.json +115 -95
  161. package/translations/de-DE.js +5 -3
  162. package/translations/fr-FR.js +5 -4
  163. package/translations/index.js +22 -12
  164. package/translations/it-IT.js +4 -3
  165. package/translations/ja-JP.js +4 -3
  166. package/translations/nl-NL.js +76 -0
  167. package/translations/pl-PL.js +4 -3
  168. package/translations/pt-BR.js +4 -3
  169. package/translations/ru-RU.js +4 -3
  170. package/translations/utils.js +10 -0
  171. package/translations/zh-CN.js +4 -3
  172. package/translations/zh-TW.js +4 -3
  173. package/typings/index.d.ts +546 -185
  174. package/typings/promiseBasedTypes.d.ts +150 -879
  175. package/typings/types.d.ts +547 -996
  176. package/lib/cli.js +0 -249
  177. package/lib/dirname.js +0 -5
  178. package/lib/helper/Expect.js +0 -425
  179. package/lib/helper/ExpectHelper.js +0 -399
  180. package/lib/helper/MockServer.js +0 -223
  181. package/lib/helper/Nightmare.js +0 -1411
  182. package/lib/helper/Protractor.js +0 -1835
  183. package/lib/helper/SoftExpectHelper.js +0 -381
  184. package/lib/helper/TestCafe.js +0 -1410
  185. package/lib/helper/clientscripts/nightmare.js +0 -213
  186. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  187. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  188. package/lib/interfaces/bdd.js +0 -98
  189. package/lib/interfaces/featureConfig.js +0 -69
  190. package/lib/interfaces/gherkin.js +0 -195
  191. package/lib/listener/artifacts.js +0 -19
  192. package/lib/listener/retry.js +0 -68
  193. package/lib/listener/timeout.js +0 -109
  194. package/lib/mochaFactory.js +0 -110
  195. package/lib/plugin/allure.js +0 -15
  196. package/lib/plugin/commentStep.js +0 -136
  197. package/lib/plugin/debugErrors.js +0 -67
  198. package/lib/plugin/eachElement.js +0 -127
  199. package/lib/plugin/fakerTransform.js +0 -49
  200. package/lib/plugin/retryTo.js +0 -121
  201. package/lib/plugin/selenoid.js +0 -371
  202. package/lib/plugin/standardActingHelpers.js +0 -9
  203. package/lib/plugin/tryTo.js +0 -105
  204. package/lib/plugin/wdio.js +0 -246
  205. package/lib/scenario.js +0 -222
  206. package/lib/ui.js +0 -238
  207. package/lib/within.js +0 -70
@@ -1,89 +1,87 @@
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 {
7
- fileExists, ucfirst, lcfirst, beautify,
8
- } from '../utils.js';
9
- import { output } from '../output.js';
10
- import generateDefinitions from './definitions.js';
11
- import {
12
- getConfig, getTestRoot, safeFileWrite, readConfig,
13
- } from './utils.js';
14
- import container from '../container.js';
15
- import { __dirname } from '../dirname.js';
16
-
17
- let extension = '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 { fileExists, ucfirst, lcfirst, beautify } from '../utils.js'
7
+ import output from '../output.js'
8
+ import generateDefinitions from './definitions.js'
9
+ import { getConfig, getTestRoot, safeFileWrite, readConfig } from './utils.js'
10
+
11
+ let extension = 'js'
18
12
 
19
13
  const testTemplate = `Feature('{{feature}}');
20
14
 
21
15
  Scenario('test something', async ({ {{actor}} }) => {
22
16
 
23
17
  });
24
- `;
18
+ `
25
19
 
26
20
  // generates empty test
27
- export const test = function (genPath) {
28
- const testsPath = getTestRoot(genPath);
29
- global.codecept_dir = testsPath;
30
- const config = getConfig(testsPath);
31
- if (!config) return;
32
-
33
- output.print('Creating a new test...');
34
- output.print('----------------------');
35
-
36
- const defaultExt = config.tests.match(/([^\*/]*?)$/)[1] || `_test.${extension}`;
37
-
38
- return inquirer.prompt([
39
- {
40
- type: 'input',
41
- name: 'feature',
42
- message: 'Feature which is being tested (ex: account, login, etc)',
43
- validate: (val) => !!val,
44
- },
45
- {
46
- type: 'input',
47
- message: 'Filename of a test',
48
- name: 'filename',
49
- default(answers) {
50
- return (answers.feature).replace(' ', '_') + defaultExt;
21
+ export async function test(genPath) {
22
+ const testsPath = getTestRoot(genPath)
23
+ global.codecept_dir = testsPath
24
+ const config = await getConfig(testsPath)
25
+ if (!config) return
26
+
27
+ output.print('Creating a new test...')
28
+ output.print('----------------------')
29
+
30
+ const defaultExt = config.tests.match(/([^\*/]*?)$/)[1] || `_test.${extension}`
31
+
32
+ return inquirer
33
+ .prompt([
34
+ {
35
+ type: 'input',
36
+ name: 'feature',
37
+ message: 'Feature which is being tested (ex: account, login, etc)',
38
+ validate: val => !!val,
51
39
  },
52
- },
53
- ]).then((result) => {
54
- const testFilePath = path.dirname(path.join(testsPath, config.tests)).replace(/\*\*$/, '');
55
- let testFile = path.join(testFilePath, result.filename);
56
- const ext = path.extname(testFile);
57
- if (!ext) testFile += defaultExt;
58
- const dir = path.dirname(testFile);
59
- if (!fileExists(dir)) mkdirp.sync(dir);
60
- let testContent = testTemplate.replace('{{feature}}', result.feature);
61
-
62
- container.create(config, {});
63
- // translate scenario test
64
- if (container.translation().loaded) {
65
- const vocabulary = container.translation().vocabulary;
66
- testContent = testContent.replace('{{actor}}', container.translation().I);
67
- if (vocabulary.contexts.Feature) testContent = testContent.replace('Feature', vocabulary.contexts.Feature);
68
- if (vocabulary.contexts.Scenario) testContent = testContent.replace('Scenario', vocabulary.contexts.Scenario);
69
- output.print(`Test was created in ${colors.bold(config.translation)} localization. See: https://codecept.io/translation/`);
70
- } else {
71
- testContent = testContent.replace('{{actor}}', 'I');
72
- }
73
- if (!config.fullPromiseBased) testContent = testContent.replace('async', '');
40
+ {
41
+ type: 'input',
42
+ message: 'Filename of a test',
43
+ name: 'filename',
44
+ default(answers) {
45
+ return answers.feature.replace(' ', '_') + defaultExt
46
+ },
47
+ },
48
+ ])
49
+ .then(async result => {
50
+ const testFilePath = path.dirname(path.join(testsPath, config.tests)).replace(/\*\*$/, '')
51
+ let testFile = path.join(testFilePath, result.filename)
52
+ const ext = path.extname(testFile)
53
+ if (!ext) testFile += defaultExt
54
+ const dir = path.dirname(testFile)
55
+ if (!fileExists(dir)) mkdirp.sync(dir)
56
+ let testContent = testTemplate.replace('{{feature}}', result.feature)
57
+
58
+ const containerModule = await import('../container.js')
59
+ const container = containerModule.default || containerModule
60
+ await container.create(config, {})
61
+ // translate scenario test
62
+ if (container.translation().loaded) {
63
+ const vocabulary = container.translation().vocabulary
64
+ testContent = testContent.replace('{{actor}}', container.translation().I)
65
+ if (vocabulary.contexts.Feature) testContent = testContent.replace('Feature', vocabulary.contexts.Feature)
66
+ if (vocabulary.contexts.Scenario) testContent = testContent.replace('Scenario', vocabulary.contexts.Scenario)
67
+ output.print(`Test was created in ${colors.bold(config.translation)} localization. See: https://codecept.io/translation/`)
68
+ } else {
69
+ testContent = testContent.replace('{{actor}}', 'I')
70
+ }
71
+ if (!config.fullPromiseBased) testContent = testContent.replace('async', '')
74
72
 
75
- if (!safeFileWrite(testFile, testContent)) return;
76
- output.success(`\nTest for ${result.filename} was created in ${testFile}`);
77
- });
78
- };
73
+ if (!safeFileWrite(testFile, testContent)) return
74
+ output.success(`\nTest for ${result.filename} was created in ${testFile}`)
75
+ })
76
+ }
79
77
 
80
78
  const pageObjectTemplate = `const { I } = inject();
81
79
 
82
- module.exports = {
80
+ export default {
83
81
 
84
82
  // insert your locators and methods here
85
83
  }
86
- `;
84
+ `
87
85
 
88
86
  const poModuleTemplateTS = `const { I } = inject();
89
87
 
@@ -91,7 +89,7 @@ export = {
91
89
 
92
90
  // insert your locators and methods here
93
91
  }
94
- `;
92
+ `
95
93
 
96
94
  const poClassTemplate = `const { I } = inject();
97
95
 
@@ -104,100 +102,103 @@ class {{name}} {
104
102
  }
105
103
 
106
104
  // For inheritance
107
- module.exports = new {{name}}();
108
- export = {{name}};
109
- `;
105
+ export default new {{name}}();
106
+ export { {{name}} };
107
+ `
110
108
 
111
- export const pageObject = function (genPath, opts) {
112
- const testsPath = getTestRoot(genPath);
113
- const config = getConfig(testsPath);
114
- const kind = opts.T || 'page';
115
- if (!config) return;
109
+ export async function pageObject(genPath, opts) {
110
+ const testsPath = getTestRoot(genPath)
111
+ const config = await getConfig(testsPath)
112
+ const kind = opts.T || 'page'
113
+ if (!config) return
116
114
 
117
- let configFile = path.join(testsPath, `codecept.conf.${extension}`);
115
+ let configFile = path.join(testsPath, `codecept.conf.${extension}`)
118
116
 
119
117
  if (!fileExists(configFile)) {
120
- extension = 'ts';
121
- configFile = path.join(testsPath, `codecept.conf.${extension}`);
118
+ extension = 'ts'
119
+ configFile = path.join(testsPath, `codecept.conf.${extension}`)
122
120
  }
123
- output.print(`Creating a new ${kind} object`);
124
- output.print('--------------------------');
125
-
126
- return inquirer.prompt([
127
- {
128
- type: 'input',
129
- name: 'name',
130
- message: `Name of a ${kind} object`,
131
- validate: (val) => !!val,
132
- },
133
- {
134
- type: 'input',
135
- name: 'filename',
136
- message: 'Where should it be stored',
137
- default: answers => `./${kind}s/${answers.name}.${extension}`,
138
- },
139
- {
140
- type: 'list',
141
- name: 'objectType',
142
- message: 'What is your preferred object type',
143
- choices: ['module', 'class'],
144
- default: 'module',
145
- },
146
- ]).then((result) => {
147
- const pageObjectFile = path.join(testsPath, result.filename);
148
- const dir = path.dirname(pageObjectFile);
149
- if (!fileExists(dir)) fs.mkdirSync(dir);
150
-
151
- let actor = 'actor';
152
-
153
- if (config.include.I) {
154
- let actorPath = config.include.I;
155
- if (actorPath.charAt(0) === '.') { // relative path
156
- actorPath = path.relative(dir, path.dirname(path.join(testsPath, actorPath))) + actorPath.substring(1); // get an upper level
121
+ output.print(`Creating a new ${kind} object`)
122
+ output.print('--------------------------')
123
+
124
+ return inquirer
125
+ .prompt([
126
+ {
127
+ type: 'input',
128
+ name: 'name',
129
+ message: `Name of a ${kind} object`,
130
+ validate: val => !!val,
131
+ },
132
+ {
133
+ type: 'input',
134
+ name: 'filename',
135
+ message: 'Where should it be stored',
136
+ default: answers => `./${kind}s/${answers.name}.${extension}`,
137
+ },
138
+ {
139
+ type: 'list',
140
+ name: 'objectType',
141
+ message: 'What is your preferred object type',
142
+ choices: ['module', 'class'],
143
+ default: 'module',
144
+ },
145
+ ])
146
+ .then(result => {
147
+ const pageObjectFile = path.join(testsPath, result.filename)
148
+ const dir = path.dirname(pageObjectFile)
149
+ if (!fileExists(dir)) fs.mkdirSync(dir)
150
+
151
+ let actor = 'actor'
152
+
153
+ if (config.include.I) {
154
+ let actorPath = config.include.I
155
+ if (actorPath.charAt(0) === '.') {
156
+ // relative path
157
+ actorPath = path.relative(dir, path.dirname(path.join(testsPath, actorPath))) + actorPath.substring(1) // get an upper level
158
+ }
159
+ actor = `import('${actorPath}')`
157
160
  }
158
- actor = `require('${actorPath}')`;
159
- }
160
161
 
161
- const name = lcfirst(result.name) + ucfirst(kind);
162
- if (result.objectType === 'module' && extension === 'ts') {
163
- if (!safeFileWrite(pageObjectFile, poModuleTemplateTS.replace('{{actor}}', actor))) return;
164
- } else if (result.objectType === 'module' && extension === 'js') {
165
- if (!safeFileWrite(pageObjectFile, pageObjectTemplate.replace('{{actor}}', actor))) return;
166
- } else if (result.objectType === 'class') {
167
- const content = poClassTemplate.replace(/{{actor}}/g, actor).replace(/{{name}}/g, name);
168
- if (!safeFileWrite(pageObjectFile, content)) return;
169
- }
162
+ const name = lcfirst(result.name) + ucfirst(kind)
163
+ if (result.objectType === 'module' && extension === 'ts') {
164
+ if (!safeFileWrite(pageObjectFile, poModuleTemplateTS.replace('{{actor}}', actor))) return
165
+ } else if (result.objectType === 'module' && extension === 'js') {
166
+ if (!safeFileWrite(pageObjectFile, pageObjectTemplate.replace('{{actor}}', actor))) return
167
+ } else if (result.objectType === 'class') {
168
+ const content = poClassTemplate.replace(/{{actor}}/g, actor).replace(/{{name}}/g, name)
169
+ if (!safeFileWrite(pageObjectFile, content)) return
170
+ }
170
171
 
171
- let data = readConfig(configFile);
172
- config.include[name] = result.filename;
172
+ let data = readConfig(configFile)
173
+ config.include[name] = result.filename
173
174
 
174
- if (!data) throw Error('Config file is empty');
175
- const currentInclude = `${data.match(/include:[\s\S][^\}]*/i)[0]}\n ${name}:${JSON.stringify(config.include[name])}`;
175
+ if (!data) throw Error('Config file is empty')
176
+ const currentInclude = `${data.match(/include:[\s\S][^\}]*/i)[0]}\n ${name}:${JSON.stringify(config.include[name])}`
176
177
 
177
- data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude},`);
178
+ data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude},`)
178
179
 
179
- fs.writeFileSync(configFile, beautify(data), 'utf-8');
180
+ fs.writeFileSync(configFile, beautify(data), 'utf-8')
180
181
 
181
- output.success(`${ucfirst(kind)} object for ${result.name} was created in ${pageObjectFile}`);
182
- output.print(`Your config file (${colors.cyan('include')} section) has included the new created PO:
182
+ output.success(`${ucfirst(kind)} object for ${result.name} was created in ${pageObjectFile}`)
183
+ output.print(`Your config file (${colors.cyan('include')} section) has included the new created PO:
183
184
 
184
185
  include: {
185
186
  ...
186
187
  ${name}: '${result.filename}',
187
- },`);
188
+ },`)
188
189
 
189
- output.print(`Use ${output.colors.bold(colors.cyan(name))} as parameter in test scenarios to access this object:`);
190
- output.print(`\nScenario('my new test', ({ I, ${name} })) { /** ... */ }\n`);
190
+ output.print(`Use ${output.colors.bold(colors.cyan(name))} as parameter in test scenarios to access this object:`)
191
+ output.print(`\nScenario('my new test', ({ I, ${name} })) { /** ... */ }\n`)
191
192
 
192
- try {
193
- generateDefinitions(testsPath, {});
194
- } catch (_err) {
195
- output.print(`Run ${colors.green('npx codeceptjs def')} to update your types to get auto-completion for object.`);
196
- }
197
- });
198
- };
193
+ try {
194
+ generateDefinitions(testsPath, {})
195
+ } catch (_err) {
196
+ output.print(`Run ${colors.green('npx codeceptjs def')} to update your types to get auto-completion for object.`)
197
+ }
198
+ })
199
+ }
199
200
 
200
- const helperTemplate = `const Helper = require('@codeceptjs/helper');
201
+ const helperTemplate = `import Helper from '@codeceptjs/helper';
201
202
 
202
203
  class {{name}} extends Helper {
203
204
 
@@ -222,64 +223,71 @@ class {{name}} extends Helper {
222
223
 
223
224
  }
224
225
 
225
- module.exports = {{name}};
226
- `;
227
-
228
- export const helper = function (genPath) {
229
- const testsPath = getTestRoot(genPath);
230
-
231
- output.print('Creating a new helper');
232
- output.print('--------------------------');
233
-
234
- return inquirer.prompt([{
235
- type: 'input',
236
- name: 'name',
237
- message: 'Name of a Helper',
238
- validate: (val) => !!val,
239
- }, {
240
- type: 'input',
241
- name: 'filename',
242
- message: 'Where should it be stored',
243
- default: answers => `./${answers.name.toLowerCase()}_helper.${extension}`,
244
- }]).then((result) => {
245
- const name = ucfirst(result.name);
246
- const helperFile = path.join(testsPath, result.filename);
247
- const dir = path.dirname(helperFile);
248
- if (!fileExists(dir)) fs.mkdirSync(dir);
249
-
250
- if (!safeFileWrite(helperFile, helperTemplate.replace(/{{name}}/g, name))) return;
251
- output.success(`Helper for ${name} was created in ${helperFile}`);
252
- output.print(`Update your config file (add to ${colors.cyan('helpers')} section):
226
+ export default {{name}};
227
+ `
228
+
229
+ export async function helper(genPath) {
230
+ const testsPath = getTestRoot(genPath)
231
+
232
+ output.print('Creating a new helper')
233
+ output.print('--------------------------')
234
+
235
+ return inquirer
236
+ .prompt([
237
+ {
238
+ type: 'input',
239
+ name: 'name',
240
+ message: 'Name of a Helper',
241
+ validate: val => !!val,
242
+ },
243
+ {
244
+ type: 'input',
245
+ name: 'filename',
246
+ message: 'Where should it be stored',
247
+ default: answers => `./${answers.name.toLowerCase()}_helper.${extension}`,
248
+ },
249
+ ])
250
+ .then(result => {
251
+ const name = ucfirst(result.name)
252
+ const helperFile = path.join(testsPath, result.filename)
253
+ const dir = path.dirname(helperFile)
254
+ if (!fileExists(dir)) fs.mkdirSync(dir)
255
+
256
+ if (!safeFileWrite(helperFile, helperTemplate.replace(/{{name}}/g, name))) return
257
+ output.success(`Helper for ${name} was created in ${helperFile}`)
258
+ output.print(`Update your config file (add to ${colors.cyan('helpers')} section):
253
259
 
254
260
  helpers: {
255
261
  ${name}: {
256
262
  require: '${result.filename}',
257
263
  },
258
264
  },
259
- `);
260
- });
261
- };
265
+ `)
266
+ })
267
+ }
262
268
 
263
- const healTemplate = fs.readFileSync(path.join(__dirname, '/template/heal.js'), 'utf8').toString();
269
+ import { fileURLToPath } from 'url'
270
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
271
+ const healTemplate = fs.readFileSync(path.join(__dirname, '../template/heal.js'), 'utf8').toString()
264
272
 
265
- export function heal(genPath) {
266
- const testsPath = getTestRoot(genPath);
273
+ export async function heal(genPath) {
274
+ const testsPath = getTestRoot(genPath)
267
275
 
268
- let configFile = path.join(testsPath, `codecept.conf.${extension}`);
276
+ let configFile = path.join(testsPath, `codecept.conf.${extension}`)
269
277
 
270
278
  if (!fileExists(configFile)) {
271
- configFile = path.join(testsPath, `codecept.conf.${extension}`);
272
- if (fileExists(configFile)) extension = 'ts';
279
+ configFile = path.join(testsPath, `codecept.conf.${extension}`)
280
+ if (fileExists(configFile)) extension = 'ts'
273
281
  }
274
282
 
275
- output.print('Creating basic heal recipes');
276
- output.print(`Add your own custom recipes to ./heal.${extension} file`);
277
- output.print('Require this file in the config file and enable heal plugin:');
278
- output.print('--------------------------');
283
+ output.print('Creating basic heal recipes')
284
+ output.print(`Add your own custom recipes to ./heal.${extension} file`)
285
+ output.print('Require this file in the config file and enable heal plugin:')
286
+ output.print('--------------------------')
279
287
  output.print(`
280
- require('./heal')
288
+ import './heal.js'
281
289
 
282
- exports.config = {
290
+ export const config = {
283
291
  // ...
284
292
  plugins: {
285
293
  heal: {
@@ -287,9 +295,41 @@ exports.config = {
287
295
  }
288
296
  }
289
297
  }
290
- `);
298
+ `)
299
+
300
+ const healFile = path.join(testsPath, `heal.${extension}`)
301
+ if (!safeFileWrite(healFile, healTemplate)) return
302
+ output.success(`Heal recipes were created in ${healFile}`)
303
+ }
304
+
305
+ export async function prompt(promptName, genPath) {
306
+ if (!promptName) {
307
+ output.error('Please specify prompt name: writeStep, healStep, or generatePageObject')
308
+ output.print('Usage: npx codeceptjs generate:prompt <promptName>')
309
+ return
310
+ }
291
311
 
292
- const healFile = path.join(testsPath, `heal.${extension}`);
293
- if (!safeFileWrite(healFile, healTemplate)) return;
294
- output.success(`Heal recipes were created in ${healFile}`);
295
- };
312
+ const validPrompts = ['writeStep', 'healStep', 'generatePageObject']
313
+ if (!validPrompts.includes(promptName)) {
314
+ output.error(`Invalid prompt name: ${promptName}`)
315
+ output.print(`Valid prompts: ${validPrompts.join(', ')}`)
316
+ return
317
+ }
318
+
319
+ const testsPath = getTestRoot(genPath)
320
+
321
+ const promptsDir = path.join(testsPath, 'prompts')
322
+ if (!fileExists(promptsDir)) {
323
+ mkdirp.sync(promptsDir)
324
+ }
325
+
326
+ const templatePath = path.join(__dirname, `../template/prompts/${promptName}.js`)
327
+ const promptContent = fs.readFileSync(templatePath, 'utf8')
328
+
329
+ const promptFile = path.join(promptsDir, `${promptName}.${extension}`)
330
+ if (!safeFileWrite(promptFile, promptContent)) return
331
+
332
+ output.success(`Prompt ${promptName} was created in ${promptFile}`)
333
+ output.print('Customize this prompt to fit your needs.')
334
+ output.print('This prompt will be automatically loaded when AI features are enabled.')
335
+ }
@@ -1,10 +1,9 @@
1
- import path from 'path';
2
- import mkdirp from 'mkdirp';
3
- import * as output from '../../output.js';
4
- import { fileExists } from '../../utils.js';
5
- import {
6
- getConfig, getTestRoot, updateConfig, safeFileWrite,
7
- } from '../utils.js';
1
+ import path from 'path'
2
+ import { mkdirp } from 'mkdirp'
3
+
4
+ import output from '../../output.js'
5
+ import { fileExists } from '../../utils.js'
6
+ import { getConfig, getTestRoot, updateConfig, safeFileWrite, findConfigFile } from '../utils.js'
8
7
 
9
8
  const featureFile = `Feature: Business rules
10
9
  In order to achieve my goals
@@ -13,7 +12,7 @@ const featureFile = `Feature: Business rules
13
12
 
14
13
  Scenario: do something
15
14
  Given I have a defined step
16
- `;
15
+ `
17
16
 
18
17
  const stepsFile = `const { I } = inject();
19
18
  // Add in your custom step files
@@ -21,50 +20,60 @@ const stepsFile = `const { I } = inject();
21
20
  Given('I have a defined step', () => {
22
21
  // TODO: replace with your own step
23
22
  });
24
- `;
23
+ `
24
+
25
+ export default async function (genPath) {
26
+ const testsPath = getTestRoot(genPath)
27
+ const configFile = findConfigFile(testsPath)
25
28
 
26
- export default function (genPath) {
27
- const testsPath = getTestRoot(genPath);
28
- const config = getConfig(testsPath);
29
+ if (!configFile) {
30
+ output.error("Can't initialize Gherkin. This command must be run in an already initialized project.")
31
+ process.exit(1)
32
+ }
29
33
 
30
- output.print('Initializing Gherkin (Cucumber BDD) for CodeceptJS');
31
- output.print('--------------------------');
34
+ const config = await getConfig(testsPath)
35
+ const extension = path.extname(configFile).substring(1)
36
+
37
+ output.print('Initializing Gherkin (Cucumber BDD) for CodeceptJS')
38
+ output.print('--------------------------')
32
39
 
33
40
  if (config.gherkin && config.gherkin.steps) {
34
- output.output.error('Gherkin is already initialized in this project. See `gherkin` section in the config');
35
- process.exit(1);
41
+ output.error('Gherkin is already initialized in this project. See `gherkin` section in the config')
42
+ process.exit(1)
36
43
  }
37
44
 
38
- let dir;
39
- dir = path.join(testsPath, 'features');
45
+ let dir
46
+ dir = path.join(testsPath, 'features')
40
47
  if (!fileExists(dir)) {
41
- mkdirp.sync(dir);
42
- output.success(`Created ${dir}, place your *.feature files in it`);
48
+ mkdirp.sync(dir)
49
+ // Use relative path for output
50
+ const relativeDir = path.relative(process.cwd(), dir)
51
+ output.success(`Created ${relativeDir}, place your *.feature files in it`)
43
52
  }
44
53
 
45
54
  if (safeFileWrite(path.join(dir, 'basic.feature'), featureFile)) {
46
- output.success('Created sample feature file: features/basic.feature');
55
+ output.success('Created sample feature file: features/basic.feature')
47
56
  }
48
57
 
49
- dir = path.join(testsPath, 'step_definitions');
58
+ dir = path.join(testsPath, 'step_definitions')
50
59
  if (!fileExists(dir)) {
51
- mkdirp.sync(dir);
52
- output.success(`Created ${dir}, place step definitions into it`);
60
+ mkdirp.sync(dir)
61
+ // Use relative path for output
62
+ const relativeDir = path.relative(process.cwd(), dir)
63
+ output.success(`Created ${relativeDir}, place step definitions into it`)
53
64
  }
54
65
 
55
- if (safeFileWrite(path.join(dir, 'steps.js'), stepsFile)) {
56
- output.success('Created sample steps file: step_definitions/steps.js');
66
+ if (safeFileWrite(path.join(dir, `steps.${extension}`), stepsFile)) {
67
+ output.success(`Created sample steps file: step_definitions/steps.${extension}`)
57
68
  }
58
69
 
59
70
  config.gherkin = {
60
71
  features: './features/*.feature',
61
- steps: [
62
- './step_definitions/steps.js',
63
- ],
64
- };
72
+ steps: [`./step_definitions/steps.${extension}`],
73
+ }
65
74
 
66
- updateConfig(testsPath, config);
75
+ updateConfig(testsPath, config, extension)
67
76
 
68
- output.success('Gherkin setup is done.');
69
- output.success('Start writing feature files and implement corresponding steps.');
77
+ output.success('Gherkin setup is done.')
78
+ output.success('Start writing feature files and implement corresponding steps.')
70
79
  }