codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.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 +89 -119
- package/bin/codecept.js +53 -54
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +70 -102
- package/lib/ai.js +131 -121
- package/lib/assert/empty.js +11 -12
- package/lib/assert/equal.js +16 -21
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +11 -15
- package/lib/assert/throws.js +3 -5
- package/lib/assert/truth.js +10 -7
- package/lib/assert.js +18 -18
- package/lib/codecept.js +112 -101
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +13 -14
- package/lib/command/definitions.js +24 -36
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +38 -39
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +49 -15
- package/lib/command/init.js +41 -37
- package/lib/command/interactive.js +22 -13
- package/lib/command/list.js +11 -10
- package/lib/command/run-multiple/chunk.js +50 -47
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +27 -47
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +15 -66
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +22 -21
- package/lib/command/workers/runTests.js +131 -241
- package/lib/config.js +111 -49
- package/lib/container.js +589 -244
- package/lib/data/context.js +16 -18
- package/lib/data/dataScenarioConfig.js +9 -9
- package/lib/data/dataTableArgument.js +7 -7
- package/lib/data/table.js +6 -12
- package/lib/effects.js +307 -0
- package/lib/els.js +160 -0
- package/lib/event.js +24 -19
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -81
- package/lib/helper/AI.js +3 -2
- package/lib/helper/ApiDataFactory.js +19 -19
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +35 -15
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +72 -45
- package/lib/helper/Mochawesome.js +14 -11
- package/lib/helper/Playwright.js +832 -434
- package/lib/helper/Puppeteer.js +393 -292
- package/lib/helper/REST.js +32 -27
- package/lib/helper/WebDriver.js +320 -219
- 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/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/exit.js +10 -12
- package/lib/listener/{retry.js → globalRetry.js} +10 -10
- package/lib/listener/globalTimeout.js +166 -0
- package/lib/listener/helpers.js +43 -24
- package/lib/listener/mocha.js +4 -5
- package/lib/listener/result.js +11 -0
- package/lib/listener/steps.js +26 -23
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +264 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +160 -0
- package/lib/{interfaces → mocha}/featureConfig.js +33 -13
- package/lib/{interfaces → mocha}/gherkin.js +75 -45
- 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 +32 -8
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +178 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +229 -0
- package/lib/output.js +86 -64
- package/lib/parser.js +44 -44
- package/lib/pause.js +160 -139
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +137 -43
- package/lib/plugin/autoDelay.js +19 -15
- package/lib/plugin/coverage.js +22 -27
- package/lib/plugin/customLocator.js +5 -5
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/heal.js +49 -17
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +60 -19
- package/lib/plugin/screenshotOnFail.js +80 -83
- package/lib/plugin/stepByStepReport.js +70 -31
- package/lib/plugin/stepTimeout.js +7 -13
- package/lib/plugin/subtitles.js +10 -9
- package/lib/recorder.js +167 -126
- package/lib/rerun.js +94 -50
- package/lib/result.js +161 -0
- package/lib/secret.js +18 -17
- 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 -332
- package/lib/steps.js +54 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +354 -250
- package/lib/workerStorage.js +16 -16
- package/lib/workers.js +366 -282
- package/package.json +107 -95
- package/translations/de-DE.js +5 -4
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +23 -9
- package/translations/it-IT.js +5 -4
- package/translations/ja-JP.js +5 -4
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +5 -4
- package/translations/pt-BR.js +5 -4
- package/translations/ru-RU.js +5 -4
- package/translations/utils.js +18 -0
- package/translations/zh-CN.js +5 -4
- package/translations/zh-TW.js +5 -4
- package/typings/index.d.ts +177 -186
- package/typings/promiseBasedTypes.d.ts +3573 -5941
- package/typings/types.d.ts +4042 -6370
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/Nightmare.js +0 -1504
- package/lib/helper/Protractor.js +0 -1863
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1414
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -62
- package/lib/interfaces/bdd.js +0 -81
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- 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 -127
- package/lib/plugin/selenoid.js +0 -384
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/plugin/tryTo.js +0 -115
- package/lib/plugin/wdio.js +0 -249
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
- package/lib/within.js +0 -70
package/lib/command/generate.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
10
|
|
|
11
11
|
let extension = 'js'
|
|
12
12
|
|
|
@@ -18,10 +18,10 @@ Scenario('test something', async ({ {{actor}} }) => {
|
|
|
18
18
|
`
|
|
19
19
|
|
|
20
20
|
// generates empty test
|
|
21
|
-
|
|
21
|
+
export async function test(genPath) {
|
|
22
22
|
const testsPath = getTestRoot(genPath)
|
|
23
23
|
global.codecept_dir = testsPath
|
|
24
|
-
const config = getConfig(testsPath)
|
|
24
|
+
const config = await getConfig(testsPath)
|
|
25
25
|
if (!config) return
|
|
26
26
|
|
|
27
27
|
output.print('Creating a new test...')
|
|
@@ -35,7 +35,7 @@ module.exports.test = function (genPath) {
|
|
|
35
35
|
type: 'input',
|
|
36
36
|
name: 'feature',
|
|
37
37
|
message: 'Feature which is being tested (ex: account, login, etc)',
|
|
38
|
-
validate:
|
|
38
|
+
validate: val => !!val,
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
type: 'input',
|
|
@@ -46,7 +46,7 @@ module.exports.test = function (genPath) {
|
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
])
|
|
49
|
-
.then(
|
|
49
|
+
.then(async result => {
|
|
50
50
|
const testFilePath = path.dirname(path.join(testsPath, config.tests)).replace(/\*\*$/, '')
|
|
51
51
|
let testFile = path.join(testFilePath, result.filename)
|
|
52
52
|
const ext = path.extname(testFile)
|
|
@@ -55,17 +55,16 @@ module.exports.test = function (genPath) {
|
|
|
55
55
|
if (!fileExists(dir)) mkdirp.sync(dir)
|
|
56
56
|
let testContent = testTemplate.replace('{{feature}}', result.feature)
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
container.
|
|
58
|
+
const containerModule = await import('../container.js')
|
|
59
|
+
const container = containerModule.default || containerModule
|
|
60
|
+
await container.create(config, {})
|
|
60
61
|
// translate scenario test
|
|
61
62
|
if (container.translation().loaded) {
|
|
62
63
|
const vocabulary = container.translation().vocabulary
|
|
63
64
|
testContent = testContent.replace('{{actor}}', container.translation().I)
|
|
64
65
|
if (vocabulary.contexts.Feature) testContent = testContent.replace('Feature', vocabulary.contexts.Feature)
|
|
65
66
|
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
|
-
)
|
|
67
|
+
output.print(`Test was created in ${colors.bold(config.translation)} localization. See: https://codecept.io/translation/`)
|
|
69
68
|
} else {
|
|
70
69
|
testContent = testContent.replace('{{actor}}', 'I')
|
|
71
70
|
}
|
|
@@ -78,7 +77,7 @@ module.exports.test = function (genPath) {
|
|
|
78
77
|
|
|
79
78
|
const pageObjectTemplate = `const { I } = inject();
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
export default {
|
|
82
81
|
|
|
83
82
|
// insert your locators and methods here
|
|
84
83
|
}
|
|
@@ -103,13 +102,13 @@ class {{name}} {
|
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
// For inheritance
|
|
106
|
-
|
|
107
|
-
export
|
|
105
|
+
export default new {{name}}();
|
|
106
|
+
export { {{name}} };
|
|
108
107
|
`
|
|
109
108
|
|
|
110
|
-
|
|
109
|
+
export async function pageObject(genPath, opts) {
|
|
111
110
|
const testsPath = getTestRoot(genPath)
|
|
112
|
-
const config = getConfig(testsPath)
|
|
111
|
+
const config = await getConfig(testsPath)
|
|
113
112
|
const kind = opts.T || 'page'
|
|
114
113
|
if (!config) return
|
|
115
114
|
|
|
@@ -128,13 +127,13 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
128
127
|
type: 'input',
|
|
129
128
|
name: 'name',
|
|
130
129
|
message: `Name of a ${kind} object`,
|
|
131
|
-
validate:
|
|
130
|
+
validate: val => !!val,
|
|
132
131
|
},
|
|
133
132
|
{
|
|
134
133
|
type: 'input',
|
|
135
134
|
name: 'filename',
|
|
136
135
|
message: 'Where should it be stored',
|
|
137
|
-
default:
|
|
136
|
+
default: answers => `./${kind}s/${answers.name}.${extension}`,
|
|
138
137
|
},
|
|
139
138
|
{
|
|
140
139
|
type: 'list',
|
|
@@ -144,7 +143,7 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
144
143
|
default: 'module',
|
|
145
144
|
},
|
|
146
145
|
])
|
|
147
|
-
.then(
|
|
146
|
+
.then(result => {
|
|
148
147
|
const pageObjectFile = path.join(testsPath, result.filename)
|
|
149
148
|
const dir = path.dirname(pageObjectFile)
|
|
150
149
|
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
@@ -157,7 +156,7 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
157
156
|
// relative path
|
|
158
157
|
actorPath = path.relative(dir, path.dirname(path.join(testsPath, actorPath))) + actorPath.substring(1) // get an upper level
|
|
159
158
|
}
|
|
160
|
-
actor = `
|
|
159
|
+
actor = `import('${actorPath}')`
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
const name = lcfirst(result.name) + ucfirst(kind)
|
|
@@ -194,14 +193,12 @@ module.exports.pageObject = function (genPath, opts) {
|
|
|
194
193
|
try {
|
|
195
194
|
generateDefinitions(testsPath, {})
|
|
196
195
|
} catch (_err) {
|
|
197
|
-
output.print(
|
|
198
|
-
`Run ${colors.green('npx codeceptjs def')} to update your types to get auto-completion for object.`,
|
|
199
|
-
)
|
|
196
|
+
output.print(`Run ${colors.green('npx codeceptjs def')} to update your types to get auto-completion for object.`)
|
|
200
197
|
}
|
|
201
198
|
})
|
|
202
199
|
}
|
|
203
200
|
|
|
204
|
-
const helperTemplate = `
|
|
201
|
+
const helperTemplate = `import Helper from '@codeceptjs/helper';
|
|
205
202
|
|
|
206
203
|
class {{name}} extends Helper {
|
|
207
204
|
|
|
@@ -226,10 +223,10 @@ class {{name}} extends Helper {
|
|
|
226
223
|
|
|
227
224
|
}
|
|
228
225
|
|
|
229
|
-
|
|
226
|
+
export default {{name}};
|
|
230
227
|
`
|
|
231
228
|
|
|
232
|
-
|
|
229
|
+
export async function helper(genPath) {
|
|
233
230
|
const testsPath = getTestRoot(genPath)
|
|
234
231
|
|
|
235
232
|
output.print('Creating a new helper')
|
|
@@ -241,16 +238,16 @@ module.exports.helper = function (genPath) {
|
|
|
241
238
|
type: 'input',
|
|
242
239
|
name: 'name',
|
|
243
240
|
message: 'Name of a Helper',
|
|
244
|
-
validate:
|
|
241
|
+
validate: val => !!val,
|
|
245
242
|
},
|
|
246
243
|
{
|
|
247
244
|
type: 'input',
|
|
248
245
|
name: 'filename',
|
|
249
246
|
message: 'Where should it be stored',
|
|
250
|
-
default:
|
|
247
|
+
default: answers => `./${answers.name.toLowerCase()}_helper.${extension}`,
|
|
251
248
|
},
|
|
252
249
|
])
|
|
253
|
-
.then(
|
|
250
|
+
.then(result => {
|
|
254
251
|
const name = ucfirst(result.name)
|
|
255
252
|
const helperFile = path.join(testsPath, result.filename)
|
|
256
253
|
const dir = path.dirname(helperFile)
|
|
@@ -269,9 +266,11 @@ helpers: {
|
|
|
269
266
|
})
|
|
270
267
|
}
|
|
271
268
|
|
|
269
|
+
import { fileURLToPath } from 'url'
|
|
270
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
272
271
|
const healTemplate = fs.readFileSync(path.join(__dirname, '../template/heal.js'), 'utf8').toString()
|
|
273
272
|
|
|
274
|
-
|
|
273
|
+
export async function heal(genPath) {
|
|
275
274
|
const testsPath = getTestRoot(genPath)
|
|
276
275
|
|
|
277
276
|
let configFile = path.join(testsPath, `codecept.conf.${extension}`)
|
|
@@ -286,9 +285,9 @@ module.exports.heal = function (genPath) {
|
|
|
286
285
|
output.print('Require this file in the config file and enable heal plugin:')
|
|
287
286
|
output.print('--------------------------')
|
|
288
287
|
output.print(`
|
|
289
|
-
|
|
288
|
+
import './heal.js'
|
|
290
289
|
|
|
291
|
-
|
|
290
|
+
export const config = {
|
|
292
291
|
// ...
|
|
293
292
|
plugins: {
|
|
294
293
|
heal: {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { mkdirp } from 'mkdirp'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
getConfig, getTestRoot, updateConfig, safeFileWrite, findConfigFile,
|
|
8
|
-
} = require('../utils');
|
|
4
|
+
import output from '../../output.js'
|
|
5
|
+
import { fileExists } from '../../utils.js'
|
|
6
|
+
import { getConfig, getTestRoot, updateConfig, safeFileWrite, findConfigFile } from '../utils.js'
|
|
9
7
|
|
|
10
8
|
const featureFile = `Feature: Business rules
|
|
11
9
|
In order to achieve my goals
|
|
@@ -14,7 +12,7 @@ const featureFile = `Feature: Business rules
|
|
|
14
12
|
|
|
15
13
|
Scenario: do something
|
|
16
14
|
Given I have a defined step
|
|
17
|
-
|
|
15
|
+
`
|
|
18
16
|
|
|
19
17
|
const stepsFile = `const { I } = inject();
|
|
20
18
|
// Add in your custom step files
|
|
@@ -22,60 +20,60 @@ const stepsFile = `const { I } = inject();
|
|
|
22
20
|
Given('I have a defined step', () => {
|
|
23
21
|
// TODO: replace with your own step
|
|
24
22
|
});
|
|
25
|
-
|
|
23
|
+
`
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
const testsPath = getTestRoot(genPath)
|
|
29
|
-
const configFile = findConfigFile(testsPath)
|
|
25
|
+
export default async function (genPath) {
|
|
26
|
+
const testsPath = getTestRoot(genPath)
|
|
27
|
+
const configFile = findConfigFile(testsPath)
|
|
30
28
|
|
|
31
29
|
if (!configFile) {
|
|
32
|
-
output.error(
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
process.exit(1);
|
|
30
|
+
output.error("Can't initialize Gherkin. This command must be run in an already initialized project.")
|
|
31
|
+
process.exit(1)
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
const config = getConfig(testsPath)
|
|
39
|
-
const extension = path.extname(configFile).substring(1)
|
|
34
|
+
const config = await getConfig(testsPath)
|
|
35
|
+
const extension = path.extname(configFile).substring(1)
|
|
40
36
|
|
|
41
|
-
output.print('Initializing Gherkin (Cucumber BDD) for CodeceptJS')
|
|
42
|
-
output.print('--------------------------')
|
|
37
|
+
output.print('Initializing Gherkin (Cucumber BDD) for CodeceptJS')
|
|
38
|
+
output.print('--------------------------')
|
|
43
39
|
|
|
44
40
|
if (config.gherkin && config.gherkin.steps) {
|
|
45
|
-
output.error('Gherkin is already initialized in this project. See `gherkin` section in the config')
|
|
46
|
-
process.exit(1)
|
|
41
|
+
output.error('Gherkin is already initialized in this project. See `gherkin` section in the config')
|
|
42
|
+
process.exit(1)
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
let dir
|
|
50
|
-
dir = path.join(testsPath, 'features')
|
|
45
|
+
let dir
|
|
46
|
+
dir = path.join(testsPath, 'features')
|
|
51
47
|
if (!fileExists(dir)) {
|
|
52
|
-
mkdirp.sync(dir)
|
|
53
|
-
|
|
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`)
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
if (safeFileWrite(path.join(dir, 'basic.feature'), featureFile)) {
|
|
57
|
-
output.success('Created sample feature file: features/basic.feature')
|
|
55
|
+
output.success('Created sample feature file: features/basic.feature')
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
dir = path.join(testsPath, 'step_definitions')
|
|
58
|
+
dir = path.join(testsPath, 'step_definitions')
|
|
61
59
|
if (!fileExists(dir)) {
|
|
62
|
-
mkdirp.sync(dir)
|
|
63
|
-
|
|
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`)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (safeFileWrite(path.join(dir, `steps.${extension}`), stepsFile)) {
|
|
67
|
-
output.success(
|
|
68
|
-
`Created sample steps file: step_definitions/steps.${extension}`,
|
|
69
|
-
);
|
|
67
|
+
output.success(`Created sample steps file: step_definitions/steps.${extension}`)
|
|
70
68
|
}
|
|
71
69
|
|
|
72
70
|
config.gherkin = {
|
|
73
71
|
features: './features/*.feature',
|
|
74
72
|
steps: [`./step_definitions/steps.${extension}`],
|
|
75
|
-
}
|
|
73
|
+
}
|
|
76
74
|
|
|
77
|
-
updateConfig(testsPath, config, extension)
|
|
75
|
+
updateConfig(testsPath, config, extension)
|
|
78
76
|
|
|
79
|
-
output.success('Gherkin setup is done.')
|
|
80
|
-
output.success('Start writing feature files and implement corresponding steps.')
|
|
81
|
-
}
|
|
77
|
+
output.success('Gherkin setup is done.')
|
|
78
|
+
output.success('Start writing feature files and implement corresponding steps.')
|
|
79
|
+
}
|
|
@@ -1,132 +1,134 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import escapeStringRegexp from 'escape-string-regexp'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import Gherkin from '@cucumber/gherkin'
|
|
4
|
+
import { IdGenerator } from '@cucumber/messages'
|
|
5
|
+
import { globSync } from 'glob'
|
|
6
|
+
import fsPath from 'path'
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import { getConfig, getTestRoot } from '../utils.js'
|
|
9
|
+
import Codecept from '../../codecept.js'
|
|
10
|
+
import output from '../../output.js'
|
|
11
|
+
import { matchStep } from '../../mocha/bdd.js'
|
|
12
12
|
|
|
13
|
-
const uuidFn =
|
|
14
|
-
const builder = new Gherkin.AstBuilder(uuidFn)
|
|
15
|
-
const matcher = new Gherkin.GherkinClassicTokenMatcher()
|
|
16
|
-
const parser = new Gherkin.Parser(builder, matcher)
|
|
17
|
-
parser.stopAtFirstError = false
|
|
13
|
+
const uuidFn = IdGenerator.uuid()
|
|
14
|
+
const builder = new Gherkin.AstBuilder(uuidFn)
|
|
15
|
+
const matcher = new Gherkin.GherkinClassicTokenMatcher()
|
|
16
|
+
const parser = new Gherkin.Parser(builder, matcher)
|
|
17
|
+
parser.stopAtFirstError = false
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
const configFile = options.config || genPath
|
|
21
|
-
const testsPath = getTestRoot(configFile)
|
|
22
|
-
const config = getConfig(configFile)
|
|
23
|
-
if (!config) return
|
|
19
|
+
export default async function (genPath, options) {
|
|
20
|
+
const configFile = options.config || genPath
|
|
21
|
+
const testsPath = getTestRoot(configFile)
|
|
22
|
+
const config = await getConfig(configFile)
|
|
23
|
+
if (!config) return
|
|
24
24
|
|
|
25
|
-
const codecept = new Codecept(config, {})
|
|
26
|
-
codecept.init(testsPath)
|
|
25
|
+
const codecept = new Codecept(config, {})
|
|
26
|
+
await codecept.init(testsPath)
|
|
27
27
|
|
|
28
28
|
if (!config.gherkin) {
|
|
29
|
-
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it')
|
|
30
|
-
process.exit(1)
|
|
29
|
+
output.error('Gherkin is not enabled in config. Run `codecept gherkin:init` to enable it')
|
|
30
|
+
process.exit(1)
|
|
31
31
|
}
|
|
32
32
|
if (!config.gherkin.steps || !config.gherkin.steps[0]) {
|
|
33
|
-
output.error('No gherkin steps defined in config. Exiting')
|
|
34
|
-
process.exit(1)
|
|
33
|
+
output.error('No gherkin steps defined in config. Exiting')
|
|
34
|
+
process.exit(1)
|
|
35
35
|
}
|
|
36
36
|
if (!options.feature && !config.gherkin.features) {
|
|
37
|
-
output.error('No gherkin features defined in config. Exiting')
|
|
38
|
-
process.exit(1)
|
|
37
|
+
output.error('No gherkin features defined in config. Exiting')
|
|
38
|
+
process.exit(1)
|
|
39
39
|
}
|
|
40
40
|
if (options.path && !config.gherkin.steps.includes(options.path)) {
|
|
41
|
-
output.error(`You must include ${options.path} to the gherkin steps in your config file`)
|
|
42
|
-
process.exit(1)
|
|
41
|
+
output.error(`You must include ${options.path} to the gherkin steps in your config file`)
|
|
42
|
+
process.exit(1)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const files = []
|
|
46
|
-
|
|
45
|
+
const files = []
|
|
46
|
+
globSync(options.feature || config.gherkin.features, { cwd: options.feature ? '.' : global.codecept_dir }).forEach(file => {
|
|
47
47
|
if (!fsPath.isAbsolute(file)) {
|
|
48
|
-
file = fsPath.join(global.codecept_dir, file)
|
|
48
|
+
file = fsPath.join(global.codecept_dir, file)
|
|
49
49
|
}
|
|
50
|
-
files.push(fsPath.resolve(file))
|
|
51
|
-
})
|
|
52
|
-
output.print(`Loaded ${files.length} files`)
|
|
50
|
+
files.push(fsPath.resolve(file))
|
|
51
|
+
})
|
|
52
|
+
output.print(`Loaded ${files.length} files`)
|
|
53
53
|
|
|
54
|
-
const newSteps = new Map()
|
|
54
|
+
const newSteps = new Map()
|
|
55
55
|
|
|
56
|
-
const parseSteps =
|
|
57
|
-
const newSteps = []
|
|
58
|
-
let currentKeyword = ''
|
|
56
|
+
const parseSteps = steps => {
|
|
57
|
+
const newSteps = []
|
|
58
|
+
let currentKeyword = ''
|
|
59
59
|
for (const step of steps) {
|
|
60
60
|
if (step.keyword.trim() === 'And') {
|
|
61
|
-
if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`)
|
|
62
|
-
step.keyword = currentKeyword
|
|
61
|
+
if (!currentKeyword) throw new Error(`There is no active keyword for step '${step.text}'`)
|
|
62
|
+
step.keyword = currentKeyword
|
|
63
63
|
}
|
|
64
|
-
currentKeyword = step.keyword
|
|
64
|
+
currentKeyword = step.keyword
|
|
65
65
|
try {
|
|
66
|
-
matchStep(step.text)
|
|
66
|
+
matchStep(step.text)
|
|
67
67
|
} catch (err) {
|
|
68
|
-
let stepLine
|
|
68
|
+
let stepLine
|
|
69
69
|
if (/[{}()/]/.test(step.text)) {
|
|
70
70
|
stepLine = escapeStringRegexp(step.text)
|
|
71
71
|
.replace(/\//g, '\\/')
|
|
72
72
|
.replace(/\"(.*?)\"/g, '"(.*?)"')
|
|
73
73
|
.replace(/(\d+\\\.\d+)/, '(\\d+\\.\\d+)')
|
|
74
|
-
.replace(/ (\d+) /, ' (\\d+) ')
|
|
75
|
-
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: true })
|
|
74
|
+
.replace(/ (\d+) /, ' (\\d+) ')
|
|
75
|
+
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: true })
|
|
76
76
|
} else {
|
|
77
77
|
stepLine = step.text
|
|
78
78
|
.replace(/\"(.*?)\"/g, '{string}')
|
|
79
79
|
.replace(/(\d+\.\d+)/, '{float}')
|
|
80
|
-
.replace(/ (\d+) /, ' {int} ')
|
|
81
|
-
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: false })
|
|
80
|
+
.replace(/ (\d+) /, ' {int} ')
|
|
81
|
+
stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: false })
|
|
82
82
|
}
|
|
83
|
-
newSteps.push(stepLine)
|
|
83
|
+
newSteps.push(stepLine)
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
return newSteps
|
|
87
|
-
}
|
|
86
|
+
return newSteps
|
|
87
|
+
}
|
|
88
88
|
|
|
89
|
-
const parseFile =
|
|
90
|
-
const ast = parser.parse(fs.readFileSync(file).toString())
|
|
89
|
+
const parseFile = file => {
|
|
90
|
+
const ast = parser.parse(fs.readFileSync(file).toString())
|
|
91
91
|
for (const child of ast.feature.children) {
|
|
92
|
-
if (child.scenario.keyword === 'Scenario Outline') continue
|
|
93
|
-
parseSteps(child.scenario.steps)
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
if (child.scenario.keyword === 'Scenario Outline') continue // skip scenario outline
|
|
93
|
+
parseSteps(child.scenario.steps)
|
|
94
|
+
.map(step => {
|
|
95
|
+
return Object.assign(step, { file: file.replace(global.codecept_dir, '').slice(1) })
|
|
96
|
+
})
|
|
97
|
+
.map(step => newSteps.set(`${step.type}(${step})`, step))
|
|
96
98
|
}
|
|
97
|
-
}
|
|
99
|
+
}
|
|
98
100
|
|
|
99
|
-
files.forEach(file => parseFile(file))
|
|
101
|
+
files.forEach(file => parseFile(file))
|
|
100
102
|
|
|
101
|
-
let stepFile = options.path || config.gherkin.steps[0]
|
|
103
|
+
let stepFile = options.path || config.gherkin.steps[0]
|
|
102
104
|
if (!fs.existsSync(stepFile)) {
|
|
103
|
-
output.error(`Please enter a valid step file path ${stepFile}`)
|
|
104
|
-
process.exit(1)
|
|
105
|
+
output.error(`Please enter a valid step file path ${stepFile}`)
|
|
106
|
+
process.exit(1)
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
if (!fsPath.isAbsolute(stepFile)) {
|
|
108
|
-
stepFile = fsPath.join(global.codecept_dir, stepFile)
|
|
110
|
+
stepFile = fsPath.join(global.codecept_dir, stepFile)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
const snippets = [...newSteps.values()]
|
|
112
114
|
.filter((value, index, self) => self.indexOf(value) === index)
|
|
113
|
-
.map(
|
|
115
|
+
.map(step => {
|
|
114
116
|
return `
|
|
115
117
|
${step.type}(${step.regexp ? '/^' : "'"}${step}${step.regexp ? '$/' : "'"}, () => {
|
|
116
118
|
// From "${step.file}" ${JSON.stringify(step.location)}
|
|
117
119
|
throw new Error('Not implemented yet');
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
+
});`
|
|
121
|
+
})
|
|
120
122
|
|
|
121
123
|
if (!snippets.length) {
|
|
122
|
-
output.print('No new snippets found')
|
|
123
|
-
return
|
|
124
|
+
output.print('No new snippets found')
|
|
125
|
+
return
|
|
124
126
|
}
|
|
125
|
-
output.success(`Snippets generated: ${snippets.length}`)
|
|
126
|
-
output.print(snippets.join('\n'))
|
|
127
|
+
output.success(`Snippets generated: ${snippets.length}`)
|
|
128
|
+
output.print(snippets.join('\n'))
|
|
127
129
|
|
|
128
130
|
if (!options.dryRun) {
|
|
129
|
-
output.success(`Snippets added to ${output.colors.bold(stepFile)}`)
|
|
130
|
-
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n')
|
|
131
|
+
output.success(`Snippets added to ${output.colors.bold(stepFile)}`)
|
|
132
|
+
fs.writeFileSync(stepFile, fs.readFileSync(stepFile).toString() + snippets.join('\n') + '\n')
|
|
131
133
|
}
|
|
132
|
-
}
|
|
134
|
+
}
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getConfig, getTestRoot } from '../utils.js'
|
|
2
|
+
import Codecept from '../../codecept.js'
|
|
3
|
+
import output from '../../output.js'
|
|
4
|
+
import { getSteps, clearSteps } from '../../mocha/bdd.js'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const configFile = options.config || genPath
|
|
8
|
-
const testsPath = getTestRoot(configFile)
|
|
9
|
-
const config = getConfig(configFile)
|
|
10
|
-
if (!config) return
|
|
6
|
+
export default async function (genPath, options) {
|
|
7
|
+
const configFile = options.config || genPath
|
|
8
|
+
const testsPath = getTestRoot(configFile)
|
|
9
|
+
const config = await getConfig(configFile)
|
|
10
|
+
if (!config) return
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Clear any previously loaded steps
|
|
13
|
+
clearSteps()
|
|
14
|
+
|
|
15
|
+
const codecept = new Codecept(config, {})
|
|
16
|
+
await codecept.init(testsPath)
|
|
14
17
|
|
|
15
|
-
output.print('Gherkin Step Definitions:')
|
|
16
|
-
output.print()
|
|
17
|
-
const steps = getSteps()
|
|
18
|
+
output.print('Gherkin Step Definitions:')
|
|
19
|
+
output.print()
|
|
20
|
+
const steps = getSteps()
|
|
18
21
|
for (const step of Object.keys(steps)) {
|
|
19
|
-
output.print(` ${output.colors.bold(step)} ${output.colors.green(steps[step].line || '')}`)
|
|
22
|
+
output.print(` ${output.colors.bold(step)} ${output.colors.green(steps[step].line || '')}`)
|
|
20
23
|
}
|
|
21
|
-
output.print()
|
|
24
|
+
output.print()
|
|
22
25
|
if (!Object.keys(steps).length) {
|
|
23
|
-
output.error('No Gherkin steps defined')
|
|
26
|
+
output.error('No Gherkin steps defined')
|
|
24
27
|
}
|
|
25
|
-
}
|
|
28
|
+
}
|