codeceptjs 4.0.0-beta.3 → 4.0.0-beta.5
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 +134 -119
- package/bin/codecept.js +12 -2
- package/bin/test-server.js +53 -0
- 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 +141 -86
- package/lib/command/check.js +201 -0
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +8 -26
- package/lib/command/dryRun.js +30 -35
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +75 -73
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +42 -8
- package/lib/command/init.js +13 -12
- package/lib/command/interactive.js +10 -2
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple/chunk.js +48 -45
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +21 -58
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +263 -222
- package/lib/container.js +386 -238
- 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 +223 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +158 -0
- package/lib/event.js +21 -17
- package/lib/heal.js +88 -80
- package/lib/helper/AI.js +2 -1
- package/lib/helper/ApiDataFactory.js +4 -7
- package/lib/helper/Appium.js +50 -57
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +75 -37
- package/lib/helper/Mochawesome.js +31 -9
- package/lib/helper/Nightmare.js +37 -58
- package/lib/helper/Playwright.js +267 -272
- package/lib/helper/Protractor.js +56 -87
- package/lib/helper/Puppeteer.js +247 -264
- package/lib/helper/REST.js +29 -17
- package/lib/helper/TestCafe.js +22 -47
- package/lib/helper/WebDriver.js +157 -368
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/network/utils.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +27 -28
- 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/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +32 -18
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +1 -1
- package/lib/mocha/asyncWrapper.js +231 -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 +32 -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 +31 -7
- package/lib/mocha/suite.js +82 -0
- package/lib/mocha/test.js +181 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +232 -0
- package/lib/output.js +93 -65
- package/lib/pause.js +160 -138
- package/lib/plugin/analyze.js +396 -0
- package/lib/plugin/auth.js +435 -0
- package/lib/plugin/autoDelay.js +8 -8
- package/lib/plugin/autoLogin.js +3 -338
- package/lib/plugin/commentStep.js +6 -1
- package/lib/plugin/coverage.js +10 -22
- 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/htmlReporter.js +1947 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryFailedStep.js +17 -18
- package/lib/plugin/retryTo.js +2 -113
- package/lib/plugin/screenshotOnFail.js +17 -58
- 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 +3 -102
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +155 -124
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/secret.js +1 -2
- 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 +21 -332
- package/lib/steps.js +50 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/test-server.js +323 -0
- package/lib/timeout.js +66 -0
- package/lib/utils.js +351 -218
- package/lib/within.js +75 -55
- package/lib/workerStorage.js +2 -1
- package/lib/workers.js +386 -277
- package/package.json +81 -75
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +1 -0
- 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 +9 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +197 -187
- package/typings/promiseBasedTypes.d.ts +53 -903
- package/typings/types.d.ts +372 -1042
- package/lib/cli.js +0 -257
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/MockServer.js +0 -221
- 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
|
@@ -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,35 @@ 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
|
+
if (Array.isArray(expected[key])) {
|
|
353
|
+
// Handle array comparison: each expected element should have a match in actual array
|
|
354
|
+
assert(Array.isArray(actual[key]), `Expected array for key "${key}", but got ${typeof actual[key]}`)
|
|
355
|
+
for (const expectedItem of expected[key]) {
|
|
356
|
+
let found = false
|
|
357
|
+
for (const actualItem of actual[key]) {
|
|
358
|
+
try {
|
|
359
|
+
this._assertContains(actualItem, expectedItem)
|
|
360
|
+
found = true
|
|
361
|
+
break
|
|
362
|
+
} catch (err) {
|
|
363
|
+
continue
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
assert(found, `No matching element found in array for ${JSON.stringify(expectedItem)}`)
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
this._assertContains(actual[key], expected[key])
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
assert.deepStrictEqual(actual[key], expected[key], `Values for key "${key}" don't match`)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
338
376
|
}
|
|
339
377
|
|
|
340
378
|
module.exports = JSONResponse
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
let addMochawesomeContext
|
|
2
1
|
let currentTest
|
|
3
2
|
let currentSuite
|
|
4
3
|
|
|
5
4
|
const Helper = require('@codeceptjs/helper')
|
|
6
5
|
const { clearString } = require('../utils')
|
|
6
|
+
const { testToFileName } = require('../mocha/test')
|
|
7
7
|
|
|
8
8
|
class Mochawesome extends Helper {
|
|
9
9
|
constructor(config) {
|
|
@@ -15,7 +15,8 @@ class Mochawesome extends Helper {
|
|
|
15
15
|
disableScreenshots: false,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
this._addContext = require('mochawesome/addContext')
|
|
19
|
+
|
|
19
20
|
this._createConfig(config)
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -36,35 +37,56 @@ class Mochawesome extends Helper {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
_test(test) {
|
|
39
|
-
|
|
40
|
+
// If this is a retried test, we want to add context to the retried test
|
|
41
|
+
// but also potentially preserve context from the original test
|
|
42
|
+
const originalTest = test.retriedTest && test.retriedTest()
|
|
43
|
+
if (originalTest) {
|
|
44
|
+
// This is a retried test - use the retried test for context
|
|
45
|
+
currentTest = { test }
|
|
46
|
+
|
|
47
|
+
// Optionally copy context from original test if it exists
|
|
48
|
+
// Note: mochawesome context is stored in test.ctx, but we need to be careful
|
|
49
|
+
// not to break the mocha context structure
|
|
50
|
+
} else {
|
|
51
|
+
// Normal test (not a retry)
|
|
52
|
+
currentTest = { test }
|
|
53
|
+
}
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
_failed(test) {
|
|
43
57
|
if (this.options.disableScreenshots) return
|
|
44
58
|
let fileName
|
|
45
59
|
// Get proper name if we are fail on hook
|
|
46
|
-
if (test.ctx
|
|
60
|
+
if (test.ctx?.test?.type === 'hook') {
|
|
47
61
|
currentTest = { test: test.ctx.test }
|
|
48
62
|
// ignore retries if we are in hook
|
|
49
63
|
test._retries = -1
|
|
50
64
|
fileName = clearString(`${test.title}_${currentTest.test.title}`)
|
|
51
65
|
} else {
|
|
52
66
|
currentTest = { test }
|
|
53
|
-
fileName =
|
|
67
|
+
fileName = testToFileName(test)
|
|
54
68
|
}
|
|
55
69
|
if (this.options.uniqueScreenshotNames) {
|
|
56
|
-
|
|
57
|
-
fileName = `${fileName.substring(0, 10)}_${uuid}`
|
|
70
|
+
fileName = testToFileName(test, { unique: true })
|
|
58
71
|
}
|
|
59
72
|
if (test._retries < 1 || test._retries === test.retryNum) {
|
|
60
73
|
fileName = `${fileName}.failed.png`
|
|
61
|
-
return
|
|
74
|
+
return this._addContext(currentTest, fileName)
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
addMochawesomeContext(context) {
|
|
66
79
|
if (currentTest === '') currentTest = { test: currentSuite.ctx.test }
|
|
67
|
-
|
|
80
|
+
|
|
81
|
+
// For retried tests, make sure we're adding context to the current (retried) test
|
|
82
|
+
// not the original test
|
|
83
|
+
let targetTest = currentTest
|
|
84
|
+
if (currentTest.test && currentTest.test.retriedTest && currentTest.test.retriedTest()) {
|
|
85
|
+
// This test has been retried, make sure we're using the current test for context
|
|
86
|
+
targetTest = { test: currentTest.test }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this._addContext(targetTest, context)
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
92
|
|
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
|
|
@@ -1324,7 +1309,7 @@ class Nightmare extends Helper {
|
|
|
1324
1309
|
offsetY,
|
|
1325
1310
|
)
|
|
1326
1311
|
}
|
|
1327
|
-
|
|
1312
|
+
|
|
1328
1313
|
return this.executeScript(
|
|
1329
1314
|
function (x, y) {
|
|
1330
1315
|
return window.scrollTo(x, y)
|
|
@@ -1345,30 +1330,24 @@ class Nightmare extends Helper {
|
|
|
1345
1330
|
* {{> scrollPageToBottom }}
|
|
1346
1331
|
*/
|
|
1347
1332
|
async scrollPageToBottom() {
|
|
1348
|
-
/* eslint-disable prefer-arrow-callback, comma-dangle */
|
|
1349
1333
|
return this.executeScript(function () {
|
|
1350
1334
|
const body = document.body
|
|
1351
1335
|
const html = document.documentElement
|
|
1352
|
-
window.scrollTo(
|
|
1353
|
-
0,
|
|
1354
|
-
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
1355
|
-
)
|
|
1336
|
+
window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
|
|
1356
1337
|
})
|
|
1357
|
-
/* eslint-enable */
|
|
1358
1338
|
}
|
|
1359
1339
|
|
|
1360
1340
|
/**
|
|
1361
1341
|
* {{> grabPageScrollPosition }}
|
|
1362
1342
|
*/
|
|
1363
1343
|
async grabPageScrollPosition() {
|
|
1364
|
-
/* eslint-disable comma-dangle */
|
|
1365
1344
|
function getScrollPosition() {
|
|
1366
1345
|
return {
|
|
1367
1346
|
x: window.pageXOffset,
|
|
1368
1347
|
y: window.pageYOffset,
|
|
1369
1348
|
}
|
|
1370
1349
|
}
|
|
1371
|
-
|
|
1350
|
+
|
|
1372
1351
|
return this.executeScript(getScrollPosition)
|
|
1373
1352
|
}
|
|
1374
1353
|
}
|
|
@@ -1393,7 +1372,7 @@ async function proceedSee(assertType, text, context) {
|
|
|
1393
1372
|
|
|
1394
1373
|
const texts = await this.browser.evaluate(
|
|
1395
1374
|
(by, locator) => {
|
|
1396
|
-
return window.codeceptjs.findElements(by, locator).map(
|
|
1375
|
+
return window.codeceptjs.findElements(by, locator).map(el => el.innerText)
|
|
1397
1376
|
},
|
|
1398
1377
|
locator.type,
|
|
1399
1378
|
locator.value,
|
|
@@ -1405,8 +1384,8 @@ async function proceedSee(assertType, text, context) {
|
|
|
1405
1384
|
async function proceedSeeInField(assertType, field, value) {
|
|
1406
1385
|
const el = await findField.call(this, field)
|
|
1407
1386
|
assertElementExists(el, field, 'Field')
|
|
1408
|
-
const tag = await this.browser.evaluate(
|
|
1409
|
-
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)
|
|
1410
1389
|
if (tag === 'select') {
|
|
1411
1390
|
// locate option by values and check them
|
|
1412
1391
|
const text = await this.browser.evaluate(
|
|
@@ -1424,8 +1403,8 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
1424
1403
|
async function proceedIsChecked(assertType, option) {
|
|
1425
1404
|
const els = await findCheckable.call(this, option)
|
|
1426
1405
|
assertElementExists(els, option, 'Checkable')
|
|
1427
|
-
const selected = await this.browser.evaluate(
|
|
1428
|
-
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)
|
|
1429
1408
|
}, els)
|
|
1430
1409
|
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
1431
1410
|
}
|