codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21
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 +73 -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 +262 -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 +301 -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 +109 -50
- package/lib/container.js +765 -261
- 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 +54 -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/loaderCheck.js +124 -0
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +124 -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 -875
- package/typings/types.d.ts +547 -992
- 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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { getConfig, getTestRoot } from './utils.js'
|
|
2
|
+
import Codecept from '../codecept.js'
|
|
3
|
+
import output from '../output.js'
|
|
4
|
+
import store from '../store.js'
|
|
5
|
+
import Container from '../container.js'
|
|
6
|
+
import figures from 'figures'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import { createTest } from '../mocha/test.js'
|
|
9
|
+
import { getMachineInfo } from './info.js'
|
|
10
|
+
import definitions from './definitions.js'
|
|
11
|
+
|
|
12
|
+
export default async function (options) {
|
|
13
|
+
const configFile = options.config
|
|
14
|
+
|
|
15
|
+
setTimeout(() => {
|
|
16
|
+
output.error("Something went wrong. Checks didn't pass and timed out. Please check your config and helpers.")
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}, options.timeout || 50000)
|
|
19
|
+
|
|
20
|
+
const checks = {
|
|
21
|
+
config: false,
|
|
22
|
+
container: false,
|
|
23
|
+
pageObjects: false,
|
|
24
|
+
plugins: false,
|
|
25
|
+
ai: true, // we don't need to check AI
|
|
26
|
+
helpers: false,
|
|
27
|
+
setup: false,
|
|
28
|
+
teardown: false,
|
|
29
|
+
tests: false,
|
|
30
|
+
def: false,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const testRoot = getTestRoot(configFile)
|
|
34
|
+
let config = await getConfig(configFile)
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
config = await getConfig(configFile)
|
|
38
|
+
checks['config'] = true
|
|
39
|
+
} catch (err) {
|
|
40
|
+
checks['config'] = err
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
printCheck('config', checks['config'], config.name)
|
|
44
|
+
|
|
45
|
+
let codecept
|
|
46
|
+
try {
|
|
47
|
+
codecept = new Codecept(config, options)
|
|
48
|
+
await codecept.init(testRoot)
|
|
49
|
+
await Container.started()
|
|
50
|
+
checks.container = true
|
|
51
|
+
} catch (err) {
|
|
52
|
+
checks.container = err
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const standardActingHelpers = Container.STANDARD_ACTING_HELPERS
|
|
56
|
+
|
|
57
|
+
printCheck('container', checks['container'])
|
|
58
|
+
|
|
59
|
+
if (codecept) {
|
|
60
|
+
try {
|
|
61
|
+
if (options.bootstrap) await codecept.bootstrap()
|
|
62
|
+
checks.bootstrap = true
|
|
63
|
+
} catch (err) {
|
|
64
|
+
checks.bootstrap = err
|
|
65
|
+
}
|
|
66
|
+
printCheck('bootstrap', checks['bootstrap'], options.bootstrap ? 'Bootstrap was executed' : 'No bootstrap command')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let numTests = 0
|
|
70
|
+
if (codecept) {
|
|
71
|
+
try {
|
|
72
|
+
codecept.loadTests()
|
|
73
|
+
const files = codecept.testFiles
|
|
74
|
+
const mocha = Container.mocha()
|
|
75
|
+
mocha.files = files
|
|
76
|
+
mocha.loadFiles()
|
|
77
|
+
|
|
78
|
+
for (const suite of mocha.suite.suites) {
|
|
79
|
+
if (suite && suite.tests) {
|
|
80
|
+
numTests += suite.tests.length
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (numTests > 0) {
|
|
85
|
+
checks.tests = true
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error('No tests found')
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
checks.tests = err
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (config?.ai?.request) {
|
|
95
|
+
checks.ai = true
|
|
96
|
+
printCheck('ai', checks['ai'], 'Configuration is enabled, request function is set')
|
|
97
|
+
} else {
|
|
98
|
+
printCheck('ai', checks['ai'], 'Disabled')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
printCheck('tests', checks['tests'], `Total: ${numTests} tests`)
|
|
102
|
+
|
|
103
|
+
store.dryRun = true
|
|
104
|
+
|
|
105
|
+
const helpers = Container.helpers()
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
if (!Object.keys(helpers).length) throw new Error('No helpers found')
|
|
109
|
+
// load helpers
|
|
110
|
+
for (const helper of Object.values(helpers)) {
|
|
111
|
+
if (helper._init) await helper._init()
|
|
112
|
+
}
|
|
113
|
+
checks.helpers = true
|
|
114
|
+
} catch (err) {
|
|
115
|
+
checks.helpers = err
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
printCheck('helpers', checks['helpers'], `${Object.keys(helpers).join(', ')}`)
|
|
119
|
+
|
|
120
|
+
const pageObjects = Container.support()
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (Object.keys(pageObjects).length) {
|
|
124
|
+
for (const pageObject of Object.values(pageObjects)) {
|
|
125
|
+
pageObject.name
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
checks.pageObjects = true
|
|
129
|
+
} catch (err) {
|
|
130
|
+
checks.pageObjects = err
|
|
131
|
+
}
|
|
132
|
+
printCheck('page objects', checks['pageObjects'], `Total: ${Object.keys(pageObjects).length} support objects`)
|
|
133
|
+
|
|
134
|
+
checks.plugins = true // how to check plugins?
|
|
135
|
+
printCheck('plugins', checks['plugins'], Object.keys(Container.plugins()).join(', '))
|
|
136
|
+
|
|
137
|
+
if (Object.keys(helpers).length) {
|
|
138
|
+
const suite = Container.mocha().suite
|
|
139
|
+
const test = createTest('test', () => {})
|
|
140
|
+
checks.setup = true
|
|
141
|
+
for (const helper of Object.values(helpers)) {
|
|
142
|
+
try {
|
|
143
|
+
if (helper._beforeSuite) await helper._beforeSuite(suite)
|
|
144
|
+
if (helper._before) await helper._before(test)
|
|
145
|
+
} catch (err) {
|
|
146
|
+
err.message = `${helper.constructor.name} helper: ${err.message}`
|
|
147
|
+
if (checks.setup instanceof Error) err.message = `${err.message}\n\n${checks.setup?.message || ''}`.trim()
|
|
148
|
+
checks.setup = err
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
printCheck('Helpers Before', checks['setup'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Initializing browser' : '')
|
|
153
|
+
|
|
154
|
+
checks.teardown = true
|
|
155
|
+
for (const helper of Object.values(helpers).reverse()) {
|
|
156
|
+
try {
|
|
157
|
+
if (helper._passed) await helper._passed(test)
|
|
158
|
+
if (helper._after) await helper._after(test)
|
|
159
|
+
if (helper._finishTest) await helper._finishTest(suite)
|
|
160
|
+
if (helper._afterSuite) await helper._afterSuite(suite)
|
|
161
|
+
} catch (err) {
|
|
162
|
+
err.message = `${helper.constructor.name} helper: ${err.message}`
|
|
163
|
+
if (checks.teardown instanceof Error) err.message = `${err.message}\n\n${checks.teardown?.message || ''}`.trim()
|
|
164
|
+
checks.teardown = err
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
printCheck('Helpers After', checks['teardown'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Closing browser' : '')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
definitions(configFile, { dryRun: true })
|
|
173
|
+
checks.def = true
|
|
174
|
+
} catch (err) {
|
|
175
|
+
checks.def = err
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
printCheck('TypeScript Definitions', checks['def'])
|
|
179
|
+
|
|
180
|
+
output.print('')
|
|
181
|
+
|
|
182
|
+
if (!Object.values(checks).every(check => check === true)) {
|
|
183
|
+
output.error("Something went wrong. Checks didn't pass.")
|
|
184
|
+
output.print()
|
|
185
|
+
await getMachineInfo()
|
|
186
|
+
process.exit(1)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
output.print(output.styles.success('All checks passed'.toUpperCase()), 'Ready to run your tests 🚀')
|
|
190
|
+
process.exit(0)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function printCheck(name, value, comment = '') {
|
|
194
|
+
let status = ''
|
|
195
|
+
if (value == true) {
|
|
196
|
+
status += chalk.bold.green(figures.tick)
|
|
197
|
+
} else {
|
|
198
|
+
status += chalk.bold.red(figures.cross)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (value instanceof Error) {
|
|
202
|
+
comment = `${comment} ${chalk.red(value.message)}`.trim()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
output.print(status, name.toUpperCase(), chalk.dim(comment))
|
|
206
|
+
}
|
|
@@ -1,70 +1,75 @@
|
|
|
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 util from 'util'
|
|
7
|
-
|
|
8
|
-
import
|
|
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 util from 'util'
|
|
7
|
+
|
|
8
|
+
import output from '../output.js'
|
|
9
|
+
const { print, success, error } = output
|
|
10
|
+
import { fileExists } from '../utils.js'
|
|
11
|
+
import { getTestRoot } from './utils.js'
|
|
10
12
|
|
|
11
13
|
export default function (initPath) {
|
|
12
|
-
const testsPath = getTestRoot(initPath)
|
|
14
|
+
const testsPath = getTestRoot(initPath)
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
print()
|
|
17
|
+
print(` Welcome to ${colors.magenta.bold('CodeceptJS')} configuration migration tool`)
|
|
18
|
+
print(` It will help you switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format at ease`)
|
|
19
|
+
print()
|
|
18
20
|
|
|
19
21
|
if (!path) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
print('No config file is specified.')
|
|
23
|
+
print(`Test root is assumed to be ${colors.yellow.bold(testsPath)}`)
|
|
24
|
+
print('----------------------------------')
|
|
23
25
|
} else {
|
|
24
|
-
|
|
26
|
+
print(`Migrating ${colors.magenta.bold('.js')} config to ${colors.bold(testsPath)}`)
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
if (!fileExists(testsPath)) {
|
|
28
|
-
|
|
29
|
-
mkdirp.sync(testsPath)
|
|
30
|
+
print(`Directory ${testsPath} does not exist, creating...`)
|
|
31
|
+
mkdirp.sync(testsPath)
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
const configFile = path.join(testsPath, 'codecept.conf.js')
|
|
34
|
+
const configFile = path.join(testsPath, 'codecept.conf.js')
|
|
33
35
|
if (fileExists(configFile)) {
|
|
34
|
-
|
|
35
|
-
return
|
|
36
|
+
error(`Config is already created at ${configFile}`)
|
|
37
|
+
return
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
inquirer
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
inquirer
|
|
41
|
+
.prompt([
|
|
42
|
+
{
|
|
43
|
+
name: 'configFile',
|
|
44
|
+
type: 'confirm',
|
|
45
|
+
message: `Would you like to switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format?`,
|
|
46
|
+
default: true,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'delete',
|
|
50
|
+
type: 'confirm',
|
|
51
|
+
message: `Would you like to delete ${colors.cyan.bold('.json')} config format afterwards?`,
|
|
52
|
+
default: true,
|
|
53
|
+
},
|
|
54
|
+
])
|
|
55
|
+
.then(result => {
|
|
56
|
+
if (result.configFile) {
|
|
57
|
+
const jsonConfigFile = path.join(testsPath, 'codecept.js')
|
|
58
|
+
const config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'))
|
|
59
|
+
config.name = testsPath.split(path.sep).pop()
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const finish = () => {
|
|
62
|
+
fs.writeFileSync(configFile, `exports.config = ${util.inspect(config, false, 4, false)}`, 'utf-8')
|
|
63
|
+
success(`Config is successfully migrated at ${configFile}`)
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
if (result.delete) {
|
|
66
|
+
if (fileExists(jsonConfigFile)) {
|
|
67
|
+
fs.unlinkSync(jsonConfigFile)
|
|
68
|
+
success('JSON config file is deleted!')
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
});
|
|
72
|
+
finish()
|
|
73
|
+
}
|
|
74
|
+
})
|
|
70
75
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { getConfig, getTestRoot } from './utils.js';
|
|
4
|
-
import Codecept from '../codecept.js';
|
|
5
|
-
import container from '../container.js';
|
|
6
|
-
import * as output from '../output.js';
|
|
7
|
-
import plugin from '../plugin/standardActingHelpers.js';
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
8
3
|
|
|
9
|
-
|
|
4
|
+
import { getConfig, getTestRoot } from './utils.js'
|
|
5
|
+
import Codecept from '../codecept.js'
|
|
6
|
+
import container from '../container.js'
|
|
7
|
+
import output from '../output.js'
|
|
8
|
+
const actingHelpers = [...container.STANDARD_ACTING_HELPERS, 'REST']
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Prepare data and generate content of definitions file
|
|
@@ -22,40 +21,28 @@ const actingHelpers = [...(plugin), 'REST'];
|
|
|
22
21
|
*
|
|
23
22
|
* @returns {string}
|
|
24
23
|
*/
|
|
25
|
-
const getDefinitionsFileContent = ({
|
|
26
|
-
hasCustomHelper,
|
|
27
|
-
hasCustomStepsFile,
|
|
28
|
-
helperNames,
|
|
29
|
-
supportObject,
|
|
30
|
-
importPaths,
|
|
31
|
-
translations,
|
|
32
|
-
}) => {
|
|
33
|
-
const getHelperListFragment = ({
|
|
34
|
-
hasCustomHelper,
|
|
35
|
-
hasCustomStepsFile,
|
|
36
|
-
}) => {
|
|
24
|
+
const getDefinitionsFileContent = ({ hasCustomHelper, hasCustomStepsFile, helperNames, supportObject, importPaths, translations }) => {
|
|
25
|
+
const getHelperListFragment = ({ hasCustomHelper, hasCustomStepsFile }) => {
|
|
37
26
|
if (hasCustomHelper && hasCustomStepsFile) {
|
|
38
|
-
return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}
|
|
27
|
+
return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}`
|
|
39
28
|
}
|
|
40
29
|
|
|
41
30
|
if (hasCustomStepsFile) {
|
|
42
|
-
return 'ReturnType<steps_file>'
|
|
31
|
+
return 'ReturnType<steps_file>'
|
|
43
32
|
}
|
|
44
33
|
|
|
45
|
-
return 'WithTranslation<Methods>'
|
|
46
|
-
}
|
|
34
|
+
return 'WithTranslation<Methods>'
|
|
35
|
+
}
|
|
47
36
|
|
|
48
37
|
const helpersListFragment = getHelperListFragment({
|
|
49
38
|
hasCustomHelper,
|
|
50
39
|
hasCustomStepsFile,
|
|
51
|
-
})
|
|
40
|
+
})
|
|
52
41
|
|
|
53
|
-
const importPathsFragment = importPaths.join('\n')
|
|
54
|
-
const supportObjectsTypeFragment = convertMapToType(supportObject)
|
|
55
|
-
const methodsTypeFragment = helperNames.length > 0
|
|
56
|
-
|
|
57
|
-
: '';
|
|
58
|
-
const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2);
|
|
42
|
+
const importPathsFragment = importPaths.join('\n')
|
|
43
|
+
const supportObjectsTypeFragment = convertMapToType(supportObject)
|
|
44
|
+
const methodsTypeFragment = helperNames.length > 0 ? `interface Methods extends ${helperNames.join(', ')} {}` : ''
|
|
45
|
+
const translatedActionsFragment = JSON.stringify(translations.vocabulary.actions, null, 2)
|
|
59
46
|
|
|
60
47
|
return generateDefinitionsContent({
|
|
61
48
|
helpersListFragment,
|
|
@@ -63,8 +50,8 @@ const getDefinitionsFileContent = ({
|
|
|
63
50
|
supportObjectsTypeFragment,
|
|
64
51
|
methodsTypeFragment,
|
|
65
52
|
translatedActionsFragment,
|
|
66
|
-
})
|
|
67
|
-
}
|
|
53
|
+
})
|
|
54
|
+
}
|
|
68
55
|
|
|
69
56
|
/**
|
|
70
57
|
* Generate content for definitions file from fragments
|
|
@@ -79,13 +66,7 @@ const getDefinitionsFileContent = ({
|
|
|
79
66
|
*
|
|
80
67
|
* @returns {string}
|
|
81
68
|
*/
|
|
82
|
-
const generateDefinitionsContent = ({
|
|
83
|
-
importPathsFragment,
|
|
84
|
-
supportObjectsTypeFragment,
|
|
85
|
-
methodsTypeFragment,
|
|
86
|
-
helpersListFragment,
|
|
87
|
-
translatedActionsFragment,
|
|
88
|
-
}) => {
|
|
69
|
+
const generateDefinitionsContent = ({ importPathsFragment, supportObjectsTypeFragment, methodsTypeFragment, helpersListFragment, translatedActionsFragment }) => {
|
|
89
70
|
return `/// <reference types='codeceptjs' />
|
|
90
71
|
${importPathsFragment}
|
|
91
72
|
|
|
@@ -97,83 +78,84 @@ declare namespace CodeceptJS {
|
|
|
97
78
|
interface Actions ${translatedActionsFragment}
|
|
98
79
|
}
|
|
99
80
|
}
|
|
100
|
-
|
|
101
|
-
}
|
|
81
|
+
`
|
|
82
|
+
}
|
|
102
83
|
|
|
103
84
|
/** @type {Array<string>} */
|
|
104
|
-
const helperNames = []
|
|
85
|
+
const helperNames = []
|
|
105
86
|
/** @type {Array<string>} */
|
|
106
|
-
const customHelpers = []
|
|
87
|
+
const customHelpers = []
|
|
107
88
|
|
|
108
|
-
export default function (genPath, options) {
|
|
109
|
-
const configFile = options.config || genPath
|
|
89
|
+
export default async function (genPath, options) {
|
|
90
|
+
const configFile = options.config || genPath
|
|
110
91
|
/** @type {string} */
|
|
111
|
-
const testsPath = getTestRoot(configFile)
|
|
112
|
-
const config = getConfig(configFile)
|
|
113
|
-
if (!config) return
|
|
92
|
+
const testsPath = getTestRoot(configFile)
|
|
93
|
+
const config = await getConfig(configFile)
|
|
94
|
+
if (!config) return
|
|
114
95
|
|
|
115
96
|
/** @type {Object<string, string>} */
|
|
116
|
-
const helperPaths = {}
|
|
97
|
+
const helperPaths = {}
|
|
117
98
|
/** @type {Object<string, string>} */
|
|
118
|
-
const supportPaths = {}
|
|
99
|
+
const supportPaths = {}
|
|
119
100
|
/** @type {boolean} */
|
|
120
|
-
let hasCustomStepsFile = false
|
|
101
|
+
let hasCustomStepsFile = false
|
|
121
102
|
/** @type {boolean} */
|
|
122
|
-
let hasCustomHelper = false
|
|
103
|
+
let hasCustomHelper = false
|
|
123
104
|
|
|
124
105
|
/** @type {string} */
|
|
125
|
-
const targetFolderPath = options.output && getTestRoot(options.output) || testsPath
|
|
106
|
+
const targetFolderPath = (options.output && getTestRoot(options.output)) || testsPath
|
|
126
107
|
|
|
127
|
-
const codecept = new Codecept(config, {})
|
|
128
|
-
codecept.init(testsPath)
|
|
108
|
+
const codecept = new Codecept(config, {})
|
|
109
|
+
await codecept.init(testsPath)
|
|
110
|
+
await container.started()
|
|
129
111
|
|
|
130
|
-
const helpers = container.helpers()
|
|
131
|
-
const translations = container.translation()
|
|
112
|
+
const helpers = container.helpers()
|
|
113
|
+
const translations = container.translation()
|
|
132
114
|
for (const name in helpers) {
|
|
133
|
-
const require = codecept.config.helpers[name].require
|
|
115
|
+
const require = codecept.config.helpers[name].require
|
|
134
116
|
if (require) {
|
|
135
|
-
helperPaths[name] = require
|
|
136
|
-
helperNames.push(name)
|
|
117
|
+
helperPaths[name] = require
|
|
118
|
+
helperNames.push(name)
|
|
137
119
|
} else {
|
|
138
|
-
const fullBasedPromised = codecept.config.fullPromiseBased
|
|
139
|
-
helperNames.push(fullBasedPromised === true ? `${name}Ts` : name)
|
|
120
|
+
const fullBasedPromised = codecept.config.fullPromiseBased
|
|
121
|
+
helperNames.push(fullBasedPromised === true ? `${name}Ts` : name)
|
|
140
122
|
}
|
|
141
123
|
|
|
142
124
|
if (!actingHelpers.includes(name)) {
|
|
143
|
-
customHelpers.push(name)
|
|
125
|
+
customHelpers.push(name)
|
|
144
126
|
}
|
|
145
127
|
}
|
|
146
128
|
|
|
147
|
-
let autoLogin
|
|
129
|
+
let autoLogin
|
|
148
130
|
if (config.plugins.autoLogin) {
|
|
149
|
-
autoLogin = config.plugins.autoLogin.inject
|
|
131
|
+
autoLogin = config.plugins.autoLogin.inject
|
|
150
132
|
}
|
|
151
133
|
|
|
152
|
-
const supportObject = new Map()
|
|
153
|
-
supportObject.set('I', 'I')
|
|
154
|
-
supportObject.set('current', 'any')
|
|
134
|
+
const supportObject = new Map()
|
|
135
|
+
supportObject.set('I', 'I')
|
|
136
|
+
supportObject.set('current', 'any')
|
|
155
137
|
|
|
156
138
|
if (translations.loaded) {
|
|
157
|
-
supportObject.set(translations.I, translations.I)
|
|
139
|
+
supportObject.set(translations.I, translations.I)
|
|
158
140
|
}
|
|
159
141
|
|
|
160
142
|
if (autoLogin) {
|
|
161
|
-
supportObject.set(autoLogin, 'any')
|
|
143
|
+
supportObject.set(autoLogin, 'any')
|
|
162
144
|
}
|
|
163
145
|
|
|
164
146
|
if (customHelpers.length > 0) {
|
|
165
|
-
hasCustomHelper = true
|
|
147
|
+
hasCustomHelper = true
|
|
166
148
|
}
|
|
167
149
|
|
|
168
150
|
for (const name in codecept.config.include) {
|
|
169
|
-
const includePath = codecept.config.include[name]
|
|
151
|
+
const includePath = codecept.config.include[name]
|
|
170
152
|
if (name === 'I' || name === translations.I) {
|
|
171
|
-
hasCustomStepsFile = true
|
|
172
|
-
supportPaths.steps_file = includePath
|
|
173
|
-
continue
|
|
153
|
+
hasCustomStepsFile = true
|
|
154
|
+
supportPaths.steps_file = includePath
|
|
155
|
+
continue
|
|
174
156
|
}
|
|
175
|
-
supportPaths[name] = includePath
|
|
176
|
-
supportObject.set(name, name)
|
|
157
|
+
supportPaths[name] = includePath
|
|
158
|
+
supportObject.set(name, name)
|
|
177
159
|
}
|
|
178
160
|
|
|
179
161
|
let definitionsFileContent = getDefinitionsFileContent({
|
|
@@ -183,30 +165,32 @@ export default function (genPath, options) {
|
|
|
183
165
|
translations,
|
|
184
166
|
hasCustomStepsFile,
|
|
185
167
|
hasCustomHelper,
|
|
186
|
-
})
|
|
168
|
+
})
|
|
187
169
|
|
|
188
170
|
// add aliases for translations
|
|
189
171
|
if (translations.loaded) {
|
|
190
|
-
const namespaceTranslationAliases = []
|
|
191
|
-
namespaceTranslationAliases.push(`interface ${translations.vocabulary.I} extends WithTranslation<Methods> {}`)
|
|
172
|
+
const namespaceTranslationAliases = []
|
|
173
|
+
namespaceTranslationAliases.push(`interface ${translations.vocabulary.I} extends WithTranslation<Methods> {}`)
|
|
192
174
|
|
|
193
|
-
namespaceTranslationAliases.push(' namespace Translation {')
|
|
194
|
-
definitionsFileContent = definitionsFileContent.replace('namespace Translation {', namespaceTranslationAliases.join('\n'))
|
|
175
|
+
namespaceTranslationAliases.push(' namespace Translation {')
|
|
176
|
+
definitionsFileContent = definitionsFileContent.replace('namespace Translation {', namespaceTranslationAliases.join('\n'))
|
|
195
177
|
|
|
196
|
-
const translationAliases = []
|
|
178
|
+
const translationAliases = []
|
|
197
179
|
|
|
198
180
|
if (translations.vocabulary.contexts) {
|
|
199
181
|
Object.keys(translations.vocabulary.contexts).forEach(k => {
|
|
200
|
-
translationAliases.push(`declare const ${translations.vocabulary.contexts[k]}: typeof ${k};`)
|
|
201
|
-
})
|
|
182
|
+
translationAliases.push(`declare const ${translations.vocabulary.contexts[k]}: typeof ${k};`)
|
|
183
|
+
})
|
|
202
184
|
}
|
|
203
185
|
|
|
204
|
-
definitionsFileContent += `\n${translationAliases.join('\n')}
|
|
186
|
+
definitionsFileContent += `\n${translationAliases.join('\n')}`
|
|
205
187
|
}
|
|
206
188
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
189
|
+
if (options.dryRun) return
|
|
190
|
+
|
|
191
|
+
fs.writeFileSync(path.join(targetFolderPath, 'steps.d.ts'), definitionsFileContent)
|
|
192
|
+
output.print('TypeScript Definitions provide autocompletion in Visual Studio Code and other IDEs')
|
|
193
|
+
output.print('Definitions were generated in steps.d.ts')
|
|
210
194
|
}
|
|
211
195
|
|
|
212
196
|
/**
|
|
@@ -216,23 +200,19 @@ export default function (genPath, options) {
|
|
|
216
200
|
* @param {string} testsPath
|
|
217
201
|
*/
|
|
218
202
|
function getPath(originalPath, targetFolderPath, testsPath) {
|
|
219
|
-
const parsedPath = path.parse(originalPath)
|
|
203
|
+
const parsedPath = path.parse(originalPath)
|
|
220
204
|
|
|
221
205
|
// Remove typescript extension if exists.
|
|
222
|
-
if (parsedPath.base.endsWith('.d.ts')) parsedPath.base = parsedPath.base.substring(0, parsedPath.base.length - 5)
|
|
223
|
-
else if (parsedPath.ext === '.ts') parsedPath.base = parsedPath.name
|
|
206
|
+
if (parsedPath.base.endsWith('.d.ts')) parsedPath.base = parsedPath.base.substring(0, parsedPath.base.length - 5)
|
|
207
|
+
else if (parsedPath.ext === '.ts') parsedPath.base = parsedPath.name
|
|
224
208
|
|
|
225
|
-
if (!parsedPath.dir.startsWith('.')) return path.posix.join(parsedPath.dir, parsedPath.base)
|
|
209
|
+
if (!parsedPath.dir.startsWith('.')) return path.posix.join(parsedPath.dir, parsedPath.base)
|
|
226
210
|
const relativePath = path.posix.relative(
|
|
227
211
|
targetFolderPath.split(path.sep).join(path.posix.sep),
|
|
228
|
-
path.posix.join(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
),
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
|
|
212
|
+
path.posix.join(testsPath.split(path.sep).join(path.posix.sep), parsedPath.dir.split(path.sep).join(path.posix.sep), parsedPath.base.split(path.sep).join(path.posix.sep)),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
|
|
236
216
|
}
|
|
237
217
|
|
|
238
218
|
/**
|
|
@@ -246,19 +226,24 @@ function getPath(originalPath, targetFolderPath, testsPath) {
|
|
|
246
226
|
* @returns {Array<string>}
|
|
247
227
|
*/
|
|
248
228
|
function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue) {
|
|
249
|
-
const importStrings = []
|
|
229
|
+
const importStrings = []
|
|
250
230
|
|
|
251
231
|
for (const name in pathsToType) {
|
|
252
|
-
const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath)
|
|
253
|
-
|
|
232
|
+
const relativePath = getPath(pathsToType[name], targetFolderPath, testsPath)
|
|
233
|
+
// For ESM modules with default exports, we need to access the default export type
|
|
234
|
+
if (relativePath.endsWith('.js')) {
|
|
235
|
+
importStrings.push(`type ${name} = typeof import('${relativePath}')['default'];`)
|
|
236
|
+
} else {
|
|
237
|
+
importStrings.push(`type ${name} = typeof import('${relativePath}');`)
|
|
238
|
+
}
|
|
254
239
|
}
|
|
255
240
|
|
|
256
241
|
for (const name in pathsToValue) {
|
|
257
|
-
const relativePath = getPath(pathsToValue[name], targetFolderPath, testsPath)
|
|
258
|
-
importStrings.push(`type ${name} = import('${relativePath}');`)
|
|
242
|
+
const relativePath = getPath(pathsToValue[name], targetFolderPath, testsPath)
|
|
243
|
+
importStrings.push(`type ${name} = import('${relativePath}');`)
|
|
259
244
|
}
|
|
260
245
|
|
|
261
|
-
return importStrings
|
|
246
|
+
return importStrings
|
|
262
247
|
}
|
|
263
248
|
|
|
264
249
|
/**
|
|
@@ -267,5 +252,7 @@ function getImportString(testsPath, targetFolderPath, pathsToType, pathsToValue)
|
|
|
267
252
|
* @returns {string}
|
|
268
253
|
*/
|
|
269
254
|
function convertMapToType(map) {
|
|
270
|
-
return `{ ${Array.from(map)
|
|
255
|
+
return `{ ${Array.from(map)
|
|
256
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
257
|
+
.join(', ')} }`
|
|
271
258
|
}
|