codeceptjs 4.0.0-beta.4 → 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.
Files changed (188) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +53 -54
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +70 -102
  5. package/lib/ai.js +131 -121
  6. package/lib/assert/empty.js +11 -12
  7. package/lib/assert/equal.js +16 -21
  8. package/lib/assert/error.js +2 -2
  9. package/lib/assert/include.js +11 -15
  10. package/lib/assert/throws.js +3 -5
  11. package/lib/assert/truth.js +10 -7
  12. package/lib/assert.js +18 -18
  13. package/lib/codecept.js +112 -101
  14. package/lib/colorUtils.js +48 -50
  15. package/lib/command/check.js +206 -0
  16. package/lib/command/configMigrate.js +13 -14
  17. package/lib/command/definitions.js +24 -36
  18. package/lib/command/dryRun.js +16 -16
  19. package/lib/command/generate.js +38 -39
  20. package/lib/command/gherkin/init.js +36 -38
  21. package/lib/command/gherkin/snippets.js +76 -74
  22. package/lib/command/gherkin/steps.js +21 -18
  23. package/lib/command/info.js +49 -15
  24. package/lib/command/init.js +41 -37
  25. package/lib/command/interactive.js +22 -13
  26. package/lib/command/list.js +11 -10
  27. package/lib/command/run-multiple/chunk.js +50 -47
  28. package/lib/command/run-multiple/collection.js +5 -5
  29. package/lib/command/run-multiple/run.js +3 -3
  30. package/lib/command/run-multiple.js +27 -47
  31. package/lib/command/run-rerun.js +6 -7
  32. package/lib/command/run-workers.js +15 -66
  33. package/lib/command/run.js +8 -8
  34. package/lib/command/utils.js +22 -21
  35. package/lib/command/workers/runTests.js +131 -241
  36. package/lib/config.js +111 -49
  37. package/lib/container.js +589 -244
  38. package/lib/data/context.js +16 -18
  39. package/lib/data/dataScenarioConfig.js +9 -9
  40. package/lib/data/dataTableArgument.js +7 -7
  41. package/lib/data/table.js +6 -12
  42. package/lib/effects.js +307 -0
  43. package/lib/els.js +160 -0
  44. package/lib/event.js +24 -19
  45. package/lib/globals.js +141 -0
  46. package/lib/heal.js +89 -81
  47. package/lib/helper/AI.js +3 -2
  48. package/lib/helper/ApiDataFactory.js +19 -19
  49. package/lib/helper/Appium.js +47 -51
  50. package/lib/helper/FileSystem.js +35 -15
  51. package/lib/helper/GraphQL.js +1 -1
  52. package/lib/helper/GraphQLDataFactory.js +4 -4
  53. package/lib/helper/JSONResponse.js +72 -45
  54. package/lib/helper/Mochawesome.js +14 -11
  55. package/lib/helper/Playwright.js +832 -434
  56. package/lib/helper/Puppeteer.js +393 -292
  57. package/lib/helper/REST.js +32 -27
  58. package/lib/helper/WebDriver.js +320 -219
  59. package/lib/helper/errors/ConnectionRefused.js +6 -6
  60. package/lib/helper/errors/ElementAssertion.js +11 -16
  61. package/lib/helper/errors/ElementNotFound.js +5 -9
  62. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  63. package/lib/helper/extras/Console.js +11 -11
  64. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  65. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  66. package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
  67. package/lib/helper/extras/Popup.js +22 -22
  68. package/lib/helper/extras/React.js +29 -30
  69. package/lib/helper/network/actions.js +33 -48
  70. package/lib/helper/network/utils.js +76 -83
  71. package/lib/helper/scripts/blurElement.js +6 -6
  72. package/lib/helper/scripts/focusElement.js +6 -6
  73. package/lib/helper/scripts/highlightElement.js +9 -9
  74. package/lib/helper/scripts/isElementClickable.js +34 -34
  75. package/lib/helper.js +2 -1
  76. package/lib/history.js +23 -20
  77. package/lib/hooks.js +10 -10
  78. package/lib/html.js +90 -100
  79. package/lib/index.js +48 -21
  80. package/lib/listener/config.js +8 -9
  81. package/lib/listener/emptyRun.js +54 -0
  82. package/lib/listener/exit.js +10 -12
  83. package/lib/listener/{retry.js → globalRetry.js} +10 -10
  84. package/lib/listener/globalTimeout.js +166 -0
  85. package/lib/listener/helpers.js +43 -24
  86. package/lib/listener/mocha.js +4 -5
  87. package/lib/listener/result.js +11 -0
  88. package/lib/listener/steps.js +26 -23
  89. package/lib/listener/store.js +20 -0
  90. package/lib/locator.js +213 -192
  91. package/lib/mocha/asyncWrapper.js +264 -0
  92. package/lib/mocha/bdd.js +167 -0
  93. package/lib/mocha/cli.js +341 -0
  94. package/lib/mocha/factory.js +160 -0
  95. package/lib/{interfaces → mocha}/featureConfig.js +33 -13
  96. package/lib/{interfaces → mocha}/gherkin.js +75 -45
  97. package/lib/mocha/hooks.js +121 -0
  98. package/lib/mocha/index.js +21 -0
  99. package/lib/mocha/inject.js +46 -0
  100. package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
  101. package/lib/mocha/suite.js +89 -0
  102. package/lib/mocha/test.js +178 -0
  103. package/lib/mocha/types.d.ts +42 -0
  104. package/lib/mocha/ui.js +229 -0
  105. package/lib/output.js +86 -64
  106. package/lib/parser.js +44 -44
  107. package/lib/pause.js +160 -139
  108. package/lib/plugin/analyze.js +403 -0
  109. package/lib/plugin/{autoLogin.js → auth.js} +137 -43
  110. package/lib/plugin/autoDelay.js +19 -15
  111. package/lib/plugin/coverage.js +22 -27
  112. package/lib/plugin/customLocator.js +5 -5
  113. package/lib/plugin/customReporter.js +53 -0
  114. package/lib/plugin/heal.js +49 -17
  115. package/lib/plugin/pageInfo.js +140 -0
  116. package/lib/plugin/pauseOnFail.js +4 -3
  117. package/lib/plugin/retryFailedStep.js +60 -19
  118. package/lib/plugin/screenshotOnFail.js +80 -83
  119. package/lib/plugin/stepByStepReport.js +70 -31
  120. package/lib/plugin/stepTimeout.js +7 -13
  121. package/lib/plugin/subtitles.js +10 -9
  122. package/lib/recorder.js +167 -126
  123. package/lib/rerun.js +94 -50
  124. package/lib/result.js +161 -0
  125. package/lib/secret.js +18 -17
  126. package/lib/session.js +95 -89
  127. package/lib/step/base.js +239 -0
  128. package/lib/step/comment.js +10 -0
  129. package/lib/step/config.js +50 -0
  130. package/lib/step/func.js +46 -0
  131. package/lib/step/helper.js +50 -0
  132. package/lib/step/meta.js +99 -0
  133. package/lib/step/record.js +74 -0
  134. package/lib/step/retry.js +11 -0
  135. package/lib/step/section.js +55 -0
  136. package/lib/step.js +18 -332
  137. package/lib/steps.js +54 -0
  138. package/lib/store.js +37 -5
  139. package/lib/template/heal.js +2 -11
  140. package/lib/timeout.js +60 -0
  141. package/lib/transform.js +8 -8
  142. package/lib/translation.js +32 -18
  143. package/lib/utils.js +354 -250
  144. package/lib/workerStorage.js +16 -16
  145. package/lib/workers.js +366 -282
  146. package/package.json +107 -95
  147. package/translations/de-DE.js +5 -4
  148. package/translations/fr-FR.js +5 -4
  149. package/translations/index.js +23 -9
  150. package/translations/it-IT.js +5 -4
  151. package/translations/ja-JP.js +5 -4
  152. package/translations/nl-NL.js +76 -0
  153. package/translations/pl-PL.js +5 -4
  154. package/translations/pt-BR.js +5 -4
  155. package/translations/ru-RU.js +5 -4
  156. package/translations/utils.js +18 -0
  157. package/translations/zh-CN.js +5 -4
  158. package/translations/zh-TW.js +5 -4
  159. package/typings/index.d.ts +177 -186
  160. package/typings/promiseBasedTypes.d.ts +3573 -5941
  161. package/typings/types.d.ts +4042 -6370
  162. package/lib/cli.js +0 -256
  163. package/lib/helper/ExpectHelper.js +0 -391
  164. package/lib/helper/Nightmare.js +0 -1504
  165. package/lib/helper/Protractor.js +0 -1863
  166. package/lib/helper/SoftExpectHelper.js +0 -381
  167. package/lib/helper/TestCafe.js +0 -1414
  168. package/lib/helper/clientscripts/nightmare.js +0 -213
  169. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
  170. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  171. package/lib/helper/testcafe/testcafe-utils.js +0 -62
  172. package/lib/interfaces/bdd.js +0 -81
  173. package/lib/listener/artifacts.js +0 -19
  174. package/lib/listener/timeout.js +0 -109
  175. package/lib/mochaFactory.js +0 -113
  176. package/lib/plugin/allure.js +0 -15
  177. package/lib/plugin/commentStep.js +0 -136
  178. package/lib/plugin/debugErrors.js +0 -67
  179. package/lib/plugin/eachElement.js +0 -127
  180. package/lib/plugin/fakerTransform.js +0 -49
  181. package/lib/plugin/retryTo.js +0 -127
  182. package/lib/plugin/selenoid.js +0 -384
  183. package/lib/plugin/standardActingHelpers.js +0 -3
  184. package/lib/plugin/tryTo.js +0 -115
  185. package/lib/plugin/wdio.js +0 -249
  186. package/lib/scenario.js +0 -224
  187. package/lib/ui.js +0 -236
  188. package/lib/within.js +0 -70
package/lib/locator.js CHANGED
@@ -1,9 +1,11 @@
1
- let cssToXPath;
2
- const { sprintf } = require('sprintf-js');
1
+ import { sprintf } from 'sprintf-js'
2
+ import { xpathLocator } from './utils.js'
3
+ import { createRequire } from 'module'
3
4
 
4
- const { xpathLocator } = require('./utils');
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('Can\'t be converted to XPath');
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
- this.toXPath(),
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
- * Adds condition: attribute value starts with text
304
- * (analog of XPATH: [starts-with(@attr,'startValue')] or CSS [attr^='startValue']
305
- * Example: I.click(locate('a').withAttrStartsWith('href', 'https://')));
306
- * Works with any attribute: class, href etc.
307
- * @param {string} attrName
308
- * @param {string} startsWith
309
- * @returns {Locator}
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
- * Adds condition: attribute value ends with text
318
- * (analog of XPATH: [ends-with(@attr,'endValue')] or CSS [attr$='endValue']
319
- * Example: I.click(locate('a').withAttrEndsWith('href', '.com')));
320
- * Works with any attribute: class, href etc.
321
- * @param {string} attrName
322
- * @param {string} endsWith
323
- * @returns {Locator}
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
- * Adds condition: attribute value contains text
333
- * (analog of XPATH: [contains(@attr,'partOfAttribute')] or CSS [attr*='partOfAttribute']
334
- * Example: I.click(locate('a').withAttrContains('href', 'google')));
335
- * Works with any attribute: class, href etc.
336
- * @param {string} attrName
337
- * @param {string} partOfAttrValue
338
- * @returns {Locator}
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 = (locator) => {
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 => xpathLocator.combine([
419
- `.//a[normalize-space(.)=${literal}]`,
420
- `.//button[normalize-space(.)=${literal}]`,
421
- `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`,
422
- `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]`,
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 => xpathLocator.combine([
430
- `.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`,
431
- `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`,
432
- `.//input[./@type = 'image'][contains(./@alt, ${literal})]`,
433
- `.//button[contains(normalize-space(string(.)), ${literal})]`,
434
- `.//label[contains(normalize-space(string(.)), ${literal})]`,
435
- `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`,
436
- `.//button[./@name = ${literal}]`,
437
- `.//*[@aria-label = ${literal}]`,
438
- `.//*[@title = ${literal}]`,
439
- `.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
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 => xpathLocator.combine([
455
- `.//*[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})]`,
456
- `.//label[normalize-space(string(.)) = ${literal}]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
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 => xpathLocator.combine([
464
- `.//*[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})]`,
465
- `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
466
- `.//*[@aria-label = ${literal}]`,
467
- `.//*[@title = ${literal}]`,
468
- `.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
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 => xpathLocator.combine([
482
- `.//*[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})]`,
483
- `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
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 => xpathLocator.combine([
494
- `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`,
495
- `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']`,
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: (opt) => {
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: (opt) => {
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
- module.exports = Locator;
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 = (locator.shadow !== undefined) && (Object.keys(locator).length === 1);
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 = (new Locator(locator, 'css')).toXPath();
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
  }