codeceptjs 4.0.0-beta.1 → 4.0.0-beta.3
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/bin/codecept.js +84 -81
- package/lib/actor.js +13 -13
- package/lib/ai.js +10 -13
- package/lib/assert/empty.js +20 -21
- package/lib/assert/equal.js +37 -39
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +46 -47
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +19 -22
- package/lib/assert.js +4 -2
- package/lib/cli.js +57 -49
- package/lib/codecept.js +142 -155
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +58 -52
- package/lib/command/definitions.js +88 -89
- package/lib/command/dryRun.js +71 -68
- package/lib/command/generate.js +197 -188
- package/lib/command/gherkin/init.js +27 -16
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +40 -38
- package/lib/command/init.js +290 -288
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +6 -2
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +20 -25
- package/lib/command/run-workers.js +64 -66
- package/lib/command/run.js +26 -29
- package/lib/command/utils.js +80 -65
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +10 -9
- package/lib/container.js +40 -48
- package/lib/data/context.js +60 -59
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/event.js +163 -167
- package/lib/heal.js +13 -17
- package/lib/helper/AI.js +130 -41
- package/lib/helper/ApiDataFactory.js +73 -69
- package/lib/helper/Appium.js +413 -382
- package/lib/helper/ExpectHelper.js +40 -48
- package/lib/helper/FileSystem.js +80 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +65 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -14
- package/lib/helper/Nightmare.js +662 -566
- package/lib/helper/Playwright.js +1361 -1216
- package/lib/helper/Protractor.js +663 -627
- package/lib/helper/Puppeteer.js +1231 -1128
- package/lib/helper/REST.js +159 -68
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +490 -484
- package/lib/helper/WebDriver.js +1297 -1156
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +21 -18
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +14 -7
- package/lib/helper/network/utils.js +3 -2
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +6 -7
- package/lib/helper.js +1 -3
- package/lib/history.js +6 -5
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +25 -41
- package/lib/interfaces/bdd.js +47 -64
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +124 -118
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +32 -30
- package/lib/listener/steps.js +50 -53
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +6 -10
- package/lib/mochaFactory.js +18 -15
- package/lib/output.js +6 -10
- package/lib/parser.js +15 -12
- package/lib/pause.js +40 -33
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +29 -37
- package/lib/plugin/autoLogin.js +70 -65
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +115 -67
- package/lib/plugin/customLocator.js +21 -20
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +67 -108
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +32 -39
- package/lib/plugin/retryTo.js +46 -40
- package/lib/plugin/screenshotOnFail.js +109 -87
- package/lib/plugin/selenoid.js +131 -118
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +110 -91
- package/lib/plugin/stepTimeout.js +24 -23
- package/lib/plugin/subtitles.js +34 -35
- package/lib/plugin/tryTo.js +40 -30
- package/lib/plugin/wdio.js +78 -75
- package/lib/recorder.js +14 -17
- package/lib/rerun.js +11 -10
- package/lib/scenario.js +25 -23
- package/lib/secret.js +4 -2
- package/lib/session.js +10 -10
- package/lib/step.js +12 -9
- package/lib/store.js +2 -3
- package/lib/transform.js +1 -1
- package/lib/translation.js +7 -8
- package/lib/ui.js +12 -14
- package/lib/utils.js +70 -72
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +27 -25
- package/lib/workers.js +29 -32
- package/package.json +56 -57
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -13
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +415 -65
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
package/lib/command/generate.js
CHANGED
|
@@ -1,81 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
const colors = require('chalk')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const inquirer = require('inquirer')
|
|
4
|
+
const mkdirp = require('mkdirp')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { fileExists, ucfirst, lcfirst, beautify } = require('../utils')
|
|
7
|
+
const output = require('../output')
|
|
8
|
+
const generateDefinitions = require('./definitions')
|
|
9
|
+
const { getConfig, getTestRoot, safeFileWrite, readConfig } = require('./utils')
|
|
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
|
-
|
|
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
|
+
module.exports.test = function (genPath) {
|
|
22
|
+
const testsPath = getTestRoot(genPath)
|
|
23
|
+
global.codecept_dir = testsPath
|
|
24
|
+
const config = 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((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 container = require('../container')
|
|
59
|
+
container.create(config, {})
|
|
60
|
+
// translate scenario test
|
|
61
|
+
if (container.translation().loaded) {
|
|
62
|
+
const vocabulary = container.translation().vocabulary
|
|
63
|
+
testContent = testContent.replace('{{actor}}', container.translation().I)
|
|
64
|
+
if (vocabulary.contexts.Feature) testContent = testContent.replace('Feature', vocabulary.contexts.Feature)
|
|
65
|
+
if (vocabulary.contexts.Scenario) testContent = testContent.replace('Scenario', vocabulary.contexts.Scenario)
|
|
66
|
+
output.print(
|
|
67
|
+
`Test was created in ${colors.bold(config.translation)} localization. See: https://codecept.io/translation/`,
|
|
68
|
+
)
|
|
69
|
+
} else {
|
|
70
|
+
testContent = testContent.replace('{{actor}}', 'I')
|
|
71
|
+
}
|
|
72
|
+
if (!config.fullPromiseBased) testContent = testContent.replace('async', '')
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
74
|
+
if (!safeFileWrite(testFile, testContent)) return
|
|
75
|
+
output.success(`\nTest for ${result.filename} was created in ${testFile}`)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
79
78
|
|
|
80
79
|
const pageObjectTemplate = `const { I } = inject();
|
|
81
80
|
|
|
@@ -83,7 +82,7 @@ module.exports = {
|
|
|
83
82
|
|
|
84
83
|
// insert your locators and methods here
|
|
85
84
|
}
|
|
86
|
-
|
|
85
|
+
`
|
|
87
86
|
|
|
88
87
|
const poModuleTemplateTS = `const { I } = inject();
|
|
89
88
|
|
|
@@ -91,7 +90,7 @@ export = {
|
|
|
91
90
|
|
|
92
91
|
// insert your locators and methods here
|
|
93
92
|
}
|
|
94
|
-
|
|
93
|
+
`
|
|
95
94
|
|
|
96
95
|
const poClassTemplate = `const { I } = inject();
|
|
97
96
|
|
|
@@ -106,96 +105,101 @@ class {{name}} {
|
|
|
106
105
|
// For inheritance
|
|
107
106
|
module.exports = new {{name}}();
|
|
108
107
|
export = {{name}};
|
|
109
|
-
|
|
108
|
+
`
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
const testsPath = getTestRoot(genPath)
|
|
113
|
-
const config = getConfig(testsPath)
|
|
114
|
-
const kind = opts.T || 'page'
|
|
115
|
-
if (!config) return
|
|
110
|
+
module.exports.pageObject = function (genPath, opts) {
|
|
111
|
+
const testsPath = getTestRoot(genPath)
|
|
112
|
+
const config = getConfig(testsPath)
|
|
113
|
+
const kind = opts.T || 'page'
|
|
114
|
+
if (!config) return
|
|
116
115
|
|
|
117
|
-
let configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
116
|
+
let configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
118
117
|
|
|
119
118
|
if (!fileExists(configFile)) {
|
|
120
|
-
extension = 'ts'
|
|
121
|
-
configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
119
|
+
extension = 'ts'
|
|
120
|
+
configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
122
121
|
}
|
|
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 =
|
|
122
|
+
output.print(`Creating a new ${kind} object`)
|
|
123
|
+
output.print('--------------------------')
|
|
124
|
+
|
|
125
|
+
return inquirer
|
|
126
|
+
.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
|
+
])
|
|
147
|
+
.then((result) => {
|
|
148
|
+
const pageObjectFile = path.join(testsPath, result.filename)
|
|
149
|
+
const dir = path.dirname(pageObjectFile)
|
|
150
|
+
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
151
|
+
|
|
152
|
+
let actor = 'actor'
|
|
153
|
+
|
|
154
|
+
if (config.include.I) {
|
|
155
|
+
let actorPath = config.include.I
|
|
156
|
+
if (actorPath.charAt(0) === '.') {
|
|
157
|
+
// relative path
|
|
158
|
+
actorPath = path.relative(dir, path.dirname(path.join(testsPath, actorPath))) + actorPath.substring(1) // get an upper level
|
|
159
|
+
}
|
|
160
|
+
actor = `require('${actorPath}')`
|
|
157
161
|
}
|
|
158
|
-
actor = `require('${actorPath}')`;
|
|
159
|
-
}
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
const name = lcfirst(result.name) + ucfirst(kind)
|
|
164
|
+
if (result.objectType === 'module' && extension === 'ts') {
|
|
165
|
+
if (!safeFileWrite(pageObjectFile, poModuleTemplateTS.replace('{{actor}}', actor))) return
|
|
166
|
+
} else if (result.objectType === 'module' && extension === 'js') {
|
|
167
|
+
if (!safeFileWrite(pageObjectFile, pageObjectTemplate.replace('{{actor}}', actor))) return
|
|
168
|
+
} else if (result.objectType === 'class') {
|
|
169
|
+
const content = poClassTemplate.replace(/{{actor}}/g, actor).replace(/{{name}}/g, name)
|
|
170
|
+
if (!safeFileWrite(pageObjectFile, content)) return
|
|
171
|
+
}
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
+
let data = readConfig(configFile)
|
|
174
|
+
config.include[name] = result.filename
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
if (!data) throw Error('Config file is empty')
|
|
177
|
+
const currentInclude = `${data.match(/include:[\s\S][^\}]*/i)[0]}\n ${name}:${JSON.stringify(config.include[name])}`
|
|
176
178
|
|
|
177
|
-
|
|
179
|
+
data = data.replace(/include:[\s\S][^\}]*/i, `${currentInclude},`)
|
|
178
180
|
|
|
179
|
-
|
|
181
|
+
fs.writeFileSync(configFile, beautify(data), 'utf-8')
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
output.success(`${ucfirst(kind)} object for ${result.name} was created in ${pageObjectFile}`)
|
|
184
|
+
output.print(`Your config file (${colors.cyan('include')} section) has included the new created PO:
|
|
183
185
|
|
|
184
186
|
include: {
|
|
185
187
|
...
|
|
186
188
|
${name}: '${result.filename}',
|
|
187
|
-
},`)
|
|
189
|
+
},`)
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
output.print(`Use ${output.colors.bold(colors.cyan(name))} as parameter in test scenarios to access this object:`)
|
|
192
|
+
output.print(`\nScenario('my new test', ({ I, ${name} })) { /** ... */ }\n`)
|
|
191
193
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
194
|
+
try {
|
|
195
|
+
generateDefinitions(testsPath, {})
|
|
196
|
+
} catch (_err) {
|
|
197
|
+
output.print(
|
|
198
|
+
`Run ${colors.green('npx codeceptjs def')} to update your types to get auto-completion for object.`,
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
}
|
|
199
203
|
|
|
200
204
|
const helperTemplate = `const Helper = require('@codeceptjs/helper');
|
|
201
205
|
|
|
@@ -223,73 +227,78 @@ class {{name}} extends Helper {
|
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
module.exports = {{name}};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
230
|
+
`
|
|
231
|
+
|
|
232
|
+
module.exports.helper = function (genPath) {
|
|
233
|
+
const testsPath = getTestRoot(genPath)
|
|
234
|
+
|
|
235
|
+
output.print('Creating a new helper')
|
|
236
|
+
output.print('--------------------------')
|
|
237
|
+
|
|
238
|
+
return inquirer
|
|
239
|
+
.prompt([
|
|
240
|
+
{
|
|
241
|
+
type: 'input',
|
|
242
|
+
name: 'name',
|
|
243
|
+
message: 'Name of a Helper',
|
|
244
|
+
validate: (val) => !!val,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
type: 'input',
|
|
248
|
+
name: 'filename',
|
|
249
|
+
message: 'Where should it be stored',
|
|
250
|
+
default: (answers) => `./${answers.name.toLowerCase()}_helper.${extension}`,
|
|
251
|
+
},
|
|
252
|
+
])
|
|
253
|
+
.then((result) => {
|
|
254
|
+
const name = ucfirst(result.name)
|
|
255
|
+
const helperFile = path.join(testsPath, result.filename)
|
|
256
|
+
const dir = path.dirname(helperFile)
|
|
257
|
+
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
258
|
+
|
|
259
|
+
if (!safeFileWrite(helperFile, helperTemplate.replace(/{{name}}/g, name))) return
|
|
260
|
+
output.success(`Helper for ${name} was created in ${helperFile}`)
|
|
261
|
+
output.print(`Update your config file (add to ${colors.cyan('helpers')} section):
|
|
253
262
|
|
|
254
263
|
helpers: {
|
|
255
264
|
${name}: {
|
|
256
265
|
require: '${result.filename}',
|
|
257
266
|
},
|
|
258
267
|
},
|
|
259
|
-
`)
|
|
260
|
-
|
|
261
|
-
}
|
|
268
|
+
`)
|
|
269
|
+
})
|
|
270
|
+
}
|
|
262
271
|
|
|
263
|
-
const healTemplate = fs.readFileSync(path.join(__dirname, '
|
|
272
|
+
const healTemplate = fs.readFileSync(path.join(__dirname, '../template/heal.js'), 'utf8').toString()
|
|
264
273
|
|
|
265
|
-
|
|
266
|
-
const testsPath = getTestRoot(genPath)
|
|
274
|
+
module.exports.heal = function (genPath) {
|
|
275
|
+
const testsPath = getTestRoot(genPath)
|
|
267
276
|
|
|
268
|
-
let configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
277
|
+
let configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
269
278
|
|
|
270
279
|
if (!fileExists(configFile)) {
|
|
271
|
-
configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
272
|
-
if (fileExists(configFile)) extension = 'ts'
|
|
280
|
+
configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
281
|
+
if (fileExists(configFile)) extension = 'ts'
|
|
273
282
|
}
|
|
274
283
|
|
|
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('--------------------------')
|
|
284
|
+
output.print('Creating basic heal recipes')
|
|
285
|
+
output.print(`Add your own custom recipes to ./heal.${extension} file`)
|
|
286
|
+
output.print('Require this file in the config file and enable heal plugin:')
|
|
287
|
+
output.print('--------------------------')
|
|
279
288
|
output.print(`
|
|
280
289
|
require('./heal')
|
|
281
290
|
|
|
282
291
|
exports.config = {
|
|
283
|
-
// ...
|
|
292
|
+
// ...
|
|
284
293
|
plugins: {
|
|
285
294
|
heal: {
|
|
286
295
|
enabled: true
|
|
287
296
|
}
|
|
288
297
|
}
|
|
289
298
|
}
|
|
290
|
-
`)
|
|
299
|
+
`)
|
|
291
300
|
|
|
292
|
-
const healFile = path.join(testsPath, `heal.${extension}`)
|
|
293
|
-
if (!safeFileWrite(healFile, healTemplate)) return
|
|
294
|
-
output.success(`Heal recipes were created in ${healFile}`)
|
|
295
|
-
}
|
|
301
|
+
const healFile = path.join(testsPath, `heal.${extension}`)
|
|
302
|
+
if (!safeFileWrite(healFile, healTemplate)) return
|
|
303
|
+
output.success(`Heal recipes were created in ${healFile}`)
|
|
304
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const mkdirp = require('mkdirp');
|
|
3
|
+
|
|
4
|
+
const output = require('../../output');
|
|
5
|
+
const { fileExists } = require('../../utils');
|
|
6
|
+
const {
|
|
7
|
+
getConfig, getTestRoot, updateConfig, safeFileWrite, findConfigFile,
|
|
8
|
+
} = require('../utils');
|
|
8
9
|
|
|
9
10
|
const featureFile = `Feature: Business rules
|
|
10
11
|
In order to achieve my goals
|
|
@@ -23,15 +24,25 @@ Given('I have a defined step', () => {
|
|
|
23
24
|
});
|
|
24
25
|
`;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
module.exports = function (genPath) {
|
|
27
28
|
const testsPath = getTestRoot(genPath);
|
|
29
|
+
const configFile = findConfigFile(testsPath);
|
|
30
|
+
|
|
31
|
+
if (!configFile) {
|
|
32
|
+
output.error(
|
|
33
|
+
"Can't initialize Gherkin. This command must be run in an already initialized project.",
|
|
34
|
+
);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
const config = getConfig(testsPath);
|
|
39
|
+
const extension = path.extname(configFile).substring(1);
|
|
29
40
|
|
|
30
41
|
output.print('Initializing Gherkin (Cucumber BDD) for CodeceptJS');
|
|
31
42
|
output.print('--------------------------');
|
|
32
43
|
|
|
33
44
|
if (config.gherkin && config.gherkin.steps) {
|
|
34
|
-
output.
|
|
45
|
+
output.error('Gherkin is already initialized in this project. See `gherkin` section in the config');
|
|
35
46
|
process.exit(1);
|
|
36
47
|
}
|
|
37
48
|
|
|
@@ -52,19 +63,19 @@ export default function (genPath) {
|
|
|
52
63
|
output.success(`Created ${dir}, 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(
|
|
68
|
+
`Created sample steps file: step_definitions/steps.${extension}`,
|
|
69
|
+
);
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
config.gherkin = {
|
|
60
73
|
features: './features/*.feature',
|
|
61
|
-
steps: [
|
|
62
|
-
'./step_definitions/steps.js',
|
|
63
|
-
],
|
|
74
|
+
steps: [`./step_definitions/steps.${extension}`],
|
|
64
75
|
};
|
|
65
76
|
|
|
66
|
-
updateConfig(testsPath, config);
|
|
77
|
+
updateConfig(testsPath, config, extension);
|
|
67
78
|
|
|
68
79
|
output.success('Gherkin setup is done.');
|
|
69
80
|
output.success('Start writing feature files and implement corresponding steps.');
|
|
70
|
-
}
|
|
81
|
+
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
const escapeStringRegexp = require('escape-string-regexp');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const Gherkin = require('@cucumber/gherkin');
|
|
4
|
+
const Messages = require('@cucumber/messages');
|
|
5
|
+
const glob = require('glob');
|
|
6
|
+
const fsPath = require('path');
|
|
7
|
+
|
|
8
|
+
const { getConfig, getTestRoot } = require('../utils');
|
|
9
|
+
const Codecept = require('../../codecept');
|
|
10
|
+
const output = require('../../output');
|
|
11
|
+
const { matchStep } = require('../../interfaces/bdd');
|
|
11
12
|
|
|
12
13
|
const uuidFn = Messages.IdGenerator.uuid();
|
|
13
14
|
const builder = new Gherkin.AstBuilder(uuidFn);
|
|
@@ -15,7 +16,7 @@ const matcher = new Gherkin.GherkinClassicTokenMatcher();
|
|
|
15
16
|
const parser = new Gherkin.Parser(builder, matcher);
|
|
16
17
|
parser.stopAtFirstError = false;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
module.exports = function (genPath, options) {
|
|
19
20
|
const configFile = options.config || genPath;
|
|
20
21
|
const testsPath = getTestRoot(configFile);
|
|
21
22
|
const config = getConfig(configFile);
|
|
@@ -25,19 +26,19 @@ export default function (genPath, options) {
|
|
|
25
26
|
codecept.init(testsPath);
|
|
26
27
|
|
|
27
28
|
if (!config.gherkin) {
|
|
28
|
-
output.
|
|
29
|
+
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it');
|
|
29
30
|
process.exit(1);
|
|
30
31
|
}
|
|
31
32
|
if (!config.gherkin.steps || !config.gherkin.steps[0]) {
|
|
32
|
-
output.
|
|
33
|
+
output.error('No gherkin steps defined in config. Exiting');
|
|
33
34
|
process.exit(1);
|
|
34
35
|
}
|
|
35
36
|
if (!options.feature && !config.gherkin.features) {
|
|
36
|
-
output.
|
|
37
|
+
output.error('No gherkin features defined in config. Exiting');
|
|
37
38
|
process.exit(1);
|
|
38
39
|
}
|
|
39
40
|
if (options.path && !config.gherkin.steps.includes(options.path)) {
|
|
40
|
-
output.
|
|
41
|
+
output.error(`You must include ${options.path} to the gherkin steps in your config file`);
|
|
41
42
|
process.exit(1);
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -86,7 +87,6 @@ export default function (genPath, options) {
|
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
const parseFile = (file) => {
|
|
89
|
-
console.log(file);
|
|
90
90
|
const ast = parser.parse(fs.readFileSync(file).toString());
|
|
91
91
|
for (const child of ast.feature.children) {
|
|
92
92
|
if (child.scenario.keyword === 'Scenario Outline') continue; // skip scenario outline
|
|
@@ -100,7 +100,7 @@ export default function (genPath, options) {
|
|
|
100
100
|
|
|
101
101
|
let stepFile = options.path || config.gherkin.steps[0];
|
|
102
102
|
if (!fs.existsSync(stepFile)) {
|
|
103
|
-
output.
|
|
103
|
+
output.error(`Please enter a valid step file path ${stepFile}`);
|
|
104
104
|
process.exit(1);
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -122,11 +122,11 @@ ${step.type}(${step.regexp ? '/^' : "'"}${step}${step.regexp ? '$/' : "'"}, () =
|
|
|
122
122
|
output.print('No new snippets found');
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
|
-
output.
|
|
125
|
+
output.success(`Snippets generated: ${snippets.length}`);
|
|
126
126
|
output.print(snippets.join('\n'));
|
|
127
127
|
|
|
128
128
|
if (!options.dryRun) {
|
|
129
|
-
output.
|
|
129
|
+
output.success(`Snippets added to ${output.colors.bold(stepFile)}`);
|
|
130
130
|
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n'); // eslint-disable-line
|
|
131
131
|
}
|
|
132
|
-
}
|
|
132
|
+
};
|