codeceptjs 4.0.0-beta.1 → 4.0.0-beta.11.esm-aria
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +71 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +238 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +300 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +124 -50
- package/lib/container.js +765 -260
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +47 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +115 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -879
- package/typings/types.d.ts +547 -996
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- package/lib/within.js +0 -70
|
@@ -1,77 +1,68 @@
|
|
|
1
1
|
import assert from 'assert'
|
|
2
|
-
import { isInTraffic, createAdvancedTestResults, getTrafficDump } from './utils.js'
|
|
2
|
+
import { isInTraffic, createAdvancedTestResults, getTrafficDump } from './utils.js'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function dontSeeTraffic({ name, url }) {
|
|
5
5
|
if (!this.recordedAtLeastOnce) {
|
|
6
|
-
throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.')
|
|
6
|
+
throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.')
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
if (!name) {
|
|
10
|
-
throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".')
|
|
10
|
+
throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".')
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
if (!url) {
|
|
14
|
-
throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".')
|
|
14
|
+
throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (isInTraffic.call(this, url)) {
|
|
18
|
-
assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`)
|
|
18
|
+
assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`)
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
name, url, parameters, requestPostData, timeout = 10,
|
|
24
|
-
}) {
|
|
22
|
+
async function seeTraffic({ name, url, parameters, requestPostData, timeout = 10 }) {
|
|
25
23
|
if (!name) {
|
|
26
|
-
throw new Error('Missing required key "name" in object given to "I.seeTraffic".')
|
|
24
|
+
throw new Error('Missing required key "name" in object given to "I.seeTraffic".')
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
if (!url) {
|
|
30
|
-
throw new Error('Missing required key "url" in object given to "I.seeTraffic".')
|
|
28
|
+
throw new Error('Missing required key "url" in object given to "I.seeTraffic".')
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
if (!this.
|
|
31
|
+
if (!this.recordedAtLeastOnce) {
|
|
34
32
|
throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.');
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
for (let i = 0; i <= timeout * 2; i++) {
|
|
38
|
-
const found = isInTraffic.call(this, url, parameters)
|
|
36
|
+
const found = isInTraffic.call(this, url, parameters)
|
|
39
37
|
if (found) {
|
|
40
|
-
return true
|
|
38
|
+
return true
|
|
41
39
|
}
|
|
42
|
-
await new Promise(
|
|
43
|
-
setTimeout(done, 1000)
|
|
44
|
-
})
|
|
40
|
+
await new Promise(done => {
|
|
41
|
+
setTimeout(done, 1000)
|
|
42
|
+
})
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
// check request post data
|
|
48
46
|
if (requestPostData && isInTraffic.call(this, url)) {
|
|
49
|
-
const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests)
|
|
47
|
+
const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests)
|
|
50
48
|
|
|
51
|
-
assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`)
|
|
49
|
+
assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`)
|
|
52
50
|
} else if (parameters && isInTraffic.call(this, url)) {
|
|
53
|
-
const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests)
|
|
51
|
+
const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests)
|
|
54
52
|
|
|
55
|
-
assert.fail(
|
|
56
|
-
`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n`
|
|
57
|
-
+ `${advancedTestResults}`,
|
|
58
|
-
);
|
|
53
|
+
assert.fail(`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n` + `${advancedTestResults}`)
|
|
59
54
|
} else {
|
|
60
|
-
assert.fail(
|
|
61
|
-
`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n`
|
|
62
|
-
+ `Expected url: ${url}.\n`
|
|
63
|
-
+ `Recorded traffic:\n${getTrafficDump.call(this)}`,
|
|
64
|
-
);
|
|
55
|
+
assert.fail(`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n` + `Expected url: ${url}.\n` + `Recorded traffic:\n${getTrafficDump.call(this)}`)
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
if (!this.
|
|
59
|
+
async function grabRecordedNetworkTraffics() {
|
|
60
|
+
if (!this.recordedAtLeastOnce) {
|
|
70
61
|
throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.');
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
const promises = this.requests.map(async
|
|
74
|
-
const resp = await request.response
|
|
64
|
+
const promises = this.requests.map(async request => {
|
|
65
|
+
const resp = await request.response
|
|
75
66
|
|
|
76
67
|
if (!resp) {
|
|
77
68
|
return {
|
|
@@ -81,13 +72,13 @@ export async function grabRecordedNetworkTraffics() {
|
|
|
81
72
|
statusText: '',
|
|
82
73
|
body: '',
|
|
83
74
|
},
|
|
84
|
-
}
|
|
75
|
+
}
|
|
85
76
|
}
|
|
86
77
|
|
|
87
|
-
let body
|
|
78
|
+
let body
|
|
88
79
|
try {
|
|
89
80
|
// There's no 'body' for some requests (redirect etc...)
|
|
90
|
-
body = JSON.parse((await resp.body()).toString())
|
|
81
|
+
body = JSON.parse((await resp.body()).toString())
|
|
91
82
|
} catch (e) {
|
|
92
83
|
// only interested in JSON, not HTML responses.
|
|
93
84
|
}
|
|
@@ -99,18 +90,21 @@ export async function grabRecordedNetworkTraffics() {
|
|
|
99
90
|
statusText: resp.statusText(),
|
|
100
91
|
body,
|
|
101
92
|
},
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
return Promise.all(promises)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
return Promise.all(promises)
|
|
105
96
|
}
|
|
106
97
|
|
|
107
|
-
|
|
98
|
+
function stopRecordingTraffic() {
|
|
108
99
|
// @ts-ignore
|
|
109
100
|
this.page.removeAllListeners('request');
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
this.page.removeAllListeners('requestfinished');
|
|
110
103
|
this.recording = false;
|
|
111
104
|
}
|
|
112
105
|
|
|
113
|
-
|
|
114
|
-
this.requests = []
|
|
106
|
+
function flushNetworkTraffics() {
|
|
107
|
+
this.requests = []
|
|
115
108
|
}
|
|
116
109
|
|
|
110
|
+
export { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics }
|
|
@@ -1,133 +1,133 @@
|
|
|
1
1
|
const createAdvancedTestResults = (url, dataToCheck, requests) => {
|
|
2
2
|
// Creates advanced test results for a network traffic check.
|
|
3
3
|
// Advanced test results only applies when expected parameters are set
|
|
4
|
-
if (!dataToCheck) return ''
|
|
4
|
+
if (!dataToCheck) return ''
|
|
5
5
|
|
|
6
|
-
let urlFound = false
|
|
7
|
-
let advancedResults
|
|
8
|
-
requests.forEach(
|
|
6
|
+
let urlFound = false
|
|
7
|
+
let advancedResults
|
|
8
|
+
requests.forEach(request => {
|
|
9
9
|
// url not found in this request. continue with next request
|
|
10
|
-
if (urlFound || !request.url.match(new RegExp(url))) return
|
|
11
|
-
urlFound = true
|
|
10
|
+
if (urlFound || !request.url.match(new RegExp(url))) return
|
|
11
|
+
urlFound = true
|
|
12
12
|
|
|
13
13
|
// Url found. Now we create advanced test report for that URL and show which parameters failed
|
|
14
14
|
if (!request.requestPostData) {
|
|
15
|
-
advancedResults = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), dataToCheck)
|
|
15
|
+
advancedResults = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), dataToCheck)
|
|
16
16
|
} else if (request.requestPostData) {
|
|
17
|
-
advancedResults = allRequestPostDataValuePairsMatchExtreme(request.requestPostData, dataToCheck)
|
|
17
|
+
advancedResults = allRequestPostDataValuePairsMatchExtreme(request.requestPostData, dataToCheck)
|
|
18
18
|
}
|
|
19
|
-
})
|
|
20
|
-
return advancedResults
|
|
21
|
-
}
|
|
19
|
+
})
|
|
20
|
+
return advancedResults
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
const extractQueryObjects =
|
|
23
|
+
const extractQueryObjects = queryString => {
|
|
24
24
|
// Converts a string of GET parameters into an array of parameter objects. Each parameter object contains the properties "name" and "value".
|
|
25
25
|
if (queryString.indexOf('?') === -1) {
|
|
26
|
-
return []
|
|
26
|
+
return []
|
|
27
27
|
}
|
|
28
|
-
const queryObjects = []
|
|
28
|
+
const queryObjects = []
|
|
29
|
+
|
|
30
|
+
const queryPart = queryString.split('?')[1]
|
|
29
31
|
|
|
30
|
-
const
|
|
32
|
+
const queryParameters = queryPart.split('&')
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
queryParameters.forEach(queryParameter => {
|
|
35
|
+
const keyValue = queryParameter.split('=')
|
|
36
|
+
const queryObject = {}
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
queryObject.name = keyValue[0];
|
|
39
|
-
queryObject.value = decodeURIComponent(keyValue[1]);
|
|
40
|
-
queryObjects.push(queryObject);
|
|
41
|
-
});
|
|
38
|
+
queryObject.name = keyValue[0]
|
|
39
|
+
queryObject.value = decodeURIComponent(keyValue[1])
|
|
40
|
+
queryObjects.push(queryObject)
|
|
41
|
+
})
|
|
42
42
|
|
|
43
|
-
return queryObjects
|
|
44
|
-
}
|
|
43
|
+
return queryObjects
|
|
44
|
+
}
|
|
45
45
|
|
|
46
46
|
const allParameterValuePairsMatchExtreme = (queryStringObject, advancedExpectedParameterValuePairs) => {
|
|
47
47
|
// More advanced check if all request parameters match with the expectations
|
|
48
|
-
let littleReport = '\nQuery parameters:\n'
|
|
49
|
-
let success = true
|
|
48
|
+
let littleReport = '\nQuery parameters:\n'
|
|
49
|
+
let success = true
|
|
50
50
|
|
|
51
51
|
for (const expectedKey in advancedExpectedParameterValuePairs) {
|
|
52
52
|
if (!Object.prototype.hasOwnProperty.call(advancedExpectedParameterValuePairs, expectedKey)) {
|
|
53
|
-
continue
|
|
53
|
+
continue
|
|
54
54
|
}
|
|
55
|
-
let parameterFound = false
|
|
56
|
-
const expectedValue = advancedExpectedParameterValuePairs[expectedKey]
|
|
55
|
+
let parameterFound = false
|
|
56
|
+
const expectedValue = advancedExpectedParameterValuePairs[expectedKey]
|
|
57
57
|
|
|
58
58
|
for (const queryParameter of queryStringObject) {
|
|
59
59
|
if (queryParameter.name === expectedKey) {
|
|
60
|
-
parameterFound = true
|
|
60
|
+
parameterFound = true
|
|
61
61
|
if (expectedValue === undefined) {
|
|
62
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')}\n
|
|
62
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')}\n`
|
|
63
63
|
} else if (typeof expectedValue === 'object' && expectedValue.base64) {
|
|
64
|
-
const decodedActualValue = Buffer.from(queryParameter.value, 'base64').toString('utf8')
|
|
64
|
+
const decodedActualValue = Buffer.from(queryParameter.value, 'base64').toString('utf8')
|
|
65
65
|
if (decodedActualValue === expectedValue.base64) {
|
|
66
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64})\n
|
|
66
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64})\n`
|
|
67
67
|
} else {
|
|
68
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64}) -> actual value: "base64(${decodedActualValue})"\n
|
|
69
|
-
success = false
|
|
68
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64}) -> actual value: "base64(${decodedActualValue})"\n`
|
|
69
|
+
success = false
|
|
70
70
|
}
|
|
71
71
|
} else if (queryParameter.value === expectedValue) {
|
|
72
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')} = ${expectedValue}\n
|
|
72
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')} = ${expectedValue}\n`
|
|
73
73
|
} else {
|
|
74
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = ${expectedValue} -> actual value: "${queryParameter.value}"\n
|
|
75
|
-
success = false
|
|
74
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = ${expectedValue} -> actual value: "${queryParameter.value}"\n`
|
|
75
|
+
success = false
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (parameterFound === false) {
|
|
81
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')}${expectedValue ? ` = ${JSON.stringify(expectedValue)}` : ''} -> parameter not found in request\n
|
|
82
|
-
success = false
|
|
81
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')}${expectedValue ? ` = ${JSON.stringify(expectedValue)}` : ''} -> parameter not found in request\n`
|
|
82
|
+
success = false
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
return success ? true : littleReport
|
|
87
|
-
}
|
|
86
|
+
return success ? true : littleReport
|
|
87
|
+
}
|
|
88
88
|
|
|
89
89
|
const allRequestPostDataValuePairsMatchExtreme = (RequestPostDataObject, advancedExpectedRequestPostValuePairs) => {
|
|
90
90
|
// More advanced check if all request post data match with the expectations
|
|
91
|
-
let littleReport = '\nRequest Post Data:\n'
|
|
92
|
-
let success = true
|
|
91
|
+
let littleReport = '\nRequest Post Data:\n'
|
|
92
|
+
let success = true
|
|
93
93
|
|
|
94
94
|
for (const expectedKey in advancedExpectedRequestPostValuePairs) {
|
|
95
95
|
if (!Object.prototype.hasOwnProperty.call(advancedExpectedRequestPostValuePairs, expectedKey)) {
|
|
96
|
-
continue
|
|
96
|
+
continue
|
|
97
97
|
}
|
|
98
|
-
let keyFound = false
|
|
99
|
-
const expectedValue = advancedExpectedRequestPostValuePairs[expectedKey]
|
|
98
|
+
let keyFound = false
|
|
99
|
+
const expectedValue = advancedExpectedRequestPostValuePairs[expectedKey]
|
|
100
100
|
|
|
101
101
|
for (const [key, value] of Object.entries(RequestPostDataObject)) {
|
|
102
102
|
if (key === expectedKey) {
|
|
103
|
-
keyFound = true
|
|
103
|
+
keyFound = true
|
|
104
104
|
if (expectedValue === undefined) {
|
|
105
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')}\n
|
|
105
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')}\n`
|
|
106
106
|
} else if (typeof expectedValue === 'object' && expectedValue.base64) {
|
|
107
|
-
const decodedActualValue = Buffer.from(value, 'base64').toString('utf8')
|
|
107
|
+
const decodedActualValue = Buffer.from(value, 'base64').toString('utf8')
|
|
108
108
|
if (decodedActualValue === expectedValue.base64) {
|
|
109
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64})\n
|
|
109
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64})\n`
|
|
110
110
|
} else {
|
|
111
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64}) -> actual value: "base64(${decodedActualValue})"\n
|
|
112
|
-
success = false
|
|
111
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = base64(${expectedValue.base64}) -> actual value: "base64(${decodedActualValue})"\n`
|
|
112
|
+
success = false
|
|
113
113
|
}
|
|
114
114
|
} else if (value === expectedValue) {
|
|
115
|
-
littleReport += ` ${expectedKey.padStart(10, ' ')} = ${expectedValue}\n
|
|
115
|
+
littleReport += ` ${expectedKey.padStart(10, ' ')} = ${expectedValue}\n`
|
|
116
116
|
} else {
|
|
117
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = ${expectedValue} -> actual value: "${value}"\n
|
|
118
|
-
success = false
|
|
117
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')} = ${expectedValue} -> actual value: "${value}"\n`
|
|
118
|
+
success = false
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
if (keyFound === false) {
|
|
124
|
-
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')}${expectedValue ? ` = ${JSON.stringify(expectedValue)}` : ''} -> key not found in request\n
|
|
125
|
-
success = false
|
|
124
|
+
littleReport += ` ✖ ${expectedKey.padStart(10, ' ')}${expectedValue ? ` = ${JSON.stringify(expectedValue)}` : ''} -> key not found in request\n`
|
|
125
|
+
success = false
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
return success ? true : littleReport
|
|
130
|
-
}
|
|
129
|
+
return success ? true : littleReport
|
|
130
|
+
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Returns all URLs of all network requests recorded so far during execution of test scenario.
|
|
@@ -135,12 +135,12 @@ const allRequestPostDataValuePairsMatchExtreme = (RequestPostDataObject, advance
|
|
|
135
135
|
* @return {string} List of URLs recorded as a string, separated by new lines after each URL
|
|
136
136
|
* @private
|
|
137
137
|
*/
|
|
138
|
-
|
|
139
|
-
let dumpedTraffic = ''
|
|
140
|
-
this.requests.forEach(
|
|
141
|
-
dumpedTraffic += `${request.method} - ${request.url}\n
|
|
142
|
-
})
|
|
143
|
-
return dumpedTraffic
|
|
138
|
+
function getTrafficDump() {
|
|
139
|
+
let dumpedTraffic = ''
|
|
140
|
+
this.requests.forEach(request => {
|
|
141
|
+
dumpedTraffic += `${request.method} - ${request.url}\n`
|
|
142
|
+
})
|
|
143
|
+
return dumpedTraffic
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
@@ -152,35 +152,29 @@ export function getTrafficDump() {
|
|
|
152
152
|
* @private
|
|
153
153
|
*/
|
|
154
154
|
function isInTraffic(url, parameters) {
|
|
155
|
-
let isInTraffic = false
|
|
156
|
-
this.requests.forEach(
|
|
155
|
+
let isInTraffic = false
|
|
156
|
+
this.requests.forEach(request => {
|
|
157
157
|
if (isInTraffic) {
|
|
158
|
-
return
|
|
158
|
+
return // We already found traffic. Continue with next request
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
if (!request.url.match(new RegExp(url))) {
|
|
162
|
-
return
|
|
162
|
+
return // url not found in this request. continue with next request
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// URL has matched. Now we check the parameters
|
|
166
166
|
|
|
167
167
|
if (parameters) {
|
|
168
|
-
const advancedReport = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), parameters)
|
|
168
|
+
const advancedReport = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), parameters)
|
|
169
169
|
if (advancedReport === true) {
|
|
170
|
-
isInTraffic = true
|
|
170
|
+
isInTraffic = true
|
|
171
171
|
}
|
|
172
172
|
} else {
|
|
173
|
-
isInTraffic = true
|
|
173
|
+
isInTraffic = true
|
|
174
174
|
}
|
|
175
|
-
})
|
|
175
|
+
})
|
|
176
176
|
|
|
177
|
-
return isInTraffic
|
|
177
|
+
return isInTraffic
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
export {
|
|
181
|
-
createAdvancedTestResults,
|
|
182
|
-
extractQueryObjects,
|
|
183
|
-
allParameterValuePairsMatchExtreme,
|
|
184
|
-
allRequestPostDataValuePairsMatchExtreme,
|
|
185
|
-
isInTraffic,
|
|
186
|
-
};
|
|
180
|
+
export { createAdvancedTestResults, extractQueryObjects, allParameterValuePairsMatchExtreme, allRequestPostDataValuePairsMatchExtreme, getTrafficDump, isInTraffic }
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
export const blurElement = (element, context) => {
|
|
2
2
|
const clientSideBlurFn = el => {
|
|
3
|
-
el.blur()
|
|
4
|
-
}
|
|
3
|
+
el.blur()
|
|
4
|
+
}
|
|
5
5
|
|
|
6
6
|
try {
|
|
7
7
|
// Puppeteer
|
|
8
|
-
context.evaluate(clientSideBlurFn, element)
|
|
8
|
+
context.evaluate(clientSideBlurFn, element)
|
|
9
9
|
} catch (e) {
|
|
10
10
|
// WebDriver
|
|
11
11
|
try {
|
|
12
|
-
context.execute(clientSideBlurFn, element)
|
|
12
|
+
context.execute(clientSideBlurFn, element)
|
|
13
13
|
} catch (err) {
|
|
14
14
|
// ignore
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
}
|
|
17
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
export const focusElement = (element, context) => {
|
|
2
2
|
const clientSideFn = el => {
|
|
3
|
-
el.focus()
|
|
4
|
-
}
|
|
3
|
+
el.focus()
|
|
4
|
+
}
|
|
5
5
|
|
|
6
6
|
try {
|
|
7
7
|
// Puppeteer
|
|
8
|
-
context.evaluate(clientSideFn, element)
|
|
8
|
+
context.evaluate(clientSideFn, element)
|
|
9
9
|
} catch (e) {
|
|
10
10
|
// WebDriver
|
|
11
11
|
try {
|
|
12
|
-
context.execute(clientSideFn, element)
|
|
12
|
+
context.execute(clientSideFn, element)
|
|
13
13
|
} catch (err) {
|
|
14
14
|
// ignore
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
}
|
|
17
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export const highlightElement = (element, context) => {
|
|
2
2
|
const clientSideHighlightFn = el => {
|
|
3
|
-
const style = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)'
|
|
4
|
-
const prevStyle = el.style.boxShadow
|
|
5
|
-
el.style.boxShadow = style
|
|
6
|
-
setTimeout(() => el.style.boxShadow = prevStyle, 2000)
|
|
7
|
-
}
|
|
3
|
+
const style = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)'
|
|
4
|
+
const prevStyle = el.style.boxShadow
|
|
5
|
+
el.style.boxShadow = style
|
|
6
|
+
setTimeout(() => (el.style.boxShadow = prevStyle), 2000)
|
|
7
|
+
}
|
|
8
8
|
|
|
9
9
|
try {
|
|
10
10
|
// Puppeteer
|
|
11
|
-
context.evaluate(clientSideHighlightFn, element).catch(err => console.error(err))
|
|
11
|
+
context.evaluate(clientSideHighlightFn, element).catch(err => console.error(err))
|
|
12
12
|
} catch (e) {
|
|
13
13
|
// WebDriver
|
|
14
14
|
try {
|
|
15
|
-
context.execute(clientSideHighlightFn, element)
|
|
15
|
+
context.execute(clientSideHighlightFn, element)
|
|
16
16
|
} catch (err) {
|
|
17
17
|
// ignore
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
}
|
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
function isElementClickable(element) {
|
|
2
2
|
if (!element.getBoundingClientRect || !element.scrollIntoView || !element.contains || !element.getClientRects || !document.elementFromPoint) {
|
|
3
|
-
return false
|
|
3
|
+
return false
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
const getOverlappingElement = (element, context = document) => {
|
|
7
|
-
const elemDimension = element.getBoundingClientRect()
|
|
8
|
-
const x = elemDimension.left +
|
|
9
|
-
const y = elemDimension.top +
|
|
7
|
+
const elemDimension = element.getBoundingClientRect()
|
|
8
|
+
const x = elemDimension.left + element.clientWidth / 2
|
|
9
|
+
const y = elemDimension.top + element.clientHeight / 2
|
|
10
10
|
|
|
11
|
-
return context.elementFromPoint(x, y)
|
|
12
|
-
}
|
|
11
|
+
return context.elementFromPoint(x, y)
|
|
12
|
+
}
|
|
13
13
|
|
|
14
14
|
const getOverlappingRects = (element, context = document) => {
|
|
15
|
-
const rects = element.getClientRects()
|
|
16
|
-
const rect = rects[0]
|
|
17
|
-
const x = rect.left +
|
|
18
|
-
const y = rect.top +
|
|
15
|
+
const rects = element.getClientRects()
|
|
16
|
+
const rect = rects[0]
|
|
17
|
+
const x = rect.left + rect.width / 2
|
|
18
|
+
const y = rect.top + rect.height / 2
|
|
19
19
|
|
|
20
|
-
return context.elementFromPoint(x, y)
|
|
21
|
-
}
|
|
20
|
+
return context.elementFromPoint(x, y)
|
|
21
|
+
}
|
|
22
22
|
|
|
23
23
|
const getOverlappingElements = (element, context) => {
|
|
24
|
-
return [getOverlappingElement(element, context), getOverlappingRects(element, context)]
|
|
25
|
-
}
|
|
24
|
+
return [getOverlappingElement(element, context), getOverlappingRects(element, context)]
|
|
25
|
+
}
|
|
26
26
|
|
|
27
27
|
const isOverlappingElementMatch = (elementsFromPoint, element) => {
|
|
28
28
|
if (elementsFromPoint.some(elementFromPoint => elementFromPoint === element || element.contains(elementFromPoint))) {
|
|
29
|
-
return true
|
|
29
|
+
return true
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
let elementsWithShadowRoot = [...new Set(elementsFromPoint)]
|
|
33
|
-
elementsWithShadowRoot = elementsWithShadowRoot.filter(elem => elem && elem.shadowRoot && elem.shadowRoot.elementFromPoint)
|
|
32
|
+
let elementsWithShadowRoot = [...new Set(elementsFromPoint)]
|
|
33
|
+
elementsWithShadowRoot = elementsWithShadowRoot.filter(elem => elem && elem.shadowRoot && elem.shadowRoot.elementFromPoint)
|
|
34
34
|
|
|
35
|
-
let shadowElementsFromPoint = []
|
|
35
|
+
let shadowElementsFromPoint = []
|
|
36
36
|
for (const shadowElement of elementsWithShadowRoot) {
|
|
37
|
-
shadowElementsFromPoint.push(...getOverlappingElements(element, shadowElement.shadowRoot))
|
|
37
|
+
shadowElementsFromPoint.push(...getOverlappingElements(element, shadowElement.shadowRoot))
|
|
38
38
|
}
|
|
39
|
-
shadowElementsFromPoint = [...new Set(shadowElementsFromPoint)]
|
|
40
|
-
shadowElementsFromPoint = shadowElementsFromPoint.filter(element => !elementsFromPoint.includes(element))
|
|
39
|
+
shadowElementsFromPoint = [...new Set(shadowElementsFromPoint)]
|
|
40
|
+
shadowElementsFromPoint = shadowElementsFromPoint.filter(element => !elementsFromPoint.includes(element))
|
|
41
41
|
|
|
42
42
|
if (shadowElementsFromPoint.length === 0) {
|
|
43
|
-
return false
|
|
43
|
+
return false
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return isOverlappingElementMatch(shadowElementsFromPoint, element)
|
|
47
|
-
}
|
|
46
|
+
return isOverlappingElementMatch(shadowElementsFromPoint, element)
|
|
47
|
+
}
|
|
48
48
|
|
|
49
|
-
const isElementInViewport =
|
|
50
|
-
const rect = element.getBoundingClientRect()
|
|
49
|
+
const isElementInViewport = element => {
|
|
50
|
+
const rect = element.getBoundingClientRect()
|
|
51
51
|
|
|
52
|
-
const windowHeight =
|
|
53
|
-
const windowWidth =
|
|
52
|
+
const windowHeight = window.innerHeight || document.documentElement.clientHeight
|
|
53
|
+
const windowWidth = window.innerWidth || document.documentElement.clientWidth
|
|
54
54
|
|
|
55
|
-
const vertInView =
|
|
56
|
-
const horInView =
|
|
55
|
+
const vertInView = rect.top <= windowHeight && rect.top + rect.height > 0
|
|
56
|
+
const horInView = rect.left <= windowWidth && rect.left + rect.width > 0
|
|
57
57
|
|
|
58
|
-
return
|
|
59
|
-
}
|
|
58
|
+
return vertInView && horInView
|
|
59
|
+
}
|
|
60
60
|
|
|
61
|
-
return element.disabled !== true && isElementInViewport(element) && isOverlappingElementMatch(getOverlappingElements(element), element)
|
|
61
|
+
return element.disabled !== true && isElementInViewport(element) && isOverlappingElementMatch(getOverlappingElements(element), element)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export default isElementClickable
|
|
64
|
+
export default isElementClickable
|
package/lib/helper.js
CHANGED