codeceptjs 4.0.0-beta.1 → 4.0.0-beta.10.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.
- package/README.md +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +71 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +238 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +300 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +124 -50
- package/lib/container.js +751 -260
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +47 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +115 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -879
- package/typings/types.d.ts +547 -996
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- package/lib/within.js +0 -70
package/lib/command/generate.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
export
|
|
109
|
-
|
|
105
|
+
export default new {{name}}();
|
|
106
|
+
export { {{name}} };
|
|
107
|
+
`
|
|
110
108
|
|
|
111
|
-
export
|
|
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
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
actorPath =
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
172
|
-
|
|
172
|
+
let data = readConfig(configFile)
|
|
173
|
+
config.include[name] = result.filename
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
178
|
+
data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude},`)
|
|
178
179
|
|
|
179
|
-
|
|
180
|
+
fs.writeFileSync(configFile, beautify(data), 'utf-8')
|
|
180
181
|
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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 = `
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
export
|
|
229
|
-
const testsPath = getTestRoot(genPath)
|
|
230
|
-
|
|
231
|
-
output.print('Creating a new helper')
|
|
232
|
-
output.print('--------------------------')
|
|
233
|
-
|
|
234
|
-
return inquirer
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
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
|
-
|
|
288
|
+
import './heal.js'
|
|
281
289
|
|
|
282
|
-
|
|
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
|
|
293
|
-
if (!
|
|
294
|
-
|
|
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
|
-
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
56
|
-
output.success(
|
|
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
|
-
|
|
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
|
}
|