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
package/lib/locator.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { sprintf } from 'sprintf-js'
|
|
1
|
+
import { sprintf } from 'sprintf-js'
|
|
2
|
+
import { xpathLocator } from './utils.js'
|
|
3
|
+
import { createRequire } from 'module'
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
const require = createRequire(import.meta.url)
|
|
6
|
+
let cssToXPath
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
import { xpathLocator } from './utils.js';
|
|
7
|
-
|
|
8
|
-
let cssToXPath;
|
|
9
|
-
|
|
10
|
-
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'pw'];
|
|
8
|
+
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'pw', 'role']
|
|
11
9
|
/** @class */
|
|
12
10
|
class Locator {
|
|
13
11
|
/**
|
|
@@ -15,162 +13,171 @@ class Locator {
|
|
|
15
13
|
* @param {string} [defaultType]
|
|
16
14
|
*/
|
|
17
15
|
constructor(locator, defaultType = '') {
|
|
18
|
-
this.type = null
|
|
19
|
-
if (!locator) return
|
|
16
|
+
this.type = null
|
|
17
|
+
if (!locator) return
|
|
20
18
|
|
|
21
|
-
this.output = null
|
|
19
|
+
this.output = null
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
22
|
* @private
|
|
25
23
|
* @type {boolean}
|
|
26
24
|
*/
|
|
27
|
-
this.strict = false
|
|
25
|
+
this.strict = false
|
|
28
26
|
|
|
29
27
|
if (typeof locator === 'object') {
|
|
30
28
|
if (locator.constructor.name === 'Locator') {
|
|
31
|
-
Object.assign(this, locator)
|
|
32
|
-
return
|
|
29
|
+
Object.assign(this, locator)
|
|
30
|
+
return
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
this.locator = locator
|
|
36
|
-
this.type = Object.keys(locator)[0]
|
|
37
|
-
this.value = locator[this.type]
|
|
38
|
-
this.strict = true
|
|
33
|
+
this.locator = locator
|
|
34
|
+
this.type = Object.keys(locator)[0]
|
|
35
|
+
this.value = locator[this.type]
|
|
36
|
+
this.strict = true
|
|
39
37
|
|
|
40
|
-
Locator.filters.forEach(f => f(locator, this))
|
|
38
|
+
Locator.filters.forEach(f => f(locator, this))
|
|
41
39
|
|
|
42
|
-
return
|
|
40
|
+
return
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
this.type = defaultType || 'fuzzy'
|
|
46
|
-
this.output = locator
|
|
47
|
-
this.value = locator
|
|
43
|
+
this.type = defaultType || 'fuzzy'
|
|
44
|
+
this.output = locator
|
|
45
|
+
this.value = locator
|
|
48
46
|
|
|
49
47
|
if (isCSS(locator)) {
|
|
50
|
-
this.type = 'css'
|
|
48
|
+
this.type = 'css'
|
|
51
49
|
}
|
|
52
50
|
if (isXPath(locator)) {
|
|
53
|
-
this.type = 'xpath'
|
|
51
|
+
this.type = 'xpath'
|
|
54
52
|
}
|
|
55
53
|
if (isShadow(locator)) {
|
|
56
|
-
this.type = 'shadow'
|
|
54
|
+
this.type = 'shadow'
|
|
57
55
|
}
|
|
58
56
|
if (isPlaywrightLocator(locator)) {
|
|
59
|
-
this.type = 'pw'
|
|
57
|
+
this.type = 'pw'
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
Locator.filters.forEach(f => f(locator, this))
|
|
60
|
+
Locator.filters.forEach(f => f(locator, this))
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
simplify() {
|
|
66
|
-
if (this.isNull()) return null
|
|
64
|
+
if (this.isNull()) return null
|
|
67
65
|
switch (this.type) {
|
|
68
66
|
case 'by':
|
|
69
67
|
case 'xpath':
|
|
70
|
-
return this.value
|
|
68
|
+
return this.value
|
|
71
69
|
case 'css':
|
|
72
|
-
return this.value
|
|
70
|
+
return this.value
|
|
73
71
|
case 'id':
|
|
74
|
-
return `#${this.value}
|
|
72
|
+
return `#${this.value}`
|
|
75
73
|
case 'name':
|
|
76
|
-
return `[name="${this.value}"]
|
|
74
|
+
return `[name="${this.value}"]`
|
|
77
75
|
case 'fuzzy':
|
|
78
|
-
return this.value
|
|
76
|
+
return this.value
|
|
79
77
|
case 'shadow':
|
|
80
|
-
return { shadow: this.value }
|
|
78
|
+
return { shadow: this.value }
|
|
81
79
|
case 'pw':
|
|
82
|
-
return { pw: this.value }
|
|
80
|
+
return { pw: this.value }
|
|
81
|
+
case 'role':
|
|
82
|
+
return `[role="${this.value}"]`
|
|
83
83
|
}
|
|
84
|
-
return this.value
|
|
84
|
+
return this.value
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
toStrict() {
|
|
88
|
-
if (!this.type) return null
|
|
89
|
-
return { [this.type]: this.value }
|
|
88
|
+
if (!this.type) return null
|
|
89
|
+
return { [this.type]: this.value }
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* @returns {string}
|
|
94
94
|
*/
|
|
95
95
|
toString() {
|
|
96
|
-
return this.output || `{${this.type}: ${this.value}}
|
|
96
|
+
return this.output || `{${this.type}: ${this.value}}`
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* @returns {boolean}
|
|
101
101
|
*/
|
|
102
102
|
isFuzzy() {
|
|
103
|
-
return this.type === 'fuzzy'
|
|
103
|
+
return this.type === 'fuzzy'
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* @returns {boolean}
|
|
108
108
|
*/
|
|
109
109
|
isShadow() {
|
|
110
|
-
return this.type === 'shadow'
|
|
110
|
+
return this.type === 'shadow'
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
114
|
* @returns {boolean}
|
|
115
115
|
*/
|
|
116
116
|
isFrame() {
|
|
117
|
-
return this.type === 'frame'
|
|
117
|
+
return this.type === 'frame'
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* @returns {boolean}
|
|
122
122
|
*/
|
|
123
123
|
isCSS() {
|
|
124
|
-
return this.type === 'css'
|
|
124
|
+
return this.type === 'css'
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
128
|
* @returns {boolean}
|
|
129
129
|
*/
|
|
130
130
|
isPlaywrightLocator() {
|
|
131
|
-
return this.type === 'pw'
|
|
131
|
+
return this.type === 'pw'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @returns {boolean}
|
|
136
|
+
*/
|
|
137
|
+
isRole() {
|
|
138
|
+
return this.type === 'role'
|
|
132
139
|
}
|
|
133
140
|
|
|
134
141
|
/**
|
|
135
142
|
* @returns {boolean}
|
|
136
143
|
*/
|
|
137
144
|
isNull() {
|
|
138
|
-
return this.type === null
|
|
145
|
+
return this.type === null
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
/**
|
|
142
149
|
* @returns {boolean}
|
|
143
150
|
*/
|
|
144
151
|
isXPath() {
|
|
145
|
-
return this.type === 'xpath'
|
|
152
|
+
return this.type === 'xpath'
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
/**
|
|
149
156
|
* @returns {boolean}
|
|
150
157
|
*/
|
|
151
158
|
isCustom() {
|
|
152
|
-
return !!this.type && !locatorTypes.includes(this.type)
|
|
159
|
+
return !!this.type && !locatorTypes.includes(this.type)
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
/**
|
|
156
163
|
* @returns {boolean}
|
|
157
164
|
*/
|
|
158
165
|
isStrict() {
|
|
159
|
-
return this.strict
|
|
166
|
+
return this.strict
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
/**
|
|
163
170
|
* @returns {boolean}
|
|
164
171
|
*/
|
|
165
172
|
isAccessibilityId() {
|
|
166
|
-
return this.isFuzzy() && this.value[0] === '~'
|
|
173
|
+
return this.isFuzzy() && this.value[0] === '~'
|
|
167
174
|
}
|
|
168
175
|
|
|
169
176
|
/**
|
|
170
177
|
* @returns {boolean}
|
|
171
178
|
*/
|
|
172
179
|
isBasic() {
|
|
173
|
-
return this.isCSS() || this.isXPath()
|
|
180
|
+
return this.isCSS() || this.isXPath()
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
/**
|
|
@@ -178,19 +185,19 @@ class Locator {
|
|
|
178
185
|
* @returns {string}
|
|
179
186
|
*/
|
|
180
187
|
toXPath(pseudoSelector = '') {
|
|
181
|
-
const locator = `${this.value}${pseudoSelector}
|
|
182
|
-
const limitation = [':nth-of-type', ':first-of-type', ':last-of-type', ':nth-last-child', ':nth-last-of-type', ':checked', ':disabled', ':enabled', ':required', ':lang', ':nth-child', ':has']
|
|
188
|
+
const locator = `${this.value}${pseudoSelector}`
|
|
189
|
+
const limitation = [':nth-of-type', ':first-of-type', ':last-of-type', ':nth-last-child', ':nth-last-of-type', ':checked', ':disabled', ':enabled', ':required', ':lang', ':nth-child', ':has']
|
|
183
190
|
|
|
184
191
|
if (limitation.some(item => locator.includes(item))) {
|
|
185
|
-
cssToXPath =
|
|
192
|
+
cssToXPath = require('css-to-xpath')
|
|
186
193
|
} else {
|
|
187
|
-
cssToXPath = csstoxpath
|
|
194
|
+
cssToXPath = require('csstoxpath')
|
|
188
195
|
}
|
|
189
196
|
|
|
190
|
-
if (this.isXPath()) return this.value
|
|
191
|
-
if (this.isCSS()) return cssToXPath(locator)
|
|
197
|
+
if (this.isXPath()) return this.value
|
|
198
|
+
if (this.isCSS()) return cssToXPath(locator)
|
|
192
199
|
|
|
193
|
-
throw new Error(
|
|
200
|
+
throw new Error("Can't be converted to XPath")
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
// DSL
|
|
@@ -199,11 +206,8 @@ class Locator {
|
|
|
199
206
|
* @returns {Locator}
|
|
200
207
|
*/
|
|
201
208
|
or(locator) {
|
|
202
|
-
const xpath = xpathLocator.combine([
|
|
203
|
-
|
|
204
|
-
(new Locator(locator, 'css')).toXPath(),
|
|
205
|
-
]);
|
|
206
|
-
return new Locator({ xpath });
|
|
209
|
+
const xpath = xpathLocator.combine([this.toXPath(), new Locator(locator, 'css').toXPath()])
|
|
210
|
+
return new Locator({ xpath })
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
/**
|
|
@@ -211,8 +215,8 @@ class Locator {
|
|
|
211
215
|
* @returns {Locator}
|
|
212
216
|
*/
|
|
213
217
|
find(locator) {
|
|
214
|
-
const xpath = sprintf('%s//%s', this.toXPath(), convertToSubSelector(locator))
|
|
215
|
-
return new Locator({ xpath })
|
|
218
|
+
const xpath = sprintf('%s//%s', this.toXPath(), convertToSubSelector(locator))
|
|
219
|
+
return new Locator({ xpath })
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
/**
|
|
@@ -220,8 +224,8 @@ class Locator {
|
|
|
220
224
|
* @returns {Locator}
|
|
221
225
|
*/
|
|
222
226
|
withChild(locator) {
|
|
223
|
-
const xpath = sprintf('%s[./child::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
224
|
-
return new Locator({ xpath })
|
|
227
|
+
const xpath = sprintf('%s[./child::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
228
|
+
return new Locator({ xpath })
|
|
225
229
|
}
|
|
226
230
|
|
|
227
231
|
/**
|
|
@@ -229,8 +233,8 @@ class Locator {
|
|
|
229
233
|
* @returns {Locator}
|
|
230
234
|
*/
|
|
231
235
|
withDescendant(locator) {
|
|
232
|
-
const xpath = sprintf('%s[./descendant::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
233
|
-
return new Locator({ xpath })
|
|
236
|
+
const xpath = sprintf('%s[./descendant::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
237
|
+
return new Locator({ xpath })
|
|
234
238
|
}
|
|
235
239
|
|
|
236
240
|
/**
|
|
@@ -239,33 +243,33 @@ class Locator {
|
|
|
239
243
|
*/
|
|
240
244
|
at(position) {
|
|
241
245
|
if (position === 0) {
|
|
242
|
-
throw new Error('0 is not valid element position. XPath expects first element to have index 1')
|
|
246
|
+
throw new Error('0 is not valid element position. XPath expects first element to have index 1')
|
|
243
247
|
}
|
|
244
248
|
|
|
245
|
-
let xpathPosition
|
|
249
|
+
let xpathPosition
|
|
246
250
|
|
|
247
251
|
if (position > 0) {
|
|
248
|
-
xpathPosition = position.toString()
|
|
252
|
+
xpathPosition = position.toString()
|
|
249
253
|
} else {
|
|
250
254
|
// -1 points to the last element
|
|
251
|
-
xpathPosition = `last()-${Math.abs(position + 1)}
|
|
255
|
+
xpathPosition = `last()-${Math.abs(position + 1)}`
|
|
252
256
|
}
|
|
253
|
-
const xpath = sprintf('(%s)[position()=%s]', this.toXPath(), xpathPosition)
|
|
254
|
-
return new Locator({ xpath })
|
|
257
|
+
const xpath = sprintf('(%s)[position()=%s]', this.toXPath(), xpathPosition)
|
|
258
|
+
return new Locator({ xpath })
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
/**
|
|
258
262
|
* @returns {Locator}
|
|
259
263
|
*/
|
|
260
264
|
first() {
|
|
261
|
-
return this.at(1)
|
|
265
|
+
return this.at(1)
|
|
262
266
|
}
|
|
263
267
|
|
|
264
268
|
/**
|
|
265
269
|
* @returns {Locator}
|
|
266
270
|
*/
|
|
267
271
|
last() {
|
|
268
|
-
return this.at(-1)
|
|
272
|
+
return this.at(-1)
|
|
269
273
|
}
|
|
270
274
|
|
|
271
275
|
/**
|
|
@@ -274,9 +278,9 @@ class Locator {
|
|
|
274
278
|
* @returns {Locator}
|
|
275
279
|
*/
|
|
276
280
|
withText(text) {
|
|
277
|
-
text = xpathLocator.literal(text)
|
|
278
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(., ${text})`)
|
|
279
|
-
return new Locator({ xpath })
|
|
281
|
+
text = xpathLocator.literal(text)
|
|
282
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(., ${text})`)
|
|
283
|
+
return new Locator({ xpath })
|
|
280
284
|
}
|
|
281
285
|
|
|
282
286
|
/**
|
|
@@ -285,9 +289,9 @@ class Locator {
|
|
|
285
289
|
* @returns {Locator}
|
|
286
290
|
*/
|
|
287
291
|
withTextEquals(text) {
|
|
288
|
-
text = xpathLocator.literal(text)
|
|
289
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `.= ${text}`)
|
|
290
|
-
return new Locator({ xpath })
|
|
292
|
+
text = xpathLocator.literal(text)
|
|
293
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `.= ${text}`)
|
|
294
|
+
return new Locator({ xpath })
|
|
291
295
|
}
|
|
292
296
|
|
|
293
297
|
/**
|
|
@@ -295,55 +299,54 @@ class Locator {
|
|
|
295
299
|
* @returns {Locator}
|
|
296
300
|
*/
|
|
297
301
|
withAttr(attributes) {
|
|
298
|
-
const operands = []
|
|
302
|
+
const operands = []
|
|
299
303
|
for (const attr of Object.keys(attributes)) {
|
|
300
|
-
operands.push(`@${attr} = ${xpathLocator.literal(attributes[attr])}`)
|
|
304
|
+
operands.push(`@${attr} = ${xpathLocator.literal(attributes[attr])}`)
|
|
301
305
|
}
|
|
302
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), operands.join(' and '))
|
|
303
|
-
return new Locator({ xpath })
|
|
306
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), operands.join(' and '))
|
|
307
|
+
return new Locator({ xpath })
|
|
304
308
|
}
|
|
305
309
|
|
|
306
310
|
/**
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
311
|
+
* Adds condition: attribute value starts with text
|
|
312
|
+
* (analog of XPATH: [starts-with(@attr,'startValue')] or CSS [attr^='startValue']
|
|
313
|
+
* Example: I.click(locate('a').withAttrStartsWith('href', 'https://')));
|
|
314
|
+
* Works with any attribute: class, href etc.
|
|
315
|
+
* @param {string} attrName
|
|
316
|
+
* @param {string} startsWith
|
|
317
|
+
* @returns {Locator}
|
|
318
|
+
*/
|
|
315
319
|
withAttrStartsWith(attrName, startsWith) {
|
|
316
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `starts-with(@${attrName}, "${startsWith}")`)
|
|
317
|
-
return new Locator({ xpath })
|
|
320
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `starts-with(@${attrName}, "${startsWith}")`)
|
|
321
|
+
return new Locator({ xpath })
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
/**
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
325
|
+
* Adds condition: attribute value ends with text
|
|
326
|
+
* (analog of XPATH: [ends-with(@attr,'endValue')] or CSS [attr$='endValue']
|
|
327
|
+
* Example: I.click(locate('a').withAttrEndsWith('href', '.com')));
|
|
328
|
+
* Works with any attribute: class, href etc.
|
|
329
|
+
* @param {string} attrName
|
|
330
|
+
* @param {string} endsWith
|
|
331
|
+
* @returns {Locator}
|
|
332
|
+
*/
|
|
329
333
|
withAttrEndsWith(attrName, endsWith) {
|
|
330
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `substring(@${attrName}, string-length(@${attrName}) - string-length("${endsWith}") + 1) = "${endsWith}"
|
|
331
|
-
)
|
|
332
|
-
return new Locator({ xpath });
|
|
334
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `substring(@${attrName}, string-length(@${attrName}) - string-length("${endsWith}") + 1) = "${endsWith}"`)
|
|
335
|
+
return new Locator({ xpath })
|
|
333
336
|
}
|
|
334
337
|
|
|
335
338
|
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
339
|
+
* Adds condition: attribute value contains text
|
|
340
|
+
* (analog of XPATH: [contains(@attr,'partOfAttribute')] or CSS [attr*='partOfAttribute']
|
|
341
|
+
* Example: I.click(locate('a').withAttrContains('href', 'google')));
|
|
342
|
+
* Works with any attribute: class, href etc.
|
|
343
|
+
* @param {string} attrName
|
|
344
|
+
* @param {string} partOfAttrValue
|
|
345
|
+
* @returns {Locator}
|
|
346
|
+
*/
|
|
344
347
|
withAttrContains(attrName, partOfAttrValue) {
|
|
345
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@${attrName}, "${partOfAttrValue}")`)
|
|
346
|
-
return new Locator({ xpath })
|
|
348
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@${attrName}, "${partOfAttrValue}")`)
|
|
349
|
+
return new Locator({ xpath })
|
|
347
350
|
}
|
|
348
351
|
|
|
349
352
|
/**
|
|
@@ -351,8 +354,8 @@ class Locator {
|
|
|
351
354
|
* @returns {Locator}
|
|
352
355
|
*/
|
|
353
356
|
withClassAttr(text) {
|
|
354
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@class, '${text}')`)
|
|
355
|
-
return new Locator({ xpath })
|
|
357
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@class, '${text}')`)
|
|
358
|
+
return new Locator({ xpath })
|
|
356
359
|
}
|
|
357
360
|
|
|
358
361
|
/**
|
|
@@ -360,8 +363,8 @@ class Locator {
|
|
|
360
363
|
* @returns {Locator}
|
|
361
364
|
*/
|
|
362
365
|
as(output) {
|
|
363
|
-
this.output = output
|
|
364
|
-
return this
|
|
366
|
+
this.output = output
|
|
367
|
+
return this
|
|
365
368
|
}
|
|
366
369
|
|
|
367
370
|
/**
|
|
@@ -369,8 +372,8 @@ class Locator {
|
|
|
369
372
|
* @returns {Locator}
|
|
370
373
|
*/
|
|
371
374
|
inside(locator) {
|
|
372
|
-
const xpath = sprintf('%s[ancestor::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
373
|
-
return new Locator({ xpath })
|
|
375
|
+
const xpath = sprintf('%s[ancestor::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
376
|
+
return new Locator({ xpath })
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
/**
|
|
@@ -378,8 +381,8 @@ class Locator {
|
|
|
378
381
|
* @returns {Locator}
|
|
379
382
|
*/
|
|
380
383
|
after(locator) {
|
|
381
|
-
const xpath = sprintf('%s[preceding-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
382
|
-
return new Locator({ xpath })
|
|
384
|
+
const xpath = sprintf('%s[preceding-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
385
|
+
return new Locator({ xpath })
|
|
383
386
|
}
|
|
384
387
|
|
|
385
388
|
/**
|
|
@@ -387,90 +390,95 @@ class Locator {
|
|
|
387
390
|
* @returns {Locator}
|
|
388
391
|
*/
|
|
389
392
|
before(locator) {
|
|
390
|
-
const xpath = sprintf('%s[following-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
391
|
-
return new Locator({ xpath })
|
|
393
|
+
const xpath = sprintf('%s[following-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
394
|
+
return new Locator({ xpath })
|
|
392
395
|
}
|
|
393
396
|
}
|
|
394
397
|
|
|
395
398
|
/**
|
|
396
|
-
* @param {CodeceptJS.LocatorOrString} locator
|
|
399
|
+
* @param {CodeceptJS.LocatorOrString} [locator]
|
|
397
400
|
* @returns {Locator}
|
|
398
401
|
*/
|
|
399
|
-
Locator.build =
|
|
400
|
-
if (!locator) return new Locator({ xpath: '//*' })
|
|
401
|
-
return new Locator(locator, 'css')
|
|
402
|
-
}
|
|
402
|
+
Locator.build = locator => {
|
|
403
|
+
if (!locator) return new Locator({ xpath: '//*' })
|
|
404
|
+
return new Locator(locator, 'css')
|
|
405
|
+
}
|
|
403
406
|
|
|
404
407
|
/**
|
|
405
408
|
* Filters to modify locators
|
|
406
409
|
* @type {Array<function(CodeceptJS.LocatorOrString, Locator): void>}
|
|
407
410
|
*/
|
|
408
|
-
Locator.filters = []
|
|
411
|
+
Locator.filters = []
|
|
409
412
|
|
|
410
413
|
/**
|
|
411
414
|
* Appends new `Locator` filter to an `Locator.filters` array, and returns the new length of the array.
|
|
412
415
|
* @param {function(CodeceptJS.LocatorOrString, Locator): void} fn
|
|
413
416
|
* @returns {number}
|
|
414
417
|
*/
|
|
415
|
-
Locator.addFilter = fn => Locator.filters.push(fn)
|
|
418
|
+
Locator.addFilter = fn => Locator.filters.push(fn)
|
|
416
419
|
|
|
417
420
|
Locator.clickable = {
|
|
418
421
|
/**
|
|
419
422
|
* @param {string} literal
|
|
420
423
|
* @returns {string}
|
|
421
424
|
*/
|
|
422
|
-
narrow: literal =>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
425
|
+
narrow: literal =>
|
|
426
|
+
xpathLocator.combine([
|
|
427
|
+
`.//a[normalize-space(.)=${literal}]`,
|
|
428
|
+
`.//button[normalize-space(.)=${literal}]`,
|
|
429
|
+
`.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`,
|
|
430
|
+
`.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]`,
|
|
431
|
+
]),
|
|
428
432
|
|
|
429
433
|
/**
|
|
430
434
|
* @param {string} literal
|
|
431
435
|
* @returns {string}
|
|
432
436
|
*/
|
|
433
|
-
wide: literal =>
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
437
|
+
wide: literal =>
|
|
438
|
+
xpathLocator.combine([
|
|
439
|
+
`.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`,
|
|
440
|
+
`.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`,
|
|
441
|
+
`.//input[./@type = 'image'][contains(./@alt, ${literal})]`,
|
|
442
|
+
`.//button[contains(normalize-space(string(.)), ${literal})]`,
|
|
443
|
+
`.//label[contains(normalize-space(string(.)), ${literal})]`,
|
|
444
|
+
`.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`,
|
|
445
|
+
`.//button[./@name = ${literal}]`,
|
|
446
|
+
`.//*[@aria-label = ${literal}]`,
|
|
447
|
+
`.//*[@title = ${literal}]`,
|
|
448
|
+
`.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
|
|
449
|
+
`.//*[@role='button'][normalize-space(.)=${literal}]`,
|
|
450
|
+
]),
|
|
445
451
|
|
|
446
452
|
/**
|
|
447
453
|
* @param {string} literal
|
|
448
454
|
* @returns {string}
|
|
449
455
|
*/
|
|
450
456
|
self: literal => `./self::*[contains(normalize-space(string(.)), ${literal}) or contains(normalize-space(@value), ${literal})]`,
|
|
451
|
-
}
|
|
457
|
+
}
|
|
452
458
|
|
|
453
459
|
Locator.field = {
|
|
454
460
|
/**
|
|
455
461
|
* @param {string} literal
|
|
456
462
|
* @returns {string}
|
|
457
463
|
*/
|
|
458
|
-
labelEquals: literal =>
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
464
|
+
labelEquals: literal =>
|
|
465
|
+
xpathLocator.combine([
|
|
466
|
+
`.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][((./@name = ${literal}) or ./@id = //label[@for][normalize-space(string(.)) = ${literal}]/@for or ./@placeholder = ${literal})]`,
|
|
467
|
+
`.//label[normalize-space(string(.)) = ${literal}]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
|
|
468
|
+
]),
|
|
462
469
|
|
|
463
470
|
/**
|
|
464
471
|
* @param {string} literal
|
|
465
472
|
* @returns {string}
|
|
466
473
|
*/
|
|
467
|
-
labelContains: literal =>
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
+
labelContains: literal =>
|
|
475
|
+
xpathLocator.combine([
|
|
476
|
+
`.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
|
|
477
|
+
`.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
|
|
478
|
+
`.//*[@aria-label = ${literal}]`,
|
|
479
|
+
`.//*[@title = ${literal}]`,
|
|
480
|
+
`.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
|
|
481
|
+
]),
|
|
474
482
|
|
|
475
483
|
/**
|
|
476
484
|
* @param {string} literal
|
|
@@ -482,51 +490,52 @@ Locator.field = {
|
|
|
482
490
|
* @param {string} literal
|
|
483
491
|
* @returns {string}
|
|
484
492
|
*/
|
|
485
|
-
byText: literal =>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
493
|
+
byText: literal =>
|
|
494
|
+
xpathLocator.combine([
|
|
495
|
+
`.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
|
|
496
|
+
`.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
|
|
497
|
+
]),
|
|
498
|
+
}
|
|
491
499
|
|
|
492
500
|
Locator.checkable = {
|
|
493
501
|
/**
|
|
494
502
|
* @param {string} literal
|
|
495
503
|
* @returns {string}
|
|
496
504
|
*/
|
|
497
|
-
byText: literal =>
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
505
|
+
byText: literal =>
|
|
506
|
+
xpathLocator.combine([
|
|
507
|
+
`.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`,
|
|
508
|
+
`.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']`,
|
|
509
|
+
]),
|
|
501
510
|
|
|
502
511
|
/**
|
|
503
512
|
* @param {string} literal
|
|
504
513
|
* @returns {string}
|
|
505
514
|
*/
|
|
506
515
|
byName: literal => `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`,
|
|
507
|
-
}
|
|
516
|
+
}
|
|
508
517
|
|
|
509
518
|
Locator.select = {
|
|
510
519
|
/**
|
|
511
520
|
* @param {string} opt
|
|
512
521
|
* @returns {string}
|
|
513
522
|
*/
|
|
514
|
-
byVisibleText:
|
|
515
|
-
const normalized = `[normalize-space(.) = ${opt.trim()}]
|
|
516
|
-
return `./option${normalized}|./optgroup/option${normalized}
|
|
523
|
+
byVisibleText: opt => {
|
|
524
|
+
const normalized = `[normalize-space(.) = ${opt.trim()}]`
|
|
525
|
+
return `./option${normalized}|./optgroup/option${normalized}`
|
|
517
526
|
},
|
|
518
527
|
|
|
519
528
|
/**
|
|
520
529
|
* @param {string} opt
|
|
521
530
|
* @returns {string}
|
|
522
531
|
*/
|
|
523
|
-
byValue:
|
|
524
|
-
const normalized = `[normalize-space(@value) = ${opt.trim()}]
|
|
525
|
-
return `./option${normalized}|./optgroup/option${normalized}
|
|
532
|
+
byValue: opt => {
|
|
533
|
+
const normalized = `[normalize-space(@value) = ${opt.trim()}]`
|
|
534
|
+
return `./option${normalized}|./optgroup/option${normalized}`
|
|
526
535
|
},
|
|
527
|
-
}
|
|
536
|
+
}
|
|
528
537
|
|
|
529
|
-
export default Locator
|
|
538
|
+
export default Locator
|
|
530
539
|
|
|
531
540
|
/**
|
|
532
541
|
* @private
|
|
@@ -536,7 +545,7 @@ export default Locator;
|
|
|
536
545
|
* @returns {boolean}
|
|
537
546
|
*/
|
|
538
547
|
function isCSS(locator) {
|
|
539
|
-
return locator[0] === '#' || locator[0] === '.' || locator[0] === '['
|
|
548
|
+
return locator[0] === '#' || locator[0] === '.' || locator[0] === '['
|
|
540
549
|
}
|
|
541
550
|
|
|
542
551
|
/**
|
|
@@ -547,8 +556,8 @@ function isCSS(locator) {
|
|
|
547
556
|
* @returns {boolean}
|
|
548
557
|
*/
|
|
549
558
|
function isXPath(locator) {
|
|
550
|
-
const trimmed = locator.replace(/^\(+/, '').substr(0, 2)
|
|
551
|
-
return trimmed === '//' || trimmed === './'
|
|
559
|
+
const trimmed = locator.replace(/^\(+/, '').substr(0, 2)
|
|
560
|
+
return trimmed === '//' || trimmed === './'
|
|
552
561
|
}
|
|
553
562
|
|
|
554
563
|
/**
|
|
@@ -565,8 +574,8 @@ function isXPath(locator) {
|
|
|
565
574
|
* @returns {boolean}
|
|
566
575
|
*/
|
|
567
576
|
function isShadow(locator) {
|
|
568
|
-
const hasShadowProperty =
|
|
569
|
-
return hasShadowProperty
|
|
577
|
+
const hasShadowProperty = locator.shadow !== undefined && Object.keys(locator).length === 1
|
|
578
|
+
return hasShadowProperty
|
|
570
579
|
}
|
|
571
580
|
|
|
572
581
|
/**
|
|
@@ -576,7 +585,7 @@ function isShadow(locator) {
|
|
|
576
585
|
* @returns {boolean}
|
|
577
586
|
*/
|
|
578
587
|
function isXPathStartingWithRoundBrackets(xpath) {
|
|
579
|
-
return isXPath(xpath) && xpath[0] === '('
|
|
588
|
+
return isXPath(xpath) && xpath[0] === '('
|
|
580
589
|
}
|
|
581
590
|
|
|
582
591
|
/**
|
|
@@ -586,8 +595,7 @@ function isXPathStartingWithRoundBrackets(xpath) {
|
|
|
586
595
|
* @returns {string}
|
|
587
596
|
*/
|
|
588
597
|
function removePrefix(xpath) {
|
|
589
|
-
return xpath
|
|
590
|
-
.replace(/^(\.|\/)+/, '');
|
|
598
|
+
return xpath.replace(/^(\.|\/)+/, '')
|
|
591
599
|
}
|
|
592
600
|
|
|
593
601
|
/**
|
|
@@ -597,7 +605,17 @@ function removePrefix(xpath) {
|
|
|
597
605
|
* @returns {boolean}
|
|
598
606
|
*/
|
|
599
607
|
function isPlaywrightLocator(locator) {
|
|
600
|
-
return locator.includes('_react') || locator.includes('_vue')
|
|
608
|
+
return locator.includes('_react') || locator.includes('_vue')
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* @private
|
|
613
|
+
* check if the locator is a role locator
|
|
614
|
+
* @param {{role: string}} locator
|
|
615
|
+
* @returns {boolean}
|
|
616
|
+
*/
|
|
617
|
+
function isRoleLocator(locator) {
|
|
618
|
+
return locator.role !== undefined && typeof locator.role === 'string' && Object.keys(locator).length >= 1
|
|
601
619
|
}
|
|
602
620
|
|
|
603
621
|
/**
|
|
@@ -606,10 +624,9 @@ function isPlaywrightLocator(locator) {
|
|
|
606
624
|
* @returns {string}
|
|
607
625
|
*/
|
|
608
626
|
function convertToSubSelector(locator) {
|
|
609
|
-
const xpath =
|
|
627
|
+
const xpath = new Locator(locator, 'css').toXPath()
|
|
610
628
|
if (isXPathStartingWithRoundBrackets(xpath)) {
|
|
611
|
-
throw new Error('XPath with round brackets is not possible here! '
|
|
612
|
-
+ 'May be a nested locator with at() last() or first() causes this error.');
|
|
629
|
+
throw new Error('XPath with round brackets is not possible here! ' + 'May be a nested locator with at() last() or first() causes this error.')
|
|
613
630
|
}
|
|
614
|
-
return removePrefix(xpath)
|
|
631
|
+
return removePrefix(xpath)
|
|
615
632
|
}
|