codeceptjs 3.6.10 → 3.7.0-beta.10
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 +9 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +66 -102
- package/lib/ai.js +130 -121
- package/lib/assert/empty.js +3 -5
- package/lib/assert/equal.js +4 -7
- package/lib/assert/include.js +4 -6
- package/lib/assert/throws.js +2 -4
- package/lib/assert/truth.js +2 -2
- package/lib/codecept.js +87 -83
- package/lib/command/check.js +186 -0
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +8 -26
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +10 -8
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +1 -3
- package/lib/command/init.js +8 -12
- package/lib/command/interactive.js +2 -2
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +5 -57
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +68 -232
- package/lib/container.js +354 -237
- package/lib/data/context.js +10 -13
- package/lib/data/dataScenarioConfig.js +8 -8
- package/lib/data/dataTableArgument.js +6 -6
- package/lib/data/table.js +5 -11
- package/lib/effects.js +218 -0
- package/lib/els.js +158 -0
- package/lib/event.js +19 -17
- package/lib/heal.js +88 -80
- package/lib/helper/AI.js +2 -1
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +45 -51
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +57 -37
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +211 -252
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +139 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +131 -169
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/emptyRun.js +55 -0
- package/lib/listener/exit.js +7 -10
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/globalTimeout.js +165 -0
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/result.js +12 -0
- package/lib/listener/steps.js +20 -18
- package/lib/listener/store.js +20 -0
- package/lib/mocha/asyncWrapper.js +216 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +308 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +24 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +112 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +29 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +21 -6
- package/lib/mocha/suite.js +81 -0
- package/lib/mocha/test.js +159 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +82 -62
- package/lib/pause.js +155 -138
- package/lib/plugin/analyze.js +349 -0
- package/lib/plugin/autoDelay.js +6 -6
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +6 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +36 -9
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +18 -118
- package/lib/plugin/screenshotOnFail.js +17 -49
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/standardActingHelpers.js +4 -1
- package/lib/plugin/stepByStepReport.js +56 -17
- package/lib/plugin/stepTimeout.js +5 -12
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +17 -107
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +146 -125
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/secret.js +1 -1
- package/lib/step/base.js +228 -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 +21 -332
- package/lib/steps.js +50 -0
- package/lib/store.js +10 -2
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +66 -0
- package/lib/utils.js +317 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +259 -275
- package/package.json +56 -54
- package/typings/index.d.ts +175 -186
- package/typings/promiseBasedTypes.d.ts +164 -17
- package/typings/types.d.ts +284 -115
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/utils.js
CHANGED
|
@@ -1,104 +1,95 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const os = require('os')
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const os = require('os')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const chalk = require('chalk')
|
|
5
|
+
const getFunctionArguments = require('fn-args')
|
|
6
|
+
const deepClone = require('lodash.clonedeep')
|
|
7
|
+
const { convertColorToRGBA, isColorProperty } = require('./colorUtils')
|
|
8
|
+
const Fuse = require('fuse.js')
|
|
7
9
|
|
|
8
10
|
function deepMerge(target, source) {
|
|
9
|
-
const merge = require('lodash.merge')
|
|
10
|
-
return merge(target, source)
|
|
11
|
+
const merge = require('lodash.merge')
|
|
12
|
+
return merge(target, source)
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
module.exports.genTestId =
|
|
14
|
-
return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64')
|
|
15
|
-
|
|
16
|
-
};
|
|
15
|
+
module.exports.genTestId = test => {
|
|
16
|
+
return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64').slice(0, -2)
|
|
17
|
+
}
|
|
17
18
|
|
|
18
|
-
module.exports.deepMerge = deepMerge
|
|
19
|
+
module.exports.deepMerge = deepMerge
|
|
19
20
|
|
|
20
|
-
module.exports.deepClone = deepClone
|
|
21
|
+
module.exports.deepClone = deepClone
|
|
21
22
|
|
|
22
23
|
module.exports.isGenerator = function (fn) {
|
|
23
|
-
return fn.constructor.name === 'GeneratorFunction'
|
|
24
|
-
}
|
|
24
|
+
return fn.constructor.name === 'GeneratorFunction'
|
|
25
|
+
}
|
|
25
26
|
|
|
26
|
-
const isFunction = module.exports.isFunction = function (fn) {
|
|
27
|
-
return typeof fn === 'function'
|
|
28
|
-
}
|
|
27
|
+
const isFunction = (module.exports.isFunction = function (fn) {
|
|
28
|
+
return typeof fn === 'function'
|
|
29
|
+
})
|
|
29
30
|
|
|
30
|
-
const isAsyncFunction = module.exports.isAsyncFunction = function (fn) {
|
|
31
|
-
if (!fn) return false
|
|
32
|
-
return fn[Symbol.toStringTag] === 'AsyncFunction'
|
|
33
|
-
}
|
|
31
|
+
const isAsyncFunction = (module.exports.isAsyncFunction = function (fn) {
|
|
32
|
+
if (!fn) return false
|
|
33
|
+
return fn[Symbol.toStringTag] === 'AsyncFunction'
|
|
34
|
+
})
|
|
34
35
|
|
|
35
36
|
module.exports.fileExists = function (filePath) {
|
|
36
|
-
return fs.existsSync(filePath)
|
|
37
|
-
}
|
|
37
|
+
return fs.existsSync(filePath)
|
|
38
|
+
}
|
|
38
39
|
|
|
39
40
|
module.exports.isFile = function (filePath) {
|
|
40
|
-
let filestat
|
|
41
|
+
let filestat
|
|
41
42
|
try {
|
|
42
|
-
filestat = fs.statSync(filePath)
|
|
43
|
+
filestat = fs.statSync(filePath)
|
|
43
44
|
} catch (err) {
|
|
44
|
-
if (err.code === 'ENOENT') return false
|
|
45
|
+
if (err.code === 'ENOENT') return false
|
|
45
46
|
}
|
|
46
|
-
if (!filestat) return false
|
|
47
|
-
return filestat.isFile()
|
|
48
|
-
}
|
|
47
|
+
if (!filestat) return false
|
|
48
|
+
return filestat.isFile()
|
|
49
|
+
}
|
|
49
50
|
|
|
50
51
|
module.exports.getParamNames = function (fn) {
|
|
51
|
-
if (fn.isSinonProxy) return []
|
|
52
|
-
return getFunctionArguments(fn)
|
|
53
|
-
}
|
|
52
|
+
if (fn.isSinonProxy) return []
|
|
53
|
+
return getFunctionArguments(fn)
|
|
54
|
+
}
|
|
54
55
|
|
|
55
56
|
module.exports.installedLocally = function () {
|
|
56
|
-
return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0
|
|
57
|
-
}
|
|
57
|
+
return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0
|
|
58
|
+
}
|
|
58
59
|
|
|
59
60
|
module.exports.methodsOfObject = function (obj, className) {
|
|
60
|
-
const methods = []
|
|
61
|
-
|
|
62
|
-
const standard = [
|
|
63
|
-
'constructor',
|
|
64
|
-
'toString',
|
|
65
|
-
'toLocaleString',
|
|
66
|
-
'valueOf',
|
|
67
|
-
'hasOwnProperty',
|
|
68
|
-
'bind',
|
|
69
|
-
'apply',
|
|
70
|
-
'call',
|
|
71
|
-
'isPrototypeOf',
|
|
72
|
-
'propertyIsEnumerable',
|
|
73
|
-
];
|
|
61
|
+
const methods = []
|
|
62
|
+
|
|
63
|
+
const standard = ['constructor', 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'bind', 'apply', 'call', 'isPrototypeOf', 'propertyIsEnumerable']
|
|
74
64
|
|
|
75
65
|
function pushToMethods(prop) {
|
|
76
66
|
try {
|
|
77
|
-
if (!isFunction(obj[prop]) && !isAsyncFunction(obj[prop])) return
|
|
78
|
-
} catch (err) {
|
|
79
|
-
|
|
67
|
+
if (!isFunction(obj[prop]) && !isAsyncFunction(obj[prop])) return
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// can't access property
|
|
70
|
+
return
|
|
80
71
|
}
|
|
81
|
-
if (standard.indexOf(prop) >= 0) return
|
|
82
|
-
if (prop.indexOf('_') === 0) return
|
|
83
|
-
methods.push(prop)
|
|
72
|
+
if (standard.indexOf(prop) >= 0) return
|
|
73
|
+
if (prop.indexOf('_') === 0) return
|
|
74
|
+
methods.push(prop)
|
|
84
75
|
}
|
|
85
76
|
|
|
86
77
|
while (obj.constructor.name !== className) {
|
|
87
|
-
Object.getOwnPropertyNames(obj).forEach(pushToMethods)
|
|
88
|
-
obj = Object.getPrototypeOf(obj)
|
|
78
|
+
Object.getOwnPropertyNames(obj).forEach(pushToMethods)
|
|
79
|
+
obj = Object.getPrototypeOf(obj)
|
|
89
80
|
|
|
90
|
-
if (!obj || !obj.constructor) break
|
|
81
|
+
if (!obj || !obj.constructor) break
|
|
91
82
|
}
|
|
92
|
-
return methods
|
|
93
|
-
}
|
|
83
|
+
return methods
|
|
84
|
+
}
|
|
94
85
|
|
|
95
86
|
module.exports.template = function (template, data) {
|
|
96
87
|
return template.replace(/{{([^{}]*)}}/g, (a, b) => {
|
|
97
|
-
const r = data[b]
|
|
98
|
-
if (r === undefined) return ''
|
|
99
|
-
return r.toString()
|
|
100
|
-
})
|
|
101
|
-
}
|
|
88
|
+
const r = data[b]
|
|
89
|
+
if (r === undefined) return ''
|
|
90
|
+
return r.toString()
|
|
91
|
+
})
|
|
92
|
+
}
|
|
102
93
|
|
|
103
94
|
/**
|
|
104
95
|
* Make first char uppercase.
|
|
@@ -106,8 +97,8 @@ module.exports.template = function (template, data) {
|
|
|
106
97
|
* @returns {string}
|
|
107
98
|
*/
|
|
108
99
|
module.exports.ucfirst = function (str) {
|
|
109
|
-
return str.charAt(0).toUpperCase() + str.substr(1)
|
|
110
|
-
}
|
|
100
|
+
return str.charAt(0).toUpperCase() + str.substr(1)
|
|
101
|
+
}
|
|
111
102
|
|
|
112
103
|
/**
|
|
113
104
|
* Make first char lowercase.
|
|
@@ -115,25 +106,25 @@ module.exports.ucfirst = function (str) {
|
|
|
115
106
|
* @returns {string}
|
|
116
107
|
*/
|
|
117
108
|
module.exports.lcfirst = function (str) {
|
|
118
|
-
return str.charAt(0).toLowerCase() + str.substr(1)
|
|
119
|
-
}
|
|
109
|
+
return str.charAt(0).toLowerCase() + str.substr(1)
|
|
110
|
+
}
|
|
120
111
|
|
|
121
112
|
module.exports.chunkArray = function (arr, chunk) {
|
|
122
|
-
let i
|
|
123
|
-
let j
|
|
124
|
-
const tmp = []
|
|
113
|
+
let i
|
|
114
|
+
let j
|
|
115
|
+
const tmp = []
|
|
125
116
|
for (i = 0, j = arr.length; i < j; i += chunk) {
|
|
126
|
-
tmp.push(arr.slice(i, i + chunk))
|
|
117
|
+
tmp.push(arr.slice(i, i + chunk))
|
|
127
118
|
}
|
|
128
|
-
return tmp
|
|
129
|
-
}
|
|
119
|
+
return tmp
|
|
120
|
+
}
|
|
130
121
|
|
|
131
122
|
module.exports.clearString = function (str) {
|
|
132
|
-
if (!str) return ''
|
|
123
|
+
if (!str) return ''
|
|
133
124
|
/* Replace forbidden symbols in string
|
|
134
125
|
*/
|
|
135
126
|
if (str.endsWith('.')) {
|
|
136
|
-
str = str.slice(0, -1)
|
|
127
|
+
str = str.slice(0, -1)
|
|
137
128
|
}
|
|
138
129
|
return str
|
|
139
130
|
.replace(/ /g, '_')
|
|
@@ -146,26 +137,29 @@ module.exports.clearString = function (str) {
|
|
|
146
137
|
.replace(/\|/g, '_')
|
|
147
138
|
.replace(/\?/g, '.')
|
|
148
139
|
.replace(/\*/g, '^')
|
|
149
|
-
.replace(/'/g, '')
|
|
150
|
-
}
|
|
140
|
+
.replace(/'/g, '')
|
|
141
|
+
}
|
|
151
142
|
|
|
152
143
|
module.exports.decodeUrl = function (url) {
|
|
153
144
|
/* Replace forbidden symbols in string
|
|
154
145
|
*/
|
|
155
|
-
return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)))
|
|
156
|
-
}
|
|
146
|
+
return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)))
|
|
147
|
+
}
|
|
157
148
|
|
|
158
149
|
module.exports.xpathLocator = {
|
|
159
150
|
/**
|
|
160
151
|
* @param {string} string
|
|
161
152
|
* @returns {string}
|
|
162
153
|
*/
|
|
163
|
-
literal:
|
|
154
|
+
literal: string => {
|
|
164
155
|
if (string.indexOf("'") > -1) {
|
|
165
|
-
string = string
|
|
166
|
-
|
|
156
|
+
string = string
|
|
157
|
+
.split("'", -1)
|
|
158
|
+
.map(substr => `'${substr}'`)
|
|
159
|
+
.join(',"\'",')
|
|
160
|
+
return `concat(${string})`
|
|
167
161
|
}
|
|
168
|
-
return `'${string}'
|
|
162
|
+
return `'${string}'`
|
|
169
163
|
},
|
|
170
164
|
|
|
171
165
|
/**
|
|
@@ -174,55 +168,53 @@ module.exports.xpathLocator = {
|
|
|
174
168
|
* @returns {string}
|
|
175
169
|
*/
|
|
176
170
|
combine: locators => locators.join(' | '),
|
|
177
|
-
}
|
|
171
|
+
}
|
|
178
172
|
|
|
179
173
|
module.exports.test = {
|
|
180
|
-
|
|
181
174
|
grepLines(array, startString, endString) {
|
|
182
|
-
let startIndex = 0
|
|
183
|
-
let endIndex
|
|
175
|
+
let startIndex = 0
|
|
176
|
+
let endIndex
|
|
184
177
|
array.every((elem, index) => {
|
|
185
178
|
if (elem === startString) {
|
|
186
|
-
startIndex = index
|
|
187
|
-
return true
|
|
179
|
+
startIndex = index
|
|
180
|
+
return true
|
|
188
181
|
}
|
|
189
182
|
if (elem === endString) {
|
|
190
|
-
endIndex = index
|
|
191
|
-
return false
|
|
183
|
+
endIndex = index
|
|
184
|
+
return false
|
|
192
185
|
}
|
|
193
|
-
return true
|
|
194
|
-
})
|
|
195
|
-
return array.slice(startIndex + 1, endIndex)
|
|
186
|
+
return true
|
|
187
|
+
})
|
|
188
|
+
return array.slice(startIndex + 1, endIndex)
|
|
196
189
|
},
|
|
197
190
|
|
|
198
191
|
submittedData(dataFile) {
|
|
199
192
|
return function (key) {
|
|
200
193
|
if (!fs.existsSync(dataFile)) {
|
|
201
|
-
const waitTill = new Date(new Date().getTime() + 1 * 1000)
|
|
202
|
-
while (waitTill > new Date()) {}
|
|
194
|
+
const waitTill = new Date(new Date().getTime() + 1 * 1000) // wait for one sec for file to be created
|
|
195
|
+
while (waitTill > new Date()) {}
|
|
203
196
|
}
|
|
204
197
|
if (!fs.existsSync(dataFile)) {
|
|
205
|
-
throw new Error('Data file was not created in time')
|
|
198
|
+
throw new Error('Data file was not created in time')
|
|
206
199
|
}
|
|
207
|
-
const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'))
|
|
200
|
+
const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'))
|
|
208
201
|
if (key) {
|
|
209
|
-
return data.form[key]
|
|
202
|
+
return data.form[key]
|
|
210
203
|
}
|
|
211
|
-
return data
|
|
212
|
-
}
|
|
204
|
+
return data
|
|
205
|
+
}
|
|
213
206
|
},
|
|
214
|
-
|
|
215
|
-
};
|
|
207
|
+
}
|
|
216
208
|
|
|
217
209
|
function toCamelCase(name) {
|
|
218
210
|
if (typeof name !== 'string') {
|
|
219
|
-
return name
|
|
211
|
+
return name
|
|
220
212
|
}
|
|
221
213
|
return name.replace(/-(\w)/gi, (_word, letter) => {
|
|
222
|
-
return letter.toUpperCase()
|
|
223
|
-
})
|
|
214
|
+
return letter.toUpperCase()
|
|
215
|
+
})
|
|
224
216
|
}
|
|
225
|
-
module.exports.toCamelCase = toCamelCase
|
|
217
|
+
module.exports.toCamelCase = toCamelCase
|
|
226
218
|
|
|
227
219
|
function convertFontWeightToNumber(name) {
|
|
228
220
|
const fontWeightPatterns = [
|
|
@@ -235,106 +227,110 @@ function convertFontWeightToNumber(name) {
|
|
|
235
227
|
{ num: 700, pattern: /^Bold$/i },
|
|
236
228
|
{ num: 800, pattern: /^(Extra|Ultra)-?bold$/i },
|
|
237
229
|
{ num: 900, pattern: /^(Black|Heavy)$/i },
|
|
238
|
-
]
|
|
230
|
+
]
|
|
239
231
|
|
|
240
232
|
if (/^[1-9]00$/.test(name)) {
|
|
241
|
-
return Number(name)
|
|
233
|
+
return Number(name)
|
|
242
234
|
}
|
|
243
235
|
|
|
244
|
-
const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name))
|
|
236
|
+
const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name))
|
|
245
237
|
|
|
246
238
|
if (matches.length) {
|
|
247
|
-
return String(matches[0].num)
|
|
239
|
+
return String(matches[0].num)
|
|
248
240
|
}
|
|
249
|
-
return name
|
|
241
|
+
return name
|
|
250
242
|
}
|
|
251
243
|
|
|
252
244
|
function isFontWeightProperty(prop) {
|
|
253
|
-
return prop === 'fontWeight'
|
|
245
|
+
return prop === 'fontWeight'
|
|
254
246
|
}
|
|
255
247
|
|
|
256
248
|
module.exports.convertCssPropertiesToCamelCase = function (props) {
|
|
257
|
-
const output = {}
|
|
258
|
-
Object.keys(props).forEach(
|
|
259
|
-
const keyCamel = toCamelCase(key)
|
|
249
|
+
const output = {}
|
|
250
|
+
Object.keys(props).forEach(key => {
|
|
251
|
+
const keyCamel = toCamelCase(key)
|
|
260
252
|
|
|
261
253
|
if (isFontWeightProperty(keyCamel)) {
|
|
262
|
-
output[keyCamel] = convertFontWeightToNumber(props[key])
|
|
254
|
+
output[keyCamel] = convertFontWeightToNumber(props[key])
|
|
263
255
|
} else if (isColorProperty(keyCamel)) {
|
|
264
|
-
output[keyCamel] = convertColorToRGBA(props[key])
|
|
256
|
+
output[keyCamel] = convertColorToRGBA(props[key])
|
|
265
257
|
} else {
|
|
266
|
-
output[keyCamel] = props[key]
|
|
258
|
+
output[keyCamel] = props[key]
|
|
267
259
|
}
|
|
268
|
-
})
|
|
269
|
-
return output
|
|
270
|
-
}
|
|
260
|
+
})
|
|
261
|
+
return output
|
|
262
|
+
}
|
|
271
263
|
|
|
272
264
|
module.exports.deleteDir = function (dir_path) {
|
|
273
265
|
if (fs.existsSync(dir_path)) {
|
|
274
266
|
fs.readdirSync(dir_path).forEach(function (entry) {
|
|
275
|
-
const entry_path = path.join(dir_path, entry)
|
|
267
|
+
const entry_path = path.join(dir_path, entry)
|
|
276
268
|
if (fs.lstatSync(entry_path).isDirectory()) {
|
|
277
|
-
this.deleteDir(entry_path)
|
|
269
|
+
this.deleteDir(entry_path)
|
|
278
270
|
} else {
|
|
279
|
-
fs.unlinkSync(entry_path)
|
|
271
|
+
fs.unlinkSync(entry_path)
|
|
280
272
|
}
|
|
281
|
-
})
|
|
282
|
-
fs.rmdirSync(dir_path)
|
|
273
|
+
})
|
|
274
|
+
fs.rmdirSync(dir_path)
|
|
283
275
|
}
|
|
284
|
-
}
|
|
276
|
+
}
|
|
285
277
|
|
|
286
278
|
/**
|
|
287
279
|
* Returns absolute filename to save screenshot.
|
|
288
280
|
* @param fileName {string} - filename.
|
|
289
281
|
*/
|
|
290
282
|
module.exports.screenshotOutputFolder = function (fileName) {
|
|
291
|
-
const fileSep = path.sep
|
|
283
|
+
const fileSep = path.sep
|
|
292
284
|
|
|
293
285
|
if (!fileName.includes(fileSep) || fileName.includes('record_')) {
|
|
294
|
-
return path.resolve(global.output_dir, fileName)
|
|
286
|
+
return path.resolve(global.output_dir, fileName)
|
|
295
287
|
}
|
|
296
|
-
return path.resolve(global.codecept_dir, fileName)
|
|
297
|
-
}
|
|
288
|
+
return path.resolve(global.codecept_dir, fileName)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports.relativeDir = function (fileName) {
|
|
292
|
+
return fileName.replace(global.codecept_dir, '').replace(/^\//, '')
|
|
293
|
+
}
|
|
298
294
|
|
|
299
295
|
module.exports.beautify = function (code) {
|
|
300
|
-
const format = require('js-beautify').js
|
|
301
|
-
return format(code, { indent_size: 2, space_in_empty_paren: true })
|
|
302
|
-
}
|
|
296
|
+
const format = require('js-beautify').js
|
|
297
|
+
return format(code, { indent_size: 2, space_in_empty_paren: true })
|
|
298
|
+
}
|
|
303
299
|
|
|
304
300
|
function shouldAppendBaseUrl(url) {
|
|
305
|
-
return !/^\w+\:\/\//.test(url)
|
|
301
|
+
return !/^\w+\:\/\//.test(url)
|
|
306
302
|
}
|
|
307
303
|
|
|
308
304
|
function trimUrl(url) {
|
|
309
|
-
const firstChar = url.substr(1)
|
|
305
|
+
const firstChar = url.substr(1)
|
|
310
306
|
if (firstChar === '/') {
|
|
311
|
-
url = url.slice(1)
|
|
307
|
+
url = url.slice(1)
|
|
312
308
|
}
|
|
313
|
-
return url
|
|
309
|
+
return url
|
|
314
310
|
}
|
|
315
311
|
|
|
316
312
|
function joinUrl(baseUrl, url) {
|
|
317
|
-
return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url
|
|
313
|
+
return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url
|
|
318
314
|
}
|
|
319
315
|
|
|
320
316
|
module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
|
|
321
317
|
if (typeof baseUrl !== 'string') {
|
|
322
|
-
throw new Error(`Invalid value for baseUrl: ${baseUrl}`)
|
|
318
|
+
throw new Error(`Invalid value for baseUrl: ${baseUrl}`)
|
|
323
319
|
}
|
|
324
320
|
if (!(typeof oneOrMoreUrls === 'string' || Array.isArray(oneOrMoreUrls))) {
|
|
325
|
-
throw new Error(`Expected type of Urls is 'string' or 'array', Found '${typeof oneOrMoreUrls}'.`)
|
|
321
|
+
throw new Error(`Expected type of Urls is 'string' or 'array', Found '${typeof oneOrMoreUrls}'.`)
|
|
326
322
|
}
|
|
327
323
|
// Remove '/' if it's at the end of baseUrl
|
|
328
|
-
const lastChar = baseUrl.substr(-1)
|
|
324
|
+
const lastChar = baseUrl.substr(-1)
|
|
329
325
|
if (lastChar === '/') {
|
|
330
|
-
baseUrl = baseUrl.slice(0, -1)
|
|
326
|
+
baseUrl = baseUrl.slice(0, -1)
|
|
331
327
|
}
|
|
332
328
|
|
|
333
329
|
if (!Array.isArray(oneOrMoreUrls)) {
|
|
334
|
-
return joinUrl(baseUrl, oneOrMoreUrls)
|
|
330
|
+
return joinUrl(baseUrl, oneOrMoreUrls)
|
|
335
331
|
}
|
|
336
|
-
return oneOrMoreUrls.map(url => joinUrl(baseUrl, url))
|
|
337
|
-
}
|
|
332
|
+
return oneOrMoreUrls.map(url => joinUrl(baseUrl, url))
|
|
333
|
+
}
|
|
338
334
|
|
|
339
335
|
/**
|
|
340
336
|
* Recursively search key in object and replace it's value.
|
|
@@ -344,56 +340,53 @@ module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
|
|
|
344
340
|
* @param {*} value value to set for key
|
|
345
341
|
*/
|
|
346
342
|
module.exports.replaceValueDeep = function replaceValueDeep(obj, key, value) {
|
|
347
|
-
if (!obj) return
|
|
343
|
+
if (!obj) return
|
|
348
344
|
|
|
349
345
|
if (obj instanceof Array) {
|
|
350
346
|
for (const i in obj) {
|
|
351
|
-
replaceValueDeep(obj[i], key, value)
|
|
347
|
+
replaceValueDeep(obj[i], key, value)
|
|
352
348
|
}
|
|
353
349
|
}
|
|
354
350
|
|
|
355
351
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
356
|
-
obj[key] = value
|
|
352
|
+
obj[key] = value
|
|
357
353
|
}
|
|
358
354
|
|
|
359
355
|
if (typeof obj === 'object' && obj !== null) {
|
|
360
|
-
const children = Object.values(obj)
|
|
356
|
+
const children = Object.values(obj)
|
|
361
357
|
for (const child of children) {
|
|
362
|
-
replaceValueDeep(child, key, value)
|
|
358
|
+
replaceValueDeep(child, key, value)
|
|
363
359
|
}
|
|
364
360
|
}
|
|
365
|
-
return obj
|
|
366
|
-
}
|
|
361
|
+
return obj
|
|
362
|
+
}
|
|
367
363
|
|
|
368
364
|
module.exports.ansiRegExp = function ({ onlyFirst = false } = {}) {
|
|
369
|
-
const pattern = [
|
|
370
|
-
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
371
|
-
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
|
|
372
|
-
].join('|');
|
|
365
|
+
const pattern = ['[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'].join('|')
|
|
373
366
|
|
|
374
|
-
return new RegExp(pattern, onlyFirst ? undefined : 'g')
|
|
375
|
-
}
|
|
367
|
+
return new RegExp(pattern, onlyFirst ? undefined : 'g')
|
|
368
|
+
}
|
|
376
369
|
|
|
377
370
|
module.exports.tryOrDefault = function (fn, defaultValue) {
|
|
378
371
|
try {
|
|
379
|
-
return fn()
|
|
372
|
+
return fn()
|
|
380
373
|
} catch (_) {
|
|
381
|
-
return defaultValue
|
|
374
|
+
return defaultValue
|
|
382
375
|
}
|
|
383
|
-
}
|
|
376
|
+
}
|
|
384
377
|
|
|
385
378
|
function normalizeKeyReplacer(match, prefix, key, suffix, offset, string) {
|
|
386
379
|
if (typeof key !== 'string') {
|
|
387
|
-
return string
|
|
380
|
+
return string
|
|
388
381
|
}
|
|
389
|
-
const normalizedKey = key.charAt(0).toUpperCase() + key.substr(1).toLowerCase()
|
|
390
|
-
let position = ''
|
|
382
|
+
const normalizedKey = key.charAt(0).toUpperCase() + key.substr(1).toLowerCase()
|
|
383
|
+
let position = ''
|
|
391
384
|
if (typeof prefix === 'string') {
|
|
392
|
-
position = prefix
|
|
385
|
+
position = prefix
|
|
393
386
|
} else if (typeof suffix === 'string') {
|
|
394
|
-
position = suffix
|
|
387
|
+
position = suffix
|
|
395
388
|
}
|
|
396
|
-
return normalizedKey + position.charAt(0).toUpperCase() + position.substr(1).toLowerCase()
|
|
389
|
+
return normalizedKey + position.charAt(0).toUpperCase() + position.substr(1).toLowerCase()
|
|
397
390
|
}
|
|
398
391
|
|
|
399
392
|
/**
|
|
@@ -403,76 +396,184 @@ function normalizeKeyReplacer(match, prefix, key, suffix, offset, string) {
|
|
|
403
396
|
*/
|
|
404
397
|
module.exports.getNormalizedKeyAttributeValue = function (key) {
|
|
405
398
|
// Use operation modifier key based on operating system
|
|
406
|
-
key = key.replace(/(Ctrl|Control|Cmd|Command)[ _]?Or[ _]?(Ctrl|Control|Cmd|Command)/i, os.platform() === 'darwin' ? 'Meta' : 'Control')
|
|
399
|
+
key = key.replace(/(Ctrl|Control|Cmd|Command)[ _]?Or[ _]?(Ctrl|Control|Cmd|Command)/i, os.platform() === 'darwin' ? 'Meta' : 'Control')
|
|
407
400
|
// Selection of keys (https://www.w3.org/TR/uievents-key/#named-key-attribute-values)
|
|
408
401
|
// which can be written in various ways and should be normalized.
|
|
409
402
|
// For example 'LEFT ALT', 'ALT_Left', 'alt left' or 'LeftAlt' will be normalized as 'AltLeft'.
|
|
410
|
-
key = key.replace(/^\s*(?:(Down|Left|Right|Up)[ _]?)?(Arrow|Alt|Ctrl|Control|Cmd|Command|Meta|Option|OS|Page|Shift|Super)(?:[ _]?(Down|Left|Right|Up|Gr(?:aph)?))?\s*$/i, normalizeKeyReplacer)
|
|
403
|
+
key = key.replace(/^\s*(?:(Down|Left|Right|Up)[ _]?)?(Arrow|Alt|Ctrl|Control|Cmd|Command|Meta|Option|OS|Page|Shift|Super)(?:[ _]?(Down|Left|Right|Up|Gr(?:aph)?))?\s*$/i, normalizeKeyReplacer)
|
|
411
404
|
// Map alias to corresponding key value
|
|
412
|
-
key = key.replace(/^(Add|Divide|Decimal|Multiply|Subtract)$/, 'Numpad$1')
|
|
413
|
-
key = key.replace(/^AltGr$/, 'AltGraph')
|
|
414
|
-
key = key.replace(/^(Cmd|Command|Os|Super)/, 'Meta')
|
|
415
|
-
key = key.replace('Ctrl', 'Control')
|
|
416
|
-
key = key.replace('Option', 'Alt')
|
|
417
|
-
key = key.replace(/^(NumpadComma|Separator)$/, 'Comma')
|
|
418
|
-
return key
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const modifierKeys = [
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
'Meta', 'MetaLeft', 'MetaRight',
|
|
425
|
-
'Shift', 'ShiftLeft', 'ShiftRight',
|
|
426
|
-
];
|
|
427
|
-
|
|
428
|
-
module.exports.modifierKeys = modifierKeys;
|
|
405
|
+
key = key.replace(/^(Add|Divide|Decimal|Multiply|Subtract)$/, 'Numpad$1')
|
|
406
|
+
key = key.replace(/^AltGr$/, 'AltGraph')
|
|
407
|
+
key = key.replace(/^(Cmd|Command|Os|Super)/, 'Meta')
|
|
408
|
+
key = key.replace('Ctrl', 'Control')
|
|
409
|
+
key = key.replace('Option', 'Alt')
|
|
410
|
+
key = key.replace(/^(NumpadComma|Separator)$/, 'Comma')
|
|
411
|
+
return key
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const modifierKeys = ['Alt', 'AltGraph', 'AltLeft', 'AltRight', 'Control', 'ControlLeft', 'ControlRight', 'Meta', 'MetaLeft', 'MetaRight', 'Shift', 'ShiftLeft', 'ShiftRight']
|
|
415
|
+
|
|
416
|
+
module.exports.modifierKeys = modifierKeys
|
|
429
417
|
module.exports.isModifierKey = function (key) {
|
|
430
|
-
return modifierKeys.includes(key)
|
|
431
|
-
}
|
|
418
|
+
return modifierKeys.includes(key)
|
|
419
|
+
}
|
|
432
420
|
|
|
433
421
|
module.exports.requireWithFallback = function (...packages) {
|
|
434
422
|
const exists = function (pkg) {
|
|
435
423
|
try {
|
|
436
|
-
require.resolve(pkg)
|
|
424
|
+
require.resolve(pkg)
|
|
437
425
|
} catch (e) {
|
|
438
|
-
return false
|
|
426
|
+
return false
|
|
439
427
|
}
|
|
440
428
|
|
|
441
|
-
return true
|
|
442
|
-
}
|
|
429
|
+
return true
|
|
430
|
+
}
|
|
443
431
|
|
|
444
432
|
for (const pkg of packages) {
|
|
445
433
|
if (exists(pkg)) {
|
|
446
|
-
return require(pkg)
|
|
434
|
+
return require(pkg)
|
|
447
435
|
}
|
|
448
436
|
}
|
|
449
437
|
|
|
450
|
-
throw new Error(`Cannot find modules ${packages.join(',')}`)
|
|
451
|
-
}
|
|
438
|
+
throw new Error(`Cannot find modules ${packages.join(',')}`)
|
|
439
|
+
}
|
|
452
440
|
|
|
453
441
|
module.exports.isNotSet = function (obj) {
|
|
454
|
-
if (obj === null) return true
|
|
455
|
-
if (obj === undefined) return true
|
|
456
|
-
return false
|
|
457
|
-
}
|
|
442
|
+
if (obj === null) return true
|
|
443
|
+
if (obj === undefined) return true
|
|
444
|
+
return false
|
|
445
|
+
}
|
|
458
446
|
|
|
459
|
-
module.exports.emptyFolder = async
|
|
460
|
-
require('child_process').execSync(`rm -rf ${directoryPath}/*`)
|
|
461
|
-
}
|
|
447
|
+
module.exports.emptyFolder = async directoryPath => {
|
|
448
|
+
require('child_process').execSync(`rm -rf ${directoryPath}/*`)
|
|
449
|
+
}
|
|
462
450
|
|
|
463
|
-
module.exports.printObjectProperties =
|
|
451
|
+
module.exports.printObjectProperties = obj => {
|
|
464
452
|
if (typeof obj !== 'object' || obj === null) {
|
|
465
|
-
return obj
|
|
453
|
+
return obj
|
|
466
454
|
}
|
|
467
455
|
|
|
468
|
-
let result = ''
|
|
456
|
+
let result = ''
|
|
469
457
|
for (const [key, value] of Object.entries(obj)) {
|
|
470
|
-
result += `${key}: "${value}";
|
|
458
|
+
result += `${key}: "${value}"; `
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return `{${result}}`
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
module.exports.normalizeSpacesInString = string => {
|
|
465
|
+
return string.replace(/\s+/g, ' ')
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
module.exports.humanizeFunction = function (fn) {
|
|
469
|
+
const fnStr = fn.toString().trim()
|
|
470
|
+
// Remove arrow function syntax, async, and parentheses
|
|
471
|
+
let simplified = fnStr
|
|
472
|
+
.replace(/^async\s*/, '')
|
|
473
|
+
.replace(/^\([^)]*\)\s*=>/, '')
|
|
474
|
+
.replace(/^function\s*\([^)]*\)/, '')
|
|
475
|
+
// Remove curly braces and any whitespace around them
|
|
476
|
+
.replace(/{\s*(.*)\s*}/, '$1')
|
|
477
|
+
// Remove return statement
|
|
478
|
+
.replace(/return\s+/, '')
|
|
479
|
+
// Remove trailing semicolon
|
|
480
|
+
.replace(/;$/, '')
|
|
481
|
+
.trim()
|
|
482
|
+
|
|
483
|
+
if (simplified.length > 100) {
|
|
484
|
+
simplified = simplified.slice(0, 97) + '...'
|
|
471
485
|
}
|
|
472
486
|
|
|
473
|
-
return
|
|
474
|
-
}
|
|
487
|
+
return simplified
|
|
488
|
+
}
|
|
475
489
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
490
|
+
/**
|
|
491
|
+
* Searches through a given data source using the Fuse.js library for fuzzy searching.
|
|
492
|
+
*
|
|
493
|
+
* @function searchWithFusejs
|
|
494
|
+
* @param {Array|Object} source - The data source to search through. This can be an array of objects or strings.
|
|
495
|
+
* @param {string} searchString - The search query string to match against the source.
|
|
496
|
+
* @param {Object} [opts] - Optional configuration object for Fuse.js.
|
|
497
|
+
* @param {boolean} [opts.includeScore=true] - Whether to include the score of the match in the results.
|
|
498
|
+
* @param {number} [opts.threshold=0.6] - Determines the match threshold; lower values mean stricter matching.
|
|
499
|
+
* @param {boolean} [opts.caseSensitive=false] - Whether the search should be case-sensitive.
|
|
500
|
+
* @param {number} [opts.distance=100] - Determines how far apart the search term is allowed to be from the target.
|
|
501
|
+
* @param {number} [opts.maxPatternLength=32] - The maximum length of the search pattern. Patterns longer than this are ignored.
|
|
502
|
+
* @param {boolean} [opts.ignoreLocation=false] - Whether the location of the match is ignored when scoring.
|
|
503
|
+
* @param {boolean} [opts.ignoreFieldNorm=false] - When true, the field's length is not considered when scoring.
|
|
504
|
+
* @param {Array<string>} [opts.keys=[]] - List of keys to search in the objects of the source array.
|
|
505
|
+
* @param {boolean} [opts.shouldSort=true] - Whether the results should be sorted by score.
|
|
506
|
+
* @param {string} [opts.sortFn] - A custom sorting function for sorting results.
|
|
507
|
+
* @param {number} [opts.minMatchCharLength=1] - The minimum number of characters that must match.
|
|
508
|
+
* @param {boolean} [opts.useExtendedSearch=false] - Enables extended search capabilities.
|
|
509
|
+
*
|
|
510
|
+
* @returns {Array<Object>} - An array of search results. Each result contains an item and, if `includeScore` is true, a score.
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* const data = [
|
|
514
|
+
* { title: "Old Man's War", author: "John Scalzi" },
|
|
515
|
+
* { title: "The Lock Artist", author: "Steve Hamilton" },
|
|
516
|
+
* ];
|
|
517
|
+
*
|
|
518
|
+
* const options = {
|
|
519
|
+
* keys: ['title', 'author'],
|
|
520
|
+
* includeScore: true,
|
|
521
|
+
* threshold: 0.4,
|
|
522
|
+
* caseSensitive: false,
|
|
523
|
+
* distance: 50,
|
|
524
|
+
* ignoreLocation: true,
|
|
525
|
+
* };
|
|
526
|
+
*
|
|
527
|
+
* const results = searchWithFusejs(data, 'lock', options);
|
|
528
|
+
* console.log(results);
|
|
529
|
+
*/
|
|
530
|
+
module.exports.searchWithFusejs = function (source, searchString, opts) {
|
|
531
|
+
const fuse = new Fuse(source, opts)
|
|
532
|
+
|
|
533
|
+
return fuse.search(searchString)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
module.exports.humanizeString = function (string) {
|
|
537
|
+
// split strings by words, then make them all lowercase
|
|
538
|
+
const _result = string
|
|
539
|
+
.replace(/([a-z](?=[A-Z]))/g, '$1 ')
|
|
540
|
+
.split(' ')
|
|
541
|
+
.map(word => word.toLowerCase())
|
|
542
|
+
|
|
543
|
+
_result[0] = _result[0] === 'i' ? this.ucfirst(_result[0]) : _result[0]
|
|
544
|
+
return _result.join(' ').trim()
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
module.exports.serializeError = function (error) {
|
|
548
|
+
if (error) {
|
|
549
|
+
const { stack, uncaught, message, actual, expected } = error
|
|
550
|
+
return { stack, uncaught, message, actual, expected }
|
|
551
|
+
}
|
|
552
|
+
return null
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
module.exports.base64EncodeFile = function (filePath) {
|
|
556
|
+
return Buffer.from(fs.readFileSync(filePath)).toString('base64')
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
module.exports.markdownToAnsi = function (markdown) {
|
|
560
|
+
return (
|
|
561
|
+
markdown
|
|
562
|
+
// Headers (# Text) - make blue and bold
|
|
563
|
+
.replace(/^(#{1,6})\s+(.+)$/gm, (_, hashes, text) => {
|
|
564
|
+
return chalk.bold.blue(`${hashes} ${text}`)
|
|
565
|
+
})
|
|
566
|
+
// Bullet points - replace with yellow bullet character
|
|
567
|
+
.replace(/^[-*]\s+(.+)$/gm, (_, text) => {
|
|
568
|
+
return `${chalk.yellow('•')} ${text}`
|
|
569
|
+
})
|
|
570
|
+
// Bold (**text**) - make bold
|
|
571
|
+
.replace(/\*\*(.+?)\*\*/g, (_, text) => {
|
|
572
|
+
return chalk.bold(text)
|
|
573
|
+
})
|
|
574
|
+
// Italic (*text*) - make italic (dim in terminals)
|
|
575
|
+
.replace(/\*(.+?)\*/g, (_, text) => {
|
|
576
|
+
return chalk.italic(text)
|
|
577
|
+
})
|
|
578
|
+
)
|
|
579
|
+
}
|