codeceptjs 4.0.0-beta.5 → 4.0.0-beta.6.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 +0 -45
- package/bin/codecept.js +46 -57
- package/lib/actor.js +15 -11
- package/lib/ai.js +6 -5
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +66 -107
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +29 -26
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +34 -31
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +10 -24
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +117 -269
- package/lib/config.js +111 -49
- package/lib/container.js +299 -102
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +1 -1
- package/lib/helper/ApiDataFactory.js +16 -13
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +1 -1
- package/lib/helper/JSONResponse.js +19 -30
- package/lib/helper/Mochawesome.js +9 -28
- package/lib/helper/Playwright.js +668 -265
- package/lib/helper/Puppeteer.js +284 -169
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +191 -71
- 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/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +11 -10
- package/lib/listener/helpers.js +33 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/steps.js +7 -18
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +108 -75
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +60 -27
- package/lib/mocha/factory.js +75 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +57 -25
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -13
- package/lib/mocha/ui.js +28 -31
- package/lib/output.js +11 -9
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +12 -8
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +47 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -23
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +6 -6
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +4 -4
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +68 -97
- package/lib/workerStorage.js +16 -17
- package/lib/workers.js +145 -171
- package/package.json +63 -57
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +11 -2
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +7 -18
- package/typings/promiseBasedTypes.d.ts +3769 -5450
- package/typings/types.d.ts +3953 -5778
- package/bin/test-server.js +0 -53
- package/lib/element/WebElement.js +0 -327
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- package/lib/listener/retryEnhancer.js +0 -85
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/htmlReporter.js +0 -1947
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/test-server.js +0 -323
- package/lib/within.js +0 -90
package/lib/locator.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { sprintf } from 'sprintf-js'
|
|
2
|
+
import { xpathLocator } from './utils.js'
|
|
3
|
+
import { createRequire } from 'module'
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const require = createRequire(import.meta.url)
|
|
6
|
+
let cssToXPath
|
|
5
7
|
|
|
6
|
-
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'pw']
|
|
8
|
+
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'pw', 'role']
|
|
7
9
|
/** @class */
|
|
8
10
|
class Locator {
|
|
9
11
|
/**
|
|
@@ -11,162 +13,171 @@ class Locator {
|
|
|
11
13
|
* @param {string} [defaultType]
|
|
12
14
|
*/
|
|
13
15
|
constructor(locator, defaultType = '') {
|
|
14
|
-
this.type = null
|
|
15
|
-
if (!locator) return
|
|
16
|
+
this.type = null
|
|
17
|
+
if (!locator) return
|
|
16
18
|
|
|
17
|
-
this.output = null
|
|
19
|
+
this.output = null
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* @private
|
|
21
23
|
* @type {boolean}
|
|
22
24
|
*/
|
|
23
|
-
this.strict = false
|
|
25
|
+
this.strict = false
|
|
24
26
|
|
|
25
27
|
if (typeof locator === 'object') {
|
|
26
28
|
if (locator.constructor.name === 'Locator') {
|
|
27
|
-
Object.assign(this, locator)
|
|
28
|
-
return
|
|
29
|
+
Object.assign(this, locator)
|
|
30
|
+
return
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
this.locator = locator
|
|
32
|
-
this.type = Object.keys(locator)[0]
|
|
33
|
-
this.value = locator[this.type]
|
|
34
|
-
this.strict = true
|
|
33
|
+
this.locator = locator
|
|
34
|
+
this.type = Object.keys(locator)[0]
|
|
35
|
+
this.value = locator[this.type]
|
|
36
|
+
this.strict = true
|
|
35
37
|
|
|
36
|
-
Locator.filters.forEach(f => f(locator, this))
|
|
38
|
+
Locator.filters.forEach(f => f(locator, this))
|
|
37
39
|
|
|
38
|
-
return
|
|
40
|
+
return
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
this.type = defaultType || 'fuzzy'
|
|
42
|
-
this.output = locator
|
|
43
|
-
this.value = locator
|
|
43
|
+
this.type = defaultType || 'fuzzy'
|
|
44
|
+
this.output = locator
|
|
45
|
+
this.value = locator
|
|
44
46
|
|
|
45
47
|
if (isCSS(locator)) {
|
|
46
|
-
this.type = 'css'
|
|
48
|
+
this.type = 'css'
|
|
47
49
|
}
|
|
48
50
|
if (isXPath(locator)) {
|
|
49
|
-
this.type = 'xpath'
|
|
51
|
+
this.type = 'xpath'
|
|
50
52
|
}
|
|
51
53
|
if (isShadow(locator)) {
|
|
52
|
-
this.type = 'shadow'
|
|
54
|
+
this.type = 'shadow'
|
|
53
55
|
}
|
|
54
56
|
if (isPlaywrightLocator(locator)) {
|
|
55
|
-
this.type = 'pw'
|
|
57
|
+
this.type = 'pw'
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
Locator.filters.forEach(f => f(locator, this))
|
|
60
|
+
Locator.filters.forEach(f => f(locator, this))
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
simplify() {
|
|
62
|
-
if (this.isNull()) return null
|
|
64
|
+
if (this.isNull()) return null
|
|
63
65
|
switch (this.type) {
|
|
64
66
|
case 'by':
|
|
65
67
|
case 'xpath':
|
|
66
|
-
return this.value
|
|
68
|
+
return this.value
|
|
67
69
|
case 'css':
|
|
68
|
-
return this.value
|
|
70
|
+
return this.value
|
|
69
71
|
case 'id':
|
|
70
|
-
return `#${this.value}
|
|
72
|
+
return `#${this.value}`
|
|
71
73
|
case 'name':
|
|
72
|
-
return `[name="${this.value}"]
|
|
74
|
+
return `[name="${this.value}"]`
|
|
73
75
|
case 'fuzzy':
|
|
74
|
-
return this.value
|
|
76
|
+
return this.value
|
|
75
77
|
case 'shadow':
|
|
76
|
-
return { shadow: this.value }
|
|
78
|
+
return { shadow: this.value }
|
|
77
79
|
case 'pw':
|
|
78
|
-
return { pw: this.value }
|
|
80
|
+
return { pw: this.value }
|
|
81
|
+
case 'role':
|
|
82
|
+
return `[role="${this.value}"]`
|
|
79
83
|
}
|
|
80
|
-
return this.value
|
|
84
|
+
return this.value
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
toStrict() {
|
|
84
|
-
if (!this.type) return null
|
|
85
|
-
return { [this.type]: this.value }
|
|
88
|
+
if (!this.type) return null
|
|
89
|
+
return { [this.type]: this.value }
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/**
|
|
89
93
|
* @returns {string}
|
|
90
94
|
*/
|
|
91
95
|
toString() {
|
|
92
|
-
return this.output || `{${this.type}: ${this.value}}
|
|
96
|
+
return this.output || `{${this.type}: ${this.value}}`
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
96
100
|
* @returns {boolean}
|
|
97
101
|
*/
|
|
98
102
|
isFuzzy() {
|
|
99
|
-
return this.type === 'fuzzy'
|
|
103
|
+
return this.type === 'fuzzy'
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
/**
|
|
103
107
|
* @returns {boolean}
|
|
104
108
|
*/
|
|
105
109
|
isShadow() {
|
|
106
|
-
return this.type === 'shadow'
|
|
110
|
+
return this.type === 'shadow'
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
/**
|
|
110
114
|
* @returns {boolean}
|
|
111
115
|
*/
|
|
112
116
|
isFrame() {
|
|
113
|
-
return this.type === 'frame'
|
|
117
|
+
return this.type === 'frame'
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* @returns {boolean}
|
|
118
122
|
*/
|
|
119
123
|
isCSS() {
|
|
120
|
-
return this.type === 'css'
|
|
124
|
+
return this.type === 'css'
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
/**
|
|
124
128
|
* @returns {boolean}
|
|
125
129
|
*/
|
|
126
130
|
isPlaywrightLocator() {
|
|
127
|
-
return this.type === 'pw'
|
|
131
|
+
return this.type === 'pw'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @returns {boolean}
|
|
136
|
+
*/
|
|
137
|
+
isRole() {
|
|
138
|
+
return this.type === 'role'
|
|
128
139
|
}
|
|
129
140
|
|
|
130
141
|
/**
|
|
131
142
|
* @returns {boolean}
|
|
132
143
|
*/
|
|
133
144
|
isNull() {
|
|
134
|
-
return this.type === null
|
|
145
|
+
return this.type === null
|
|
135
146
|
}
|
|
136
147
|
|
|
137
148
|
/**
|
|
138
149
|
* @returns {boolean}
|
|
139
150
|
*/
|
|
140
151
|
isXPath() {
|
|
141
|
-
return this.type === 'xpath'
|
|
152
|
+
return this.type === 'xpath'
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
/**
|
|
145
156
|
* @returns {boolean}
|
|
146
157
|
*/
|
|
147
158
|
isCustom() {
|
|
148
|
-
return !!this.type && !locatorTypes.includes(this.type)
|
|
159
|
+
return !!this.type && !locatorTypes.includes(this.type)
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
/**
|
|
152
163
|
* @returns {boolean}
|
|
153
164
|
*/
|
|
154
165
|
isStrict() {
|
|
155
|
-
return this.strict
|
|
166
|
+
return this.strict
|
|
156
167
|
}
|
|
157
168
|
|
|
158
169
|
/**
|
|
159
170
|
* @returns {boolean}
|
|
160
171
|
*/
|
|
161
172
|
isAccessibilityId() {
|
|
162
|
-
return this.isFuzzy() && this.value[0] === '~'
|
|
173
|
+
return this.isFuzzy() && this.value[0] === '~'
|
|
163
174
|
}
|
|
164
175
|
|
|
165
176
|
/**
|
|
166
177
|
* @returns {boolean}
|
|
167
178
|
*/
|
|
168
179
|
isBasic() {
|
|
169
|
-
return this.isCSS() || this.isXPath()
|
|
180
|
+
return this.isCSS() || this.isXPath()
|
|
170
181
|
}
|
|
171
182
|
|
|
172
183
|
/**
|
|
@@ -174,19 +185,19 @@ class Locator {
|
|
|
174
185
|
* @returns {string}
|
|
175
186
|
*/
|
|
176
187
|
toXPath(pseudoSelector = '') {
|
|
177
|
-
const locator = `${this.value}${pseudoSelector}
|
|
178
|
-
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']
|
|
179
190
|
|
|
180
191
|
if (limitation.some(item => locator.includes(item))) {
|
|
181
|
-
cssToXPath = require('css-to-xpath')
|
|
192
|
+
cssToXPath = require('css-to-xpath')
|
|
182
193
|
} else {
|
|
183
|
-
cssToXPath = require('csstoxpath')
|
|
194
|
+
cssToXPath = require('csstoxpath')
|
|
184
195
|
}
|
|
185
196
|
|
|
186
|
-
if (this.isXPath()) return this.value
|
|
187
|
-
if (this.isCSS()) return cssToXPath(locator)
|
|
197
|
+
if (this.isXPath()) return this.value
|
|
198
|
+
if (this.isCSS()) return cssToXPath(locator)
|
|
188
199
|
|
|
189
|
-
throw new Error(
|
|
200
|
+
throw new Error("Can't be converted to XPath")
|
|
190
201
|
}
|
|
191
202
|
|
|
192
203
|
// DSL
|
|
@@ -195,11 +206,8 @@ class Locator {
|
|
|
195
206
|
* @returns {Locator}
|
|
196
207
|
*/
|
|
197
208
|
or(locator) {
|
|
198
|
-
const xpath = xpathLocator.combine([
|
|
199
|
-
|
|
200
|
-
(new Locator(locator, 'css')).toXPath(),
|
|
201
|
-
]);
|
|
202
|
-
return new Locator({ xpath });
|
|
209
|
+
const xpath = xpathLocator.combine([this.toXPath(), new Locator(locator, 'css').toXPath()])
|
|
210
|
+
return new Locator({ xpath })
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
/**
|
|
@@ -207,8 +215,8 @@ class Locator {
|
|
|
207
215
|
* @returns {Locator}
|
|
208
216
|
*/
|
|
209
217
|
find(locator) {
|
|
210
|
-
const xpath = sprintf('%s//%s', this.toXPath(), convertToSubSelector(locator))
|
|
211
|
-
return new Locator({ xpath })
|
|
218
|
+
const xpath = sprintf('%s//%s', this.toXPath(), convertToSubSelector(locator))
|
|
219
|
+
return new Locator({ xpath })
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
/**
|
|
@@ -216,8 +224,8 @@ class Locator {
|
|
|
216
224
|
* @returns {Locator}
|
|
217
225
|
*/
|
|
218
226
|
withChild(locator) {
|
|
219
|
-
const xpath = sprintf('%s[./child::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
220
|
-
return new Locator({ xpath })
|
|
227
|
+
const xpath = sprintf('%s[./child::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
228
|
+
return new Locator({ xpath })
|
|
221
229
|
}
|
|
222
230
|
|
|
223
231
|
/**
|
|
@@ -225,8 +233,8 @@ class Locator {
|
|
|
225
233
|
* @returns {Locator}
|
|
226
234
|
*/
|
|
227
235
|
withDescendant(locator) {
|
|
228
|
-
const xpath = sprintf('%s[./descendant::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
229
|
-
return new Locator({ xpath })
|
|
236
|
+
const xpath = sprintf('%s[./descendant::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
237
|
+
return new Locator({ xpath })
|
|
230
238
|
}
|
|
231
239
|
|
|
232
240
|
/**
|
|
@@ -235,33 +243,33 @@ class Locator {
|
|
|
235
243
|
*/
|
|
236
244
|
at(position) {
|
|
237
245
|
if (position === 0) {
|
|
238
|
-
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')
|
|
239
247
|
}
|
|
240
248
|
|
|
241
|
-
let xpathPosition
|
|
249
|
+
let xpathPosition
|
|
242
250
|
|
|
243
251
|
if (position > 0) {
|
|
244
|
-
xpathPosition = position.toString()
|
|
252
|
+
xpathPosition = position.toString()
|
|
245
253
|
} else {
|
|
246
254
|
// -1 points to the last element
|
|
247
|
-
xpathPosition = `last()-${Math.abs(position + 1)}
|
|
255
|
+
xpathPosition = `last()-${Math.abs(position + 1)}`
|
|
248
256
|
}
|
|
249
|
-
const xpath = sprintf('(%s)[position()=%s]', this.toXPath(), xpathPosition)
|
|
250
|
-
return new Locator({ xpath })
|
|
257
|
+
const xpath = sprintf('(%s)[position()=%s]', this.toXPath(), xpathPosition)
|
|
258
|
+
return new Locator({ xpath })
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
/**
|
|
254
262
|
* @returns {Locator}
|
|
255
263
|
*/
|
|
256
264
|
first() {
|
|
257
|
-
return this.at(1)
|
|
265
|
+
return this.at(1)
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
/**
|
|
261
269
|
* @returns {Locator}
|
|
262
270
|
*/
|
|
263
271
|
last() {
|
|
264
|
-
return this.at(-1)
|
|
272
|
+
return this.at(-1)
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
/**
|
|
@@ -270,9 +278,9 @@ class Locator {
|
|
|
270
278
|
* @returns {Locator}
|
|
271
279
|
*/
|
|
272
280
|
withText(text) {
|
|
273
|
-
text = xpathLocator.literal(text)
|
|
274
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(., ${text})`)
|
|
275
|
-
return new Locator({ xpath })
|
|
281
|
+
text = xpathLocator.literal(text)
|
|
282
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(., ${text})`)
|
|
283
|
+
return new Locator({ xpath })
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
/**
|
|
@@ -281,9 +289,9 @@ class Locator {
|
|
|
281
289
|
* @returns {Locator}
|
|
282
290
|
*/
|
|
283
291
|
withTextEquals(text) {
|
|
284
|
-
text = xpathLocator.literal(text)
|
|
285
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `.= ${text}`)
|
|
286
|
-
return new Locator({ xpath })
|
|
292
|
+
text = xpathLocator.literal(text)
|
|
293
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `.= ${text}`)
|
|
294
|
+
return new Locator({ xpath })
|
|
287
295
|
}
|
|
288
296
|
|
|
289
297
|
/**
|
|
@@ -291,55 +299,54 @@ class Locator {
|
|
|
291
299
|
* @returns {Locator}
|
|
292
300
|
*/
|
|
293
301
|
withAttr(attributes) {
|
|
294
|
-
const operands = []
|
|
302
|
+
const operands = []
|
|
295
303
|
for (const attr of Object.keys(attributes)) {
|
|
296
|
-
operands.push(`@${attr} = ${xpathLocator.literal(attributes[attr])}`)
|
|
304
|
+
operands.push(`@${attr} = ${xpathLocator.literal(attributes[attr])}`)
|
|
297
305
|
}
|
|
298
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), operands.join(' and '))
|
|
299
|
-
return new Locator({ xpath })
|
|
306
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), operands.join(' and '))
|
|
307
|
+
return new Locator({ xpath })
|
|
300
308
|
}
|
|
301
309
|
|
|
302
310
|
/**
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
*/
|
|
311
319
|
withAttrStartsWith(attrName, startsWith) {
|
|
312
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `starts-with(@${attrName}, "${startsWith}")`)
|
|
313
|
-
return new Locator({ xpath })
|
|
320
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `starts-with(@${attrName}, "${startsWith}")`)
|
|
321
|
+
return new Locator({ xpath })
|
|
314
322
|
}
|
|
315
323
|
|
|
316
324
|
/**
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
+
*/
|
|
325
333
|
withAttrEndsWith(attrName, endsWith) {
|
|
326
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `substring(@${attrName}, string-length(@${attrName}) - string-length("${endsWith}") + 1) = "${endsWith}"
|
|
327
|
-
)
|
|
328
|
-
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 })
|
|
329
336
|
}
|
|
330
337
|
|
|
331
338
|
/**
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
+
*/
|
|
340
347
|
withAttrContains(attrName, partOfAttrValue) {
|
|
341
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@${attrName}, "${partOfAttrValue}")`)
|
|
342
|
-
return new Locator({ xpath })
|
|
348
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@${attrName}, "${partOfAttrValue}")`)
|
|
349
|
+
return new Locator({ xpath })
|
|
343
350
|
}
|
|
344
351
|
|
|
345
352
|
/**
|
|
@@ -347,8 +354,8 @@ class Locator {
|
|
|
347
354
|
* @returns {Locator}
|
|
348
355
|
*/
|
|
349
356
|
withClassAttr(text) {
|
|
350
|
-
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@class, '${text}')`)
|
|
351
|
-
return new Locator({ xpath })
|
|
357
|
+
const xpath = sprintf('%s[%s]', this.toXPath(), `contains(@class, '${text}')`)
|
|
358
|
+
return new Locator({ xpath })
|
|
352
359
|
}
|
|
353
360
|
|
|
354
361
|
/**
|
|
@@ -356,8 +363,8 @@ class Locator {
|
|
|
356
363
|
* @returns {Locator}
|
|
357
364
|
*/
|
|
358
365
|
as(output) {
|
|
359
|
-
this.output = output
|
|
360
|
-
return this
|
|
366
|
+
this.output = output
|
|
367
|
+
return this
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
/**
|
|
@@ -365,8 +372,8 @@ class Locator {
|
|
|
365
372
|
* @returns {Locator}
|
|
366
373
|
*/
|
|
367
374
|
inside(locator) {
|
|
368
|
-
const xpath = sprintf('%s[ancestor::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
369
|
-
return new Locator({ xpath })
|
|
375
|
+
const xpath = sprintf('%s[ancestor::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
376
|
+
return new Locator({ xpath })
|
|
370
377
|
}
|
|
371
378
|
|
|
372
379
|
/**
|
|
@@ -374,8 +381,8 @@ class Locator {
|
|
|
374
381
|
* @returns {Locator}
|
|
375
382
|
*/
|
|
376
383
|
after(locator) {
|
|
377
|
-
const xpath = sprintf('%s[preceding-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
378
|
-
return new Locator({ xpath })
|
|
384
|
+
const xpath = sprintf('%s[preceding-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
385
|
+
return new Locator({ xpath })
|
|
379
386
|
}
|
|
380
387
|
|
|
381
388
|
/**
|
|
@@ -383,8 +390,8 @@ class Locator {
|
|
|
383
390
|
* @returns {Locator}
|
|
384
391
|
*/
|
|
385
392
|
before(locator) {
|
|
386
|
-
const xpath = sprintf('%s[following-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
387
|
-
return new Locator({ xpath })
|
|
393
|
+
const xpath = sprintf('%s[following-sibling::%s]', this.toXPath(), convertToSubSelector(locator))
|
|
394
|
+
return new Locator({ xpath })
|
|
388
395
|
}
|
|
389
396
|
}
|
|
390
397
|
|
|
@@ -392,81 +399,86 @@ class Locator {
|
|
|
392
399
|
* @param {CodeceptJS.LocatorOrString} [locator]
|
|
393
400
|
* @returns {Locator}
|
|
394
401
|
*/
|
|
395
|
-
Locator.build =
|
|
396
|
-
if (!locator) return new Locator({ xpath: '//*' })
|
|
397
|
-
return new Locator(locator, 'css')
|
|
398
|
-
}
|
|
402
|
+
Locator.build = locator => {
|
|
403
|
+
if (!locator) return new Locator({ xpath: '//*' })
|
|
404
|
+
return new Locator(locator, 'css')
|
|
405
|
+
}
|
|
399
406
|
|
|
400
407
|
/**
|
|
401
408
|
* Filters to modify locators
|
|
402
409
|
* @type {Array<function(CodeceptJS.LocatorOrString, Locator): void>}
|
|
403
410
|
*/
|
|
404
|
-
Locator.filters = []
|
|
411
|
+
Locator.filters = []
|
|
405
412
|
|
|
406
413
|
/**
|
|
407
414
|
* Appends new `Locator` filter to an `Locator.filters` array, and returns the new length of the array.
|
|
408
415
|
* @param {function(CodeceptJS.LocatorOrString, Locator): void} fn
|
|
409
416
|
* @returns {number}
|
|
410
417
|
*/
|
|
411
|
-
Locator.addFilter = fn => Locator.filters.push(fn)
|
|
418
|
+
Locator.addFilter = fn => Locator.filters.push(fn)
|
|
412
419
|
|
|
413
420
|
Locator.clickable = {
|
|
414
421
|
/**
|
|
415
422
|
* @param {string} literal
|
|
416
423
|
* @returns {string}
|
|
417
424
|
*/
|
|
418
|
-
narrow: literal =>
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
]),
|
|
424
432
|
|
|
425
433
|
/**
|
|
426
434
|
* @param {string} literal
|
|
427
435
|
* @returns {string}
|
|
428
436
|
*/
|
|
429
|
-
wide: literal =>
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
+
]),
|
|
441
451
|
|
|
442
452
|
/**
|
|
443
453
|
* @param {string} literal
|
|
444
454
|
* @returns {string}
|
|
445
455
|
*/
|
|
446
456
|
self: literal => `./self::*[contains(normalize-space(string(.)), ${literal}) or contains(normalize-space(@value), ${literal})]`,
|
|
447
|
-
}
|
|
457
|
+
}
|
|
448
458
|
|
|
449
459
|
Locator.field = {
|
|
450
460
|
/**
|
|
451
461
|
* @param {string} literal
|
|
452
462
|
* @returns {string}
|
|
453
463
|
*/
|
|
454
|
-
labelEquals: literal =>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
+
]),
|
|
458
469
|
|
|
459
470
|
/**
|
|
460
471
|
* @param {string} literal
|
|
461
472
|
* @returns {string}
|
|
462
473
|
*/
|
|
463
|
-
labelContains: literal =>
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
+
]),
|
|
470
482
|
|
|
471
483
|
/**
|
|
472
484
|
* @param {string} literal
|
|
@@ -478,51 +490,52 @@ Locator.field = {
|
|
|
478
490
|
* @param {string} literal
|
|
479
491
|
* @returns {string}
|
|
480
492
|
*/
|
|
481
|
-
byText: literal =>
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
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
|
+
}
|
|
487
499
|
|
|
488
500
|
Locator.checkable = {
|
|
489
501
|
/**
|
|
490
502
|
* @param {string} literal
|
|
491
503
|
* @returns {string}
|
|
492
504
|
*/
|
|
493
|
-
byText: literal =>
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
+
]),
|
|
497
510
|
|
|
498
511
|
/**
|
|
499
512
|
* @param {string} literal
|
|
500
513
|
* @returns {string}
|
|
501
514
|
*/
|
|
502
515
|
byName: literal => `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`,
|
|
503
|
-
}
|
|
516
|
+
}
|
|
504
517
|
|
|
505
518
|
Locator.select = {
|
|
506
519
|
/**
|
|
507
520
|
* @param {string} opt
|
|
508
521
|
* @returns {string}
|
|
509
522
|
*/
|
|
510
|
-
byVisibleText:
|
|
511
|
-
const normalized = `[normalize-space(.) = ${opt.trim()}]
|
|
512
|
-
return `./option${normalized}|./optgroup/option${normalized}
|
|
523
|
+
byVisibleText: opt => {
|
|
524
|
+
const normalized = `[normalize-space(.) = ${opt.trim()}]`
|
|
525
|
+
return `./option${normalized}|./optgroup/option${normalized}`
|
|
513
526
|
},
|
|
514
527
|
|
|
515
528
|
/**
|
|
516
529
|
* @param {string} opt
|
|
517
530
|
* @returns {string}
|
|
518
531
|
*/
|
|
519
|
-
byValue:
|
|
520
|
-
const normalized = `[normalize-space(@value) = ${opt.trim()}]
|
|
521
|
-
return `./option${normalized}|./optgroup/option${normalized}
|
|
532
|
+
byValue: opt => {
|
|
533
|
+
const normalized = `[normalize-space(@value) = ${opt.trim()}]`
|
|
534
|
+
return `./option${normalized}|./optgroup/option${normalized}`
|
|
522
535
|
},
|
|
523
|
-
}
|
|
536
|
+
}
|
|
524
537
|
|
|
525
|
-
|
|
538
|
+
export default Locator
|
|
526
539
|
|
|
527
540
|
/**
|
|
528
541
|
* @private
|
|
@@ -532,7 +545,7 @@ module.exports = Locator;
|
|
|
532
545
|
* @returns {boolean}
|
|
533
546
|
*/
|
|
534
547
|
function isCSS(locator) {
|
|
535
|
-
return locator[0] === '#' || locator[0] === '.' || locator[0] === '['
|
|
548
|
+
return locator[0] === '#' || locator[0] === '.' || locator[0] === '['
|
|
536
549
|
}
|
|
537
550
|
|
|
538
551
|
/**
|
|
@@ -543,8 +556,8 @@ function isCSS(locator) {
|
|
|
543
556
|
* @returns {boolean}
|
|
544
557
|
*/
|
|
545
558
|
function isXPath(locator) {
|
|
546
|
-
const trimmed = locator.replace(/^\(+/, '').substr(0, 2)
|
|
547
|
-
return trimmed === '//' || trimmed === './'
|
|
559
|
+
const trimmed = locator.replace(/^\(+/, '').substr(0, 2)
|
|
560
|
+
return trimmed === '//' || trimmed === './'
|
|
548
561
|
}
|
|
549
562
|
|
|
550
563
|
/**
|
|
@@ -561,8 +574,8 @@ function isXPath(locator) {
|
|
|
561
574
|
* @returns {boolean}
|
|
562
575
|
*/
|
|
563
576
|
function isShadow(locator) {
|
|
564
|
-
const hasShadowProperty =
|
|
565
|
-
return hasShadowProperty
|
|
577
|
+
const hasShadowProperty = locator.shadow !== undefined && Object.keys(locator).length === 1
|
|
578
|
+
return hasShadowProperty
|
|
566
579
|
}
|
|
567
580
|
|
|
568
581
|
/**
|
|
@@ -572,7 +585,7 @@ function isShadow(locator) {
|
|
|
572
585
|
* @returns {boolean}
|
|
573
586
|
*/
|
|
574
587
|
function isXPathStartingWithRoundBrackets(xpath) {
|
|
575
|
-
return isXPath(xpath) && xpath[0] === '('
|
|
588
|
+
return isXPath(xpath) && xpath[0] === '('
|
|
576
589
|
}
|
|
577
590
|
|
|
578
591
|
/**
|
|
@@ -582,8 +595,7 @@ function isXPathStartingWithRoundBrackets(xpath) {
|
|
|
582
595
|
* @returns {string}
|
|
583
596
|
*/
|
|
584
597
|
function removePrefix(xpath) {
|
|
585
|
-
return xpath
|
|
586
|
-
.replace(/^(\.|\/)+/, '');
|
|
598
|
+
return xpath.replace(/^(\.|\/)+/, '')
|
|
587
599
|
}
|
|
588
600
|
|
|
589
601
|
/**
|
|
@@ -593,7 +605,17 @@ function removePrefix(xpath) {
|
|
|
593
605
|
* @returns {boolean}
|
|
594
606
|
*/
|
|
595
607
|
function isPlaywrightLocator(locator) {
|
|
596
|
-
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
|
|
597
619
|
}
|
|
598
620
|
|
|
599
621
|
/**
|
|
@@ -602,10 +624,9 @@ function isPlaywrightLocator(locator) {
|
|
|
602
624
|
* @returns {string}
|
|
603
625
|
*/
|
|
604
626
|
function convertToSubSelector(locator) {
|
|
605
|
-
const xpath =
|
|
627
|
+
const xpath = new Locator(locator, 'css').toXPath()
|
|
606
628
|
if (isXPathStartingWithRoundBrackets(xpath)) {
|
|
607
|
-
throw new Error('XPath with round brackets is not possible here! '
|
|
608
|
-
+ '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.')
|
|
609
630
|
}
|
|
610
|
-
return removePrefix(xpath)
|
|
631
|
+
return removePrefix(xpath)
|
|
611
632
|
}
|