codeceptjs 3.6.10 → 3.7.0-beta.1
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 +81 -110
- package/bin/codecept.js +2 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +46 -36
- 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/configMigrate.js +2 -4
- package/lib/command/definitions.js +5 -25
- 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 +1 -1
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +10 -10
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +14 -17
- package/lib/container.js +327 -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/els.js +177 -0
- package/lib/event.js +1 -0
- package/lib/heal.js +78 -80
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +15 -30
- 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 +189 -251
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +134 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +103 -162
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/artifacts.js +2 -2
- package/lib/listener/emptyRun.js +58 -0
- package/lib/listener/exit.js +4 -4
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/steps.js +17 -12
- package/lib/listener/store.js +12 -0
- package/lib/mocha/asyncWrapper.js +204 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +257 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +11 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +83 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +24 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
- package/lib/mocha/suite.js +55 -0
- package/lib/mocha/test.js +60 -0
- package/lib/mocha/types.d.ts +31 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +28 -10
- package/lib/pause.js +159 -135
- package/lib/plugin/autoDelay.js +4 -4
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +1 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/debugErrors.js +2 -2
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +6 -9
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +2 -2
- package/lib/plugin/screenshotOnFail.js +9 -36
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/stepByStepReport.js +51 -13
- package/lib/plugin/stepTimeout.js +4 -11
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +1 -1
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +142 -121
- package/lib/secret.js +1 -1
- package/lib/step.js +160 -144
- package/lib/store.js +6 -2
- package/lib/template/heal.js +2 -11
- package/lib/utils.js +224 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +265 -261
- package/package.json +46 -47
- package/typings/index.d.ts +172 -184
- package/typings/promiseBasedTypes.d.ts +53 -516
- package/typings/types.d.ts +127 -587
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/mochaFactory.js +0 -113
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/helper/Appium.js
CHANGED
|
@@ -185,9 +185,7 @@ class Appium extends Webdriver {
|
|
|
185
185
|
|
|
186
186
|
webdriverio = require('webdriverio')
|
|
187
187
|
if (!config.appiumV2) {
|
|
188
|
-
console.log(
|
|
189
|
-
'The Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022. Please migrating to Appium 2.x by adding appiumV2: true to your config.',
|
|
190
|
-
)
|
|
188
|
+
console.log('The Appium core team does not maintain Appium 1.x anymore since the 1st of January 2022. Please migrating to Appium 2.x by adding appiumV2: true to your config.')
|
|
191
189
|
console.log('More info: https://bit.ly/appium-v2-migration')
|
|
192
190
|
console.log('This Appium 1.x support will be removed in next major release.')
|
|
193
191
|
}
|
|
@@ -234,20 +232,14 @@ class Appium extends Webdriver {
|
|
|
234
232
|
|
|
235
233
|
config.baseUrl = config.url || config.baseUrl
|
|
236
234
|
if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
|
|
237
|
-
config.capabilities =
|
|
238
|
-
this.appiumV2 === true ? this._convertAppiumV2Caps(config.desiredCapabilities) : config.desiredCapabilities
|
|
235
|
+
config.capabilities = this.appiumV2 === true ? this._convertAppiumV2Caps(config.desiredCapabilities) : config.desiredCapabilities
|
|
239
236
|
}
|
|
240
237
|
|
|
241
238
|
if (this.appiumV2) {
|
|
242
|
-
config.capabilities[`${vendorPrefix.appium}:deviceName`] =
|
|
243
|
-
|
|
244
|
-
config.capabilities[`${vendorPrefix.appium}:
|
|
245
|
-
|
|
246
|
-
config.capabilities[`${vendorPrefix.appium}:app`] =
|
|
247
|
-
config[`${vendorPrefix.appium}:app`] || config.capabilities[`${vendorPrefix.appium}:app`]
|
|
248
|
-
config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`] =
|
|
249
|
-
config[`${vendorPrefix.appium}:tunnelIdentifier`] ||
|
|
250
|
-
config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`] // Adding the code to connect to sauce labs via sauce tunnel
|
|
239
|
+
config.capabilities[`${vendorPrefix.appium}:deviceName`] = config[`${vendorPrefix.appium}:device`] || config.capabilities[`${vendorPrefix.appium}:deviceName`]
|
|
240
|
+
config.capabilities[`${vendorPrefix.appium}:browserName`] = config[`${vendorPrefix.appium}:browser`] || config.capabilities[`${vendorPrefix.appium}:browserName`]
|
|
241
|
+
config.capabilities[`${vendorPrefix.appium}:app`] = config[`${vendorPrefix.appium}:app`] || config.capabilities[`${vendorPrefix.appium}:app`]
|
|
242
|
+
config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`] = config[`${vendorPrefix.appium}:tunnelIdentifier`] || config.capabilities[`${vendorPrefix.appium}:tunnelIdentifier`] // Adding the code to connect to sauce labs via sauce tunnel
|
|
251
243
|
} else {
|
|
252
244
|
config.capabilities.deviceName = config.device || config.capabilities.deviceName
|
|
253
245
|
config.capabilities.browserName = config.browser || config.capabilities.browserName
|
|
@@ -1304,14 +1296,14 @@ class Appium extends Webdriver {
|
|
|
1304
1296
|
}
|
|
1305
1297
|
return browser
|
|
1306
1298
|
.$$(parseLocator.call(this, searchableLocator))
|
|
1307
|
-
.then(
|
|
1308
|
-
.then(
|
|
1299
|
+
.then(els => els.length && els[0].isDisplayed())
|
|
1300
|
+
.then(res => {
|
|
1309
1301
|
if (res) {
|
|
1310
1302
|
return true
|
|
1311
1303
|
}
|
|
1312
1304
|
return this[direction](scrollLocator, offset, speed)
|
|
1313
1305
|
.getSource()
|
|
1314
|
-
.then(
|
|
1306
|
+
.then(source => {
|
|
1315
1307
|
if (source === currentSource) {
|
|
1316
1308
|
err = true
|
|
1317
1309
|
} else {
|
|
@@ -1324,12 +1316,9 @@ class Appium extends Webdriver {
|
|
|
1324
1316
|
timeout * 1000,
|
|
1325
1317
|
errorMsg,
|
|
1326
1318
|
)
|
|
1327
|
-
.catch(
|
|
1319
|
+
.catch(e => {
|
|
1328
1320
|
if (e.message.indexOf('timeout') && e.type !== 'NoSuchElement') {
|
|
1329
|
-
throw new AssertionFailedError(
|
|
1330
|
-
{ customMessage: `Scroll to the end and element ${searchableLocator} was not found` },
|
|
1331
|
-
'',
|
|
1332
|
-
)
|
|
1321
|
+
throw new AssertionFailedError({ customMessage: `Scroll to the end and element ${searchableLocator} was not found` }, '')
|
|
1333
1322
|
} else {
|
|
1334
1323
|
throw e
|
|
1335
1324
|
}
|
|
@@ -1386,8 +1375,8 @@ class Appium extends Webdriver {
|
|
|
1386
1375
|
*/
|
|
1387
1376
|
async pullFile(path, dest) {
|
|
1388
1377
|
onlyForApps.call(this)
|
|
1389
|
-
return this.browser.pullFile(path).then(
|
|
1390
|
-
fs.writeFile(dest, Buffer.from(res, 'base64'),
|
|
1378
|
+
return this.browser.pullFile(path).then(res =>
|
|
1379
|
+
fs.writeFile(dest, Buffer.from(res, 'base64'), err => {
|
|
1391
1380
|
if (err) {
|
|
1392
1381
|
return false
|
|
1393
1382
|
}
|
|
@@ -1759,12 +1748,8 @@ function parseLocator(locator) {
|
|
|
1759
1748
|
}
|
|
1760
1749
|
|
|
1761
1750
|
locator = new Locator(locator, 'xpath')
|
|
1762
|
-
if (locator.type === 'css' && !this.isWeb)
|
|
1763
|
-
|
|
1764
|
-
'Unable to use css locators in apps. Locator strategies for this request: xpath, id, class name or accessibility id',
|
|
1765
|
-
)
|
|
1766
|
-
if (locator.type === 'name' && !this.isWeb)
|
|
1767
|
-
throw new Error("Can't locate element by name in Native context. Use either ID, class name or accessibility id")
|
|
1751
|
+
if (locator.type === 'css' && !this.isWeb) throw new Error('Unable to use css locators in apps. Locator strategies for this request: xpath, id, class name or accessibility id')
|
|
1752
|
+
if (locator.type === 'name' && !this.isWeb) throw new Error("Can't locate element by name in Native context. Use either ID, class name or accessibility id")
|
|
1768
1753
|
if (locator.type === 'id' && !this.isWeb && this.platform === 'android') return `//*[@resource-id='${locator.value}']`
|
|
1769
1754
|
return locator.simplify()
|
|
1770
1755
|
}
|
package/lib/helper/FileSystem.js
CHANGED
|
@@ -105,7 +105,7 @@ class FileSystem extends Helper {
|
|
|
105
105
|
*/
|
|
106
106
|
seeFileNameMatching(text) {
|
|
107
107
|
assert.ok(
|
|
108
|
-
this.grabFileNames().some(
|
|
108
|
+
this.grabFileNames().some(file => file.includes(text)),
|
|
109
109
|
`File name which contains ${text} not found in ${this.dir}`,
|
|
110
110
|
)
|
|
111
111
|
}
|
|
@@ -175,7 +175,7 @@ class FileSystem extends Helper {
|
|
|
175
175
|
* ```
|
|
176
176
|
*/
|
|
177
177
|
grabFileNames() {
|
|
178
|
-
return fs.readdirSync(this.dir).filter(
|
|
178
|
+
return fs.readdirSync(this.dir).filter(item => !fs.lstatSync(path.join(this.dir, item)).isDirectory())
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -216,7 +216,7 @@ function isFileExists(file, timeout) {
|
|
|
216
216
|
}
|
|
217
217
|
})
|
|
218
218
|
|
|
219
|
-
fs.access(file, fs.constants.R_OK,
|
|
219
|
+
fs.access(file, fs.constants.R_OK, err => {
|
|
220
220
|
if (!err) {
|
|
221
221
|
clearTimeout(timer)
|
|
222
222
|
watcher.close()
|
|
@@ -170,7 +170,7 @@ class GraphQLDataFactory extends Helper {
|
|
|
170
170
|
this.factories = this.config.factories
|
|
171
171
|
|
|
172
172
|
this.created = {}
|
|
173
|
-
Object.keys(this.factories).forEach(
|
|
173
|
+
Object.keys(this.factories).forEach(f => (this.created[f] = []))
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
static _checkRequirements() {
|
|
@@ -278,7 +278,7 @@ class GraphQLDataFactory extends Helper {
|
|
|
278
278
|
*/
|
|
279
279
|
_requestCreate(operation, variables) {
|
|
280
280
|
const { query } = this.factories[operation]
|
|
281
|
-
return this.graphqlHelper.sendMutation(query, variables).then(
|
|
281
|
+
return this.graphqlHelper.sendMutation(query, variables).then(response => {
|
|
282
282
|
const data = response.data.data[operation]
|
|
283
283
|
this.created[operation].push(data)
|
|
284
284
|
this.debugSection('Created', `record: ${data}`)
|
|
@@ -297,7 +297,7 @@ class GraphQLDataFactory extends Helper {
|
|
|
297
297
|
const deleteOperation = this.factories[operation].revert(data)
|
|
298
298
|
const { query, variables } = deleteOperation
|
|
299
299
|
|
|
300
|
-
return this.graphqlHelper.sendMutation(query, variables).then(
|
|
300
|
+
return this.graphqlHelper.sendMutation(query, variables).then(response => {
|
|
301
301
|
const idx = this.created[operation].indexOf(data)
|
|
302
302
|
this.debugSection('Deleted', `record: ${response.data.data}`)
|
|
303
303
|
this.created[operation].splice(idx, 1)
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
const Helper = require('@codeceptjs/helper')
|
|
2
|
-
|
|
3
|
-
let expect
|
|
4
|
-
|
|
5
|
-
import('chai').then((chai) => {
|
|
6
|
-
expect = chai.expect
|
|
7
|
-
chai.use(require('chai-deep-match'))
|
|
8
|
-
})
|
|
9
|
-
|
|
2
|
+
const assert = require('assert')
|
|
10
3
|
const joi = require('joi')
|
|
11
4
|
|
|
12
5
|
/**
|
|
@@ -77,12 +70,10 @@ class JSONResponse extends Helper {
|
|
|
77
70
|
_beforeSuite() {
|
|
78
71
|
this.response = null
|
|
79
72
|
if (!this.helpers[this.options.requestHelper]) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`,
|
|
82
|
-
)
|
|
73
|
+
throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
|
|
83
74
|
}
|
|
84
75
|
// connect to REST helper
|
|
85
|
-
this.helpers[this.options.requestHelper].config.onResponse =
|
|
76
|
+
this.helpers[this.options.requestHelper].config.onResponse = response => {
|
|
86
77
|
this.response = response
|
|
87
78
|
}
|
|
88
79
|
}
|
|
@@ -110,7 +101,7 @@ class JSONResponse extends Helper {
|
|
|
110
101
|
*/
|
|
111
102
|
seeResponseCodeIs(code) {
|
|
112
103
|
this._checkResponseReady()
|
|
113
|
-
|
|
104
|
+
assert.strictEqual(this.response.status, code, 'Response code is not the same as expected')
|
|
114
105
|
}
|
|
115
106
|
|
|
116
107
|
/**
|
|
@@ -124,7 +115,7 @@ class JSONResponse extends Helper {
|
|
|
124
115
|
*/
|
|
125
116
|
dontSeeResponseCodeIs(code) {
|
|
126
117
|
this._checkResponseReady()
|
|
127
|
-
|
|
118
|
+
assert.notStrictEqual(this.response.status, code)
|
|
128
119
|
}
|
|
129
120
|
|
|
130
121
|
/**
|
|
@@ -132,8 +123,7 @@ class JSONResponse extends Helper {
|
|
|
132
123
|
*/
|
|
133
124
|
seeResponseCodeIsClientError() {
|
|
134
125
|
this._checkResponseReady()
|
|
135
|
-
|
|
136
|
-
expect(this.response.status).to.be.lt(500)
|
|
126
|
+
assert(this.response.status >= 400 && this.response.status < 500)
|
|
137
127
|
}
|
|
138
128
|
|
|
139
129
|
/**
|
|
@@ -141,8 +131,7 @@ class JSONResponse extends Helper {
|
|
|
141
131
|
*/
|
|
142
132
|
seeResponseCodeIsRedirection() {
|
|
143
133
|
this._checkResponseReady()
|
|
144
|
-
|
|
145
|
-
expect(this.response.status).to.be.lt(400)
|
|
134
|
+
assert(this.response.status >= 300 && this.response.status < 400)
|
|
146
135
|
}
|
|
147
136
|
|
|
148
137
|
/**
|
|
@@ -150,8 +139,7 @@ class JSONResponse extends Helper {
|
|
|
150
139
|
*/
|
|
151
140
|
seeResponseCodeIsServerError() {
|
|
152
141
|
this._checkResponseReady()
|
|
153
|
-
|
|
154
|
-
expect(this.response.status).to.be.lt(600)
|
|
142
|
+
assert(this.response.status >= 500 && this.response.status < 600)
|
|
155
143
|
}
|
|
156
144
|
|
|
157
145
|
/**
|
|
@@ -164,8 +152,7 @@ class JSONResponse extends Helper {
|
|
|
164
152
|
*/
|
|
165
153
|
seeResponseCodeIsSuccessful() {
|
|
166
154
|
this._checkResponseReady()
|
|
167
|
-
|
|
168
|
-
expect(this.response.status).to.be.lt(300)
|
|
155
|
+
assert(this.response.status >= 200 && this.response.status < 300)
|
|
169
156
|
}
|
|
170
157
|
|
|
171
158
|
/**
|
|
@@ -188,17 +175,19 @@ class JSONResponse extends Helper {
|
|
|
188
175
|
seeResponseContainsJson(json = {}) {
|
|
189
176
|
this._checkResponseReady()
|
|
190
177
|
if (Array.isArray(this.response.data)) {
|
|
191
|
-
let
|
|
178
|
+
let found = false
|
|
192
179
|
for (const el of this.response.data) {
|
|
193
180
|
try {
|
|
194
|
-
|
|
181
|
+
this._assertContains(el, json)
|
|
182
|
+
found = true
|
|
183
|
+
break
|
|
195
184
|
} catch (err) {
|
|
196
|
-
|
|
185
|
+
continue
|
|
197
186
|
}
|
|
198
187
|
}
|
|
199
|
-
|
|
188
|
+
assert(found, `No elements in array matched ${JSON.stringify(json)}`)
|
|
200
189
|
} else {
|
|
201
|
-
|
|
190
|
+
this._assertContains(this.response.data, json)
|
|
202
191
|
}
|
|
203
192
|
}
|
|
204
193
|
|
|
@@ -222,9 +211,22 @@ class JSONResponse extends Helper {
|
|
|
222
211
|
dontSeeResponseContainsJson(json = {}) {
|
|
223
212
|
this._checkResponseReady()
|
|
224
213
|
if (Array.isArray(this.response.data)) {
|
|
225
|
-
this.response.data
|
|
214
|
+
for (const data of this.response.data) {
|
|
215
|
+
try {
|
|
216
|
+
this._assertContains(data, json)
|
|
217
|
+
assert.fail(`Found matching element: ${JSON.stringify(data)}`)
|
|
218
|
+
} catch (err) {
|
|
219
|
+
// expected to fail
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
}
|
|
226
223
|
} else {
|
|
227
|
-
|
|
224
|
+
try {
|
|
225
|
+
this._assertContains(this.response.data, json)
|
|
226
|
+
assert.fail('Response contains the JSON')
|
|
227
|
+
} catch (err) {
|
|
228
|
+
// expected to fail
|
|
229
|
+
}
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
|
|
@@ -250,20 +252,27 @@ class JSONResponse extends Helper {
|
|
|
250
252
|
seeResponseContainsKeys(keys = []) {
|
|
251
253
|
this._checkResponseReady()
|
|
252
254
|
if (Array.isArray(this.response.data)) {
|
|
253
|
-
this.response.data
|
|
255
|
+
for (const data of this.response.data) {
|
|
256
|
+
for (const key of keys) {
|
|
257
|
+
assert(key in data, `Key "${key}" is not found in ${JSON.stringify(data)}`)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
254
260
|
} else {
|
|
255
|
-
|
|
261
|
+
for (const key of keys) {
|
|
262
|
+
assert(key in this.response.data, `Key "${key}" is not found in ${JSON.stringify(this.response.data)}`)
|
|
263
|
+
}
|
|
256
264
|
}
|
|
257
265
|
}
|
|
258
266
|
|
|
259
267
|
/**
|
|
260
|
-
* Executes a callback function passing in `response` object and
|
|
268
|
+
* Executes a callback function passing in `response` object and assert
|
|
261
269
|
* Use it to perform custom checks of response data
|
|
262
270
|
*
|
|
263
271
|
* ```js
|
|
264
|
-
* I.seeResponseValidByCallback(({ data, status
|
|
265
|
-
*
|
|
266
|
-
*
|
|
272
|
+
* I.seeResponseValidByCallback(({ data, status }) => {
|
|
273
|
+
* assert.strictEqual(status, 200);
|
|
274
|
+
* assert('user' in data);
|
|
275
|
+
* assert('company' in data);
|
|
267
276
|
* });
|
|
268
277
|
* ```
|
|
269
278
|
*
|
|
@@ -271,7 +280,7 @@ class JSONResponse extends Helper {
|
|
|
271
280
|
*/
|
|
272
281
|
seeResponseValidByCallback(fn) {
|
|
273
282
|
this._checkResponseReady()
|
|
274
|
-
fn({ ...this.response,
|
|
283
|
+
fn({ ...this.response, assert })
|
|
275
284
|
const body = fn.toString()
|
|
276
285
|
fn.toString = () => `${body.split('\n')[1]}...`
|
|
277
286
|
}
|
|
@@ -288,7 +297,7 @@ class JSONResponse extends Helper {
|
|
|
288
297
|
*/
|
|
289
298
|
seeResponseEquals(resp) {
|
|
290
299
|
this._checkResponseReady()
|
|
291
|
-
|
|
300
|
+
assert.deepStrictEqual(this.response.data, resp)
|
|
292
301
|
}
|
|
293
302
|
|
|
294
303
|
/**
|
|
@@ -335,6 +344,17 @@ class JSONResponse extends Helper {
|
|
|
335
344
|
_checkResponseReady() {
|
|
336
345
|
if (!this.response) throw new Error('Response is not available')
|
|
337
346
|
}
|
|
347
|
+
|
|
348
|
+
_assertContains(actual, expected) {
|
|
349
|
+
for (const key in expected) {
|
|
350
|
+
assert(key in actual, `Key "${key}" not found in ${JSON.stringify(actual)}`)
|
|
351
|
+
if (typeof expected[key] === 'object' && expected[key] !== null) {
|
|
352
|
+
this._assertContains(actual[key], expected[key])
|
|
353
|
+
} else {
|
|
354
|
+
assert.deepStrictEqual(actual[key], expected[key], `Values for key "${key}" don't match`)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
338
358
|
}
|
|
339
359
|
|
|
340
360
|
module.exports = JSONResponse
|
package/lib/helper/Nightmare.js
CHANGED
|
@@ -111,13 +111,7 @@ class Nightmare extends Helper {
|
|
|
111
111
|
const by = Object.keys(locator)[0]
|
|
112
112
|
const value = locator[by]
|
|
113
113
|
|
|
114
|
-
this.evaluate_now(
|
|
115
|
-
(by, locator, contextEl) => window.codeceptjs.findAndStoreElements(by, locator, contextEl),
|
|
116
|
-
done,
|
|
117
|
-
by,
|
|
118
|
-
value,
|
|
119
|
-
contextEl,
|
|
120
|
-
)
|
|
114
|
+
this.evaluate_now((by, locator, contextEl) => window.codeceptjs.findAndStoreElements(by, locator, contextEl), done, by, value, contextEl)
|
|
121
115
|
})
|
|
122
116
|
|
|
123
117
|
this.Nightmare.action('findElement', function (locator, contextEl, done) {
|
|
@@ -244,7 +238,7 @@ class Nightmare extends Helper {
|
|
|
244
238
|
nodeId: queryResult.nodeId,
|
|
245
239
|
files: pathsToUpload,
|
|
246
240
|
},
|
|
247
|
-
|
|
241
|
+
err => {
|
|
248
242
|
if (Object.keys(err).length > 0) {
|
|
249
243
|
parent.emit('log', 'problem setting input', err)
|
|
250
244
|
return done(err)
|
|
@@ -351,7 +345,7 @@ class Nightmare extends Helper {
|
|
|
351
345
|
const outputFile = path.join(global.output_dir, fileName)
|
|
352
346
|
this.debug(`HAR is saving to ${outputFile}`)
|
|
353
347
|
|
|
354
|
-
await this.browser.getHAR().then(
|
|
348
|
+
await this.browser.getHAR().then(har => {
|
|
355
349
|
require('fs').writeFileSync(outputFile, JSON.stringify({ log: har }))
|
|
356
350
|
})
|
|
357
351
|
}
|
|
@@ -361,7 +355,7 @@ class Nightmare extends Helper {
|
|
|
361
355
|
}
|
|
362
356
|
|
|
363
357
|
async _stopBrowser() {
|
|
364
|
-
return this.browser.end().catch(
|
|
358
|
+
return this.browser.end().catch(error => {
|
|
365
359
|
this.debugSection('Error on End', error)
|
|
366
360
|
})
|
|
367
361
|
}
|
|
@@ -445,7 +439,7 @@ class Nightmare extends Helper {
|
|
|
445
439
|
// navigating to the same url will cause an error in nightmare, so don't do it
|
|
446
440
|
return
|
|
447
441
|
}
|
|
448
|
-
return this.browser.goto(url, headers).then(
|
|
442
|
+
return this.browser.goto(url, headers).then(res => {
|
|
449
443
|
this.debugSection('URL', res.url)
|
|
450
444
|
this.debugSection('Code', res.code)
|
|
451
445
|
this.debugSection('Headers', JSON.stringify(res.headers))
|
|
@@ -535,7 +529,7 @@ class Nightmare extends Helper {
|
|
|
535
529
|
locator = new Locator(locator, 'css')
|
|
536
530
|
const num = await this.browser.evaluate(
|
|
537
531
|
(by, locator) => {
|
|
538
|
-
return window.codeceptjs.findElements(by, locator).filter(
|
|
532
|
+
return window.codeceptjs.findElements(by, locator).filter(e => e.offsetWidth > 0 && e.offsetHeight > 0).length
|
|
539
533
|
},
|
|
540
534
|
locator.type,
|
|
541
535
|
locator.value,
|
|
@@ -551,7 +545,7 @@ class Nightmare extends Helper {
|
|
|
551
545
|
locator = new Locator(locator, 'css')
|
|
552
546
|
const num = await this.browser.evaluate(
|
|
553
547
|
(by, locator) => {
|
|
554
|
-
return window.codeceptjs.findElements(by, locator).filter(
|
|
548
|
+
return window.codeceptjs.findElements(by, locator).filter(e => e.offsetWidth > 0 && e.offsetHeight > 0).length
|
|
555
549
|
},
|
|
556
550
|
locator.type,
|
|
557
551
|
locator.value,
|
|
@@ -598,9 +592,7 @@ class Nightmare extends Helper {
|
|
|
598
592
|
*/
|
|
599
593
|
async seeNumberOfElements(locator, num) {
|
|
600
594
|
const elements = await this._locate(locator)
|
|
601
|
-
return equals(
|
|
602
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
603
|
-
).assert(elements.length, num)
|
|
595
|
+
return equals(`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`).assert(elements.length, num)
|
|
604
596
|
}
|
|
605
597
|
|
|
606
598
|
/**
|
|
@@ -608,10 +600,7 @@ class Nightmare extends Helper {
|
|
|
608
600
|
*/
|
|
609
601
|
async seeNumberOfVisibleElements(locator, num) {
|
|
610
602
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
611
|
-
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
612
|
-
res,
|
|
613
|
-
num,
|
|
614
|
-
)
|
|
603
|
+
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(res, num)
|
|
615
604
|
}
|
|
616
605
|
|
|
617
606
|
/**
|
|
@@ -622,7 +611,7 @@ class Nightmare extends Helper {
|
|
|
622
611
|
|
|
623
612
|
const num = await this.browser.evaluate(
|
|
624
613
|
(by, locator) => {
|
|
625
|
-
return window.codeceptjs.findElements(by, locator).filter(
|
|
614
|
+
return window.codeceptjs.findElements(by, locator).filter(e => e.offsetWidth > 0 && e.offsetHeight > 0).length
|
|
626
615
|
},
|
|
627
616
|
locator.type,
|
|
628
617
|
locator.value,
|
|
@@ -637,7 +626,7 @@ class Nightmare extends Helper {
|
|
|
637
626
|
async click(locator, context = null) {
|
|
638
627
|
const el = await findClickable.call(this, locator, context)
|
|
639
628
|
assertElementExists(el, locator, 'Clickable')
|
|
640
|
-
return this.browser.evaluate(
|
|
629
|
+
return this.browser.evaluate(el => window.codeceptjs.clickEl(el), el).wait(this.options.waitForAction)
|
|
641
630
|
}
|
|
642
631
|
|
|
643
632
|
/**
|
|
@@ -646,7 +635,7 @@ class Nightmare extends Helper {
|
|
|
646
635
|
async doubleClick(locator, context = null) {
|
|
647
636
|
const el = await findClickable.call(this, locator, context)
|
|
648
637
|
assertElementExists(el, locator, 'Clickable')
|
|
649
|
-
return this.browser.evaluate(
|
|
638
|
+
return this.browser.evaluate(el => window.codeceptjs.doubleClickEl(el), el).wait(this.options.waitForAction)
|
|
650
639
|
}
|
|
651
640
|
|
|
652
641
|
/**
|
|
@@ -655,7 +644,7 @@ class Nightmare extends Helper {
|
|
|
655
644
|
async rightClick(locator, context = null) {
|
|
656
645
|
const el = await findClickable.call(this, locator, context)
|
|
657
646
|
assertElementExists(el, locator, 'Clickable')
|
|
658
|
-
return this.browser.evaluate(
|
|
647
|
+
return this.browser.evaluate(el => window.codeceptjs.rightClickEl(el), el).wait(this.options.waitForAction)
|
|
659
648
|
}
|
|
660
649
|
|
|
661
650
|
/**
|
|
@@ -665,9 +654,7 @@ class Nightmare extends Helper {
|
|
|
665
654
|
locator = new Locator(locator, 'css')
|
|
666
655
|
const el = await this.browser.findElement(locator.toStrict())
|
|
667
656
|
assertElementExists(el, locator)
|
|
668
|
-
return this.browser
|
|
669
|
-
.evaluate((el, x, y) => window.codeceptjs.hoverEl(el, x, y), el, offsetX, offsetY)
|
|
670
|
-
.wait(this.options.waitForAction) // wait for hover event to happen
|
|
657
|
+
return this.browser.evaluate((el, x, y) => window.codeceptjs.hoverEl(el, x, y), el, offsetX, offsetY).wait(this.options.waitForAction) // wait for hover event to happen
|
|
671
658
|
}
|
|
672
659
|
|
|
673
660
|
/**
|
|
@@ -676,7 +663,7 @@ class Nightmare extends Helper {
|
|
|
676
663
|
* Wrapper for synchronous [evaluate](https://github.com/segmentio/nightmare#evaluatefn-arg1-arg2)
|
|
677
664
|
*/
|
|
678
665
|
async executeScript(...args) {
|
|
679
|
-
return this.browser.evaluate.apply(this.browser, args).catch(
|
|
666
|
+
return this.browser.evaluate.apply(this.browser, args).catch(err => err) // Nightmare's first argument is error :(
|
|
680
667
|
}
|
|
681
668
|
|
|
682
669
|
/**
|
|
@@ -686,7 +673,7 @@ class Nightmare extends Helper {
|
|
|
686
673
|
* Unlike NightmareJS implementation calling `done` will return its first argument.
|
|
687
674
|
*/
|
|
688
675
|
async executeAsyncScript(...args) {
|
|
689
|
-
return this.browser.evaluate.apply(this.browser, args).catch(
|
|
676
|
+
return this.browser.evaluate.apply(this.browser, args).catch(err => err) // Nightmare's first argument is error :(
|
|
690
677
|
}
|
|
691
678
|
|
|
692
679
|
/**
|
|
@@ -705,7 +692,7 @@ class Nightmare extends Helper {
|
|
|
705
692
|
async checkOption(field, context = null) {
|
|
706
693
|
const els = await findCheckable.call(this, field, context)
|
|
707
694
|
assertElementExists(els[0], field, 'Checkbox or radio')
|
|
708
|
-
return this.browser.evaluate(
|
|
695
|
+
return this.browser.evaluate(els => window.codeceptjs.checkEl(els[0]), els).wait(this.options.waitForAction)
|
|
709
696
|
}
|
|
710
697
|
|
|
711
698
|
/**
|
|
@@ -714,7 +701,7 @@ class Nightmare extends Helper {
|
|
|
714
701
|
async uncheckOption(field, context = null) {
|
|
715
702
|
const els = await findCheckable.call(this, field, context)
|
|
716
703
|
assertElementExists(els[0], field, 'Checkbox or radio')
|
|
717
|
-
return this.browser.evaluate(
|
|
704
|
+
return this.browser.evaluate(els => window.codeceptjs.unCheckEl(els[0]), els).wait(this.options.waitForAction)
|
|
718
705
|
}
|
|
719
706
|
|
|
720
707
|
/**
|
|
@@ -826,7 +813,7 @@ class Nightmare extends Helper {
|
|
|
826
813
|
locator = new Locator(locator, 'css')
|
|
827
814
|
const els = await this.browser.findElements(locator.toStrict())
|
|
828
815
|
const texts = []
|
|
829
|
-
const getText =
|
|
816
|
+
const getText = el => window.codeceptjs.fetchElement(el).innerText
|
|
830
817
|
for (const el of els) {
|
|
831
818
|
texts.push(await this.browser.evaluate(getText, el))
|
|
832
819
|
}
|
|
@@ -855,7 +842,7 @@ class Nightmare extends Helper {
|
|
|
855
842
|
locator = new Locator(locator, 'css')
|
|
856
843
|
const els = await this.browser.findElements(locator.toStrict())
|
|
857
844
|
const values = []
|
|
858
|
-
const getValues =
|
|
845
|
+
const getValues = el => window.codeceptjs.fetchElement(el).value
|
|
859
846
|
for (const el of els) {
|
|
860
847
|
values.push(await this.browser.evaluate(getValues, el))
|
|
861
848
|
}
|
|
@@ -887,9 +874,7 @@ class Nightmare extends Helper {
|
|
|
887
874
|
|
|
888
875
|
for (let index = 0; index < els.length; index++) {
|
|
889
876
|
const el = els[index]
|
|
890
|
-
array.push(
|
|
891
|
-
await this.browser.evaluate((el, attr) => window.codeceptjs.fetchElement(el).getAttribute(attr), el, attr),
|
|
892
|
-
)
|
|
877
|
+
array.push(await this.browser.evaluate((el, attr) => window.codeceptjs.fetchElement(el).getAttribute(attr), el, attr))
|
|
893
878
|
}
|
|
894
879
|
|
|
895
880
|
return array
|
|
@@ -921,7 +906,7 @@ class Nightmare extends Helper {
|
|
|
921
906
|
|
|
922
907
|
for (let index = 0; index < els.length; index++) {
|
|
923
908
|
const el = els[index]
|
|
924
|
-
array.push(await this.browser.evaluate(
|
|
909
|
+
array.push(await this.browser.evaluate(el => window.codeceptjs.fetchElement(el).innerHTML, el))
|
|
925
910
|
}
|
|
926
911
|
this.debugSection('GrabHTML', array)
|
|
927
912
|
|
|
@@ -953,7 +938,7 @@ class Nightmare extends Helper {
|
|
|
953
938
|
|
|
954
939
|
const getCssPropForElement = async (el, prop) => {
|
|
955
940
|
return (
|
|
956
|
-
await this.browser.evaluate(
|
|
941
|
+
await this.browser.evaluate(el => {
|
|
957
942
|
return window.getComputedStyle(window.codeceptjs.fetchElement(el))
|
|
958
943
|
}, el)
|
|
959
944
|
)[toCamelCase(prop)]
|
|
@@ -1083,7 +1068,7 @@ class Nightmare extends Helper {
|
|
|
1083
1068
|
* {{> wait }}
|
|
1084
1069
|
*/
|
|
1085
1070
|
async wait(sec) {
|
|
1086
|
-
return new Promise(
|
|
1071
|
+
return new Promise(done => {
|
|
1087
1072
|
setTimeout(done, sec * 1000)
|
|
1088
1073
|
})
|
|
1089
1074
|
}
|
|
@@ -1106,7 +1091,7 @@ class Nightmare extends Helper {
|
|
|
1106
1091
|
locator.value,
|
|
1107
1092
|
text,
|
|
1108
1093
|
)
|
|
1109
|
-
.catch(
|
|
1094
|
+
.catch(err => {
|
|
1110
1095
|
if (err.message.indexOf('Cannot read property') > -1) {
|
|
1111
1096
|
throw new Error(`element (${JSON.stringify(context)}) is not in DOM. Unable to wait text.`)
|
|
1112
1097
|
} else if (err.message && err.message.indexOf('.wait() timed out after') > -1) {
|
|
@@ -1132,7 +1117,7 @@ class Nightmare extends Helper {
|
|
|
1132
1117
|
locator.type,
|
|
1133
1118
|
locator.value,
|
|
1134
1119
|
)
|
|
1135
|
-
.catch(
|
|
1120
|
+
.catch(err => {
|
|
1136
1121
|
if (err.message && err.message.indexOf('.wait() timed out after') > -1) {
|
|
1137
1122
|
throw new Error(`element (${JSON.stringify(locator)}) still not visible on page after ${sec} sec`)
|
|
1138
1123
|
} else throw err
|
|
@@ -1163,7 +1148,7 @@ class Nightmare extends Helper {
|
|
|
1163
1148
|
locator.type,
|
|
1164
1149
|
locator.value,
|
|
1165
1150
|
)
|
|
1166
|
-
.catch(
|
|
1151
|
+
.catch(err => {
|
|
1167
1152
|
if (err.message && err.message.indexOf('.wait() timed out after') > -1) {
|
|
1168
1153
|
throw new Error(`element (${JSON.stringify(locator)}) still visible after ${sec} sec`)
|
|
1169
1154
|
} else throw err
|
|
@@ -1179,7 +1164,7 @@ class Nightmare extends Helper {
|
|
|
1179
1164
|
|
|
1180
1165
|
return this.browser
|
|
1181
1166
|
.wait((by, locator) => window.codeceptjs.findElement(by, locator) !== null, locator.type, locator.value)
|
|
1182
|
-
.catch(
|
|
1167
|
+
.catch(err => {
|
|
1183
1168
|
if (err.message && err.message.indexOf('.wait() timed out after') > -1) {
|
|
1184
1169
|
throw new Error(`element (${JSON.stringify(locator)}) still not present on page after ${sec} sec`)
|
|
1185
1170
|
} else throw err
|
|
@@ -1203,7 +1188,7 @@ class Nightmare extends Helper {
|
|
|
1203
1188
|
|
|
1204
1189
|
return this.browser
|
|
1205
1190
|
.wait((by, locator) => window.codeceptjs.findElement(by, locator) === null, locator.type, locator.value)
|
|
1206
|
-
.catch(
|
|
1191
|
+
.catch(err => {
|
|
1207
1192
|
if (err.message && err.message.indexOf('.wait() timed out after') > -1) {
|
|
1208
1193
|
throw new Error(`element (${JSON.stringify(locator)}) still on page after ${sec} sec`)
|
|
1209
1194
|
} else throw err
|
|
@@ -1348,10 +1333,7 @@ class Nightmare extends Helper {
|
|
|
1348
1333
|
return this.executeScript(function () {
|
|
1349
1334
|
const body = document.body
|
|
1350
1335
|
const html = document.documentElement
|
|
1351
|
-
window.scrollTo(
|
|
1352
|
-
0,
|
|
1353
|
-
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
1354
|
-
)
|
|
1336
|
+
window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
|
|
1355
1337
|
})
|
|
1356
1338
|
}
|
|
1357
1339
|
|
|
@@ -1390,7 +1372,7 @@ async function proceedSee(assertType, text, context) {
|
|
|
1390
1372
|
|
|
1391
1373
|
const texts = await this.browser.evaluate(
|
|
1392
1374
|
(by, locator) => {
|
|
1393
|
-
return window.codeceptjs.findElements(by, locator).map(
|
|
1375
|
+
return window.codeceptjs.findElements(by, locator).map(el => el.innerText)
|
|
1394
1376
|
},
|
|
1395
1377
|
locator.type,
|
|
1396
1378
|
locator.value,
|
|
@@ -1402,8 +1384,8 @@ async function proceedSee(assertType, text, context) {
|
|
|
1402
1384
|
async function proceedSeeInField(assertType, field, value) {
|
|
1403
1385
|
const el = await findField.call(this, field)
|
|
1404
1386
|
assertElementExists(el, field, 'Field')
|
|
1405
|
-
const tag = await this.browser.evaluate(
|
|
1406
|
-
const fieldVal = await this.browser.evaluate(
|
|
1387
|
+
const tag = await this.browser.evaluate(el => window.codeceptjs.fetchElement(el).tagName, el)
|
|
1388
|
+
const fieldVal = await this.browser.evaluate(el => window.codeceptjs.fetchElement(el).value, el)
|
|
1407
1389
|
if (tag === 'select') {
|
|
1408
1390
|
// locate option by values and check them
|
|
1409
1391
|
const text = await this.browser.evaluate(
|
|
@@ -1421,8 +1403,8 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
1421
1403
|
async function proceedIsChecked(assertType, option) {
|
|
1422
1404
|
const els = await findCheckable.call(this, option)
|
|
1423
1405
|
assertElementExists(els, option, 'Checkable')
|
|
1424
|
-
const selected = await this.browser.evaluate(
|
|
1425
|
-
return els.map(
|
|
1406
|
+
const selected = await this.browser.evaluate(els => {
|
|
1407
|
+
return els.map(el => window.codeceptjs.fetchElement(el).checked).reduce((prev, cur) => prev || cur)
|
|
1426
1408
|
}, els)
|
|
1427
1409
|
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
1428
1410
|
}
|