codeceptjs 4.0.1-beta.18 → 4.0.1-beta.19
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/lib/helper/Playwright.js +74 -80
- package/lib/helper/extras/PlaywrightLocator.js +13 -34
- package/lib/locator.js +43 -52
- package/package.json +1 -1
- package/typings/promiseBasedTypes.d.ts +220 -32
- package/typings/types.d.ts +343 -40
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
package/lib/helper/Playwright.js
CHANGED
|
@@ -30,7 +30,7 @@ import ElementNotFound from './errors/ElementNotFound.js'
|
|
|
30
30
|
import RemoteBrowserConnectionRefused from './errors/RemoteBrowserConnectionRefused.js'
|
|
31
31
|
import Popup from './extras/Popup.js'
|
|
32
32
|
import Console from './extras/Console.js'
|
|
33
|
-
import { findReact, findVue
|
|
33
|
+
import { findReact, findVue } from './extras/PlaywrightLocator.js'
|
|
34
34
|
import WebElement from '../element/WebElement.js'
|
|
35
35
|
|
|
36
36
|
let playwright
|
|
@@ -2489,7 +2489,19 @@ class Playwright extends Helper {
|
|
|
2489
2489
|
* {{> selectOption }}
|
|
2490
2490
|
*/
|
|
2491
2491
|
async selectOption(select, option) {
|
|
2492
|
-
const
|
|
2492
|
+
const selectLocator = Locator.from(select, 'css')
|
|
2493
|
+
let els = null
|
|
2494
|
+
|
|
2495
|
+
if (selectLocator.isFuzzy()) {
|
|
2496
|
+
els = await findByRole(this.page, { role: 'listbox', name: selectLocator.value })
|
|
2497
|
+
if (!els || els.length === 0) {
|
|
2498
|
+
els = await findByRole(this.page, { role: 'combobox', name: selectLocator.value })
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
if (!els || els.length === 0) {
|
|
2503
|
+
els = await findFields.call(this, select)
|
|
2504
|
+
}
|
|
2493
2505
|
assertElementExists(els, select, 'Selectable field')
|
|
2494
2506
|
const el = els[0]
|
|
2495
2507
|
|
|
@@ -2790,17 +2802,6 @@ class Playwright extends Helper {
|
|
|
2790
2802
|
*
|
|
2791
2803
|
*/
|
|
2792
2804
|
async grabTextFrom(locator) {
|
|
2793
|
-
// Handle role locators with text/exact options
|
|
2794
|
-
if (isRoleLocatorObject(locator)) {
|
|
2795
|
-
const elements = await handleRoleLocator(this.page, locator)
|
|
2796
|
-
if (elements && elements.length > 0) {
|
|
2797
|
-
const text = await elements[0].textContent()
|
|
2798
|
-
assertElementExists(text, JSON.stringify(locator))
|
|
2799
|
-
this.debugSection('Text', text)
|
|
2800
|
-
return text
|
|
2801
|
-
}
|
|
2802
|
-
}
|
|
2803
|
-
|
|
2804
2805
|
const locatorObj = new Locator(locator, 'css')
|
|
2805
2806
|
|
|
2806
2807
|
if (locatorObj.isCustom()) {
|
|
@@ -2813,21 +2814,32 @@ class Playwright extends Helper {
|
|
|
2813
2814
|
assertElementExists(text, locatorObj.toString())
|
|
2814
2815
|
this.debugSection('Text', text)
|
|
2815
2816
|
return text
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
if (locatorObj.isRole()) {
|
|
2820
|
+
// Handle role locators with text/exact options
|
|
2821
|
+
const roleElements = await findByRole(this.page, locator)
|
|
2822
|
+
if (roleElements && roleElements.length > 0) {
|
|
2823
|
+
const text = await roleElements[0].textContent()
|
|
2824
|
+
assertElementExists(text, JSON.stringify(locator))
|
|
2821
2825
|
this.debugSection('Text', text)
|
|
2822
2826
|
return text
|
|
2823
|
-
} catch (error) {
|
|
2824
|
-
// Convert Playwright timeout errors to ElementNotFound for consistency
|
|
2825
|
-
if (error.message && error.message.includes('Timeout')) {
|
|
2826
|
-
throw new ElementNotFound(locator, 'text')
|
|
2827
|
-
}
|
|
2828
|
-
throw error
|
|
2829
2827
|
}
|
|
2830
2828
|
}
|
|
2829
|
+
|
|
2830
|
+
locator = this._contextLocator(locator)
|
|
2831
|
+
try {
|
|
2832
|
+
const text = await this.page.textContent(locator)
|
|
2833
|
+
assertElementExists(text, locator)
|
|
2834
|
+
this.debugSection('Text', text)
|
|
2835
|
+
return text
|
|
2836
|
+
} catch (error) {
|
|
2837
|
+
// Convert Playwright timeout errors to ElementNotFound for consistency
|
|
2838
|
+
if (error.message && error.message.includes('Timeout')) {
|
|
2839
|
+
throw new ElementNotFound(locator, 'text')
|
|
2840
|
+
}
|
|
2841
|
+
throw error
|
|
2842
|
+
}
|
|
2831
2843
|
}
|
|
2832
2844
|
|
|
2833
2845
|
/**
|
|
@@ -4306,55 +4318,26 @@ function buildLocatorString(locator) {
|
|
|
4306
4318
|
if (locator.isXPath()) {
|
|
4307
4319
|
return `xpath=${locator.value}`
|
|
4308
4320
|
}
|
|
4309
|
-
|
|
4310
|
-
// For JSON locators, pass the entire object to Playwright
|
|
4311
|
-
// Playwright natively supports role locator objects
|
|
4312
|
-
return locator.value
|
|
4313
|
-
}
|
|
4314
|
-
return locator.simplify()
|
|
4315
|
-
}
|
|
4316
|
-
|
|
4317
|
-
/**
|
|
4318
|
-
* Checks if a locator is a role locator object (e.g., {role: 'button', text: 'Submit', exact: true})
|
|
4319
|
-
*/
|
|
4320
|
-
function isRoleLocatorObject(locator) {
|
|
4321
|
-
return locator && typeof locator === 'object' && locator.role && !locator.type
|
|
4321
|
+
return locator.simplify()
|
|
4322
4322
|
}
|
|
4323
4323
|
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
if (!isRoleLocatorObject(locator)) return null
|
|
4330
|
-
|
|
4331
|
-
const options = {}
|
|
4332
|
-
if (locator.text) options.name = locator.text
|
|
4333
|
-
if (locator.exact !== undefined) options.exact = locator.exact
|
|
4334
|
-
|
|
4335
|
-
return context.getByRole(locator.role, Object.keys(options).length > 0 ? options : undefined).all()
|
|
4324
|
+
async function findByRole(context, locator) {
|
|
4325
|
+
const matchedLocator = Locator.from(locator)
|
|
4326
|
+
if (!matchedLocator.isRole()) return null
|
|
4327
|
+
const roleOptions = matchedLocator.getRoleOptions()
|
|
4328
|
+
return context.getByRole(roleOptions.role, roleOptions.options).all()
|
|
4336
4329
|
}
|
|
4337
4330
|
|
|
4338
4331
|
async function findElements(matcher, locator) {
|
|
4339
|
-
|
|
4340
|
-
const
|
|
4341
|
-
const isVueLocator = locator.type === 'vue' || (locator.locator && locator.locator.vue) || locator.vue
|
|
4342
|
-
const isPwLocator = locator.type === 'pw' || (locator.locator && locator.locator.pw) || locator.pw
|
|
4343
|
-
|
|
4344
|
-
if (isReactLocator) return findReact(matcher, locator)
|
|
4345
|
-
if (isVueLocator) return findVue(matcher, locator)
|
|
4346
|
-
if (isPwLocator) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
4347
|
-
|
|
4348
|
-
// Handle role locators with text/exact options (e.g., {role: 'button', text: 'Submit', exact: true})
|
|
4349
|
-
const roleElements = await handleRoleLocator(matcher, locator)
|
|
4332
|
+
const matchedLocator = Locator.from(locator)
|
|
4333
|
+
const roleElements = await findByRole(matcher, matchedLocator)
|
|
4350
4334
|
if (roleElements) return roleElements
|
|
4351
4335
|
|
|
4352
|
-
|
|
4336
|
+
const isReactLocator = matchedLocator.type === 'react'
|
|
4337
|
+
const isVueLocator = matchedLocator.type === 'vue'
|
|
4353
4338
|
|
|
4354
|
-
|
|
4355
|
-
if (
|
|
4356
|
-
return findCustomElements.call(this, matcher, locator)
|
|
4357
|
-
}
|
|
4339
|
+
if (isReactLocator) return findReact(matcher, matchedLocator)
|
|
4340
|
+
if (isVueLocator) return findVue(matcher, matchedLocator)
|
|
4358
4341
|
|
|
4359
4342
|
// Check if we have a custom context locator and need to search within it
|
|
4360
4343
|
if (this.contextLocator) {
|
|
@@ -4367,12 +4350,12 @@ async function findElements(matcher, locator) {
|
|
|
4367
4350
|
}
|
|
4368
4351
|
|
|
4369
4352
|
// Search within the first context element
|
|
4370
|
-
const locatorString = buildLocatorString(
|
|
4353
|
+
const locatorString = buildLocatorString(matchedLocator)
|
|
4371
4354
|
return contextElements[0].locator(locatorString).all()
|
|
4372
4355
|
}
|
|
4373
4356
|
}
|
|
4374
4357
|
|
|
4375
|
-
const locatorString = buildLocatorString(
|
|
4358
|
+
const locatorString = buildLocatorString(matchedLocator)
|
|
4376
4359
|
|
|
4377
4360
|
return matcher.locator(locatorString).all()
|
|
4378
4361
|
}
|
|
@@ -4465,13 +4448,17 @@ async function findCustomElements(matcher, locator) {
|
|
|
4465
4448
|
}
|
|
4466
4449
|
|
|
4467
4450
|
async function findElement(matcher, locator) {
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
if (
|
|
4451
|
+
const matchedLocator = Locator.from(locator)
|
|
4452
|
+
const roleElements = await findByRole(matcher, matchedLocator)
|
|
4453
|
+
if (roleElements && roleElements.length > 0) return roleElements[0]
|
|
4454
|
+
|
|
4455
|
+
const isReactLocator = matchedLocator.type === 'react'
|
|
4456
|
+
const isVueLocator = matchedLocator.type === 'vue'
|
|
4471
4457
|
|
|
4472
|
-
|
|
4458
|
+
if (isReactLocator) return findReact(matcher, matchedLocator)
|
|
4459
|
+
if (isVueLocator) return findVue(matcher, matchedLocator)
|
|
4473
4460
|
|
|
4474
|
-
return matcher.locator(buildLocatorString(
|
|
4461
|
+
return matcher.locator(buildLocatorString(matchedLocator)).first()
|
|
4475
4462
|
}
|
|
4476
4463
|
|
|
4477
4464
|
async function getVisibleElements(elements) {
|
|
@@ -4523,8 +4510,14 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
4523
4510
|
}
|
|
4524
4511
|
|
|
4525
4512
|
async function findClickable(matcher, locator) {
|
|
4513
|
+
// Convert to Locator first to handle JSON strings properly
|
|
4526
4514
|
const matchedLocator = new Locator(locator)
|
|
4527
4515
|
|
|
4516
|
+
// Handle role locators from Locator
|
|
4517
|
+
if (matchedLocator.isRole()) {
|
|
4518
|
+
return findByRole(matcher, matchedLocator)
|
|
4519
|
+
}
|
|
4520
|
+
|
|
4528
4521
|
if (!matchedLocator.isFuzzy()) return findElements.call(this, matcher, matchedLocator)
|
|
4529
4522
|
|
|
4530
4523
|
let els
|
|
@@ -4597,14 +4590,17 @@ async function findCheckable(locator, context) {
|
|
|
4597
4590
|
}
|
|
4598
4591
|
|
|
4599
4592
|
// Handle role locators with text/exact options
|
|
4600
|
-
const roleElements = await
|
|
4593
|
+
const roleElements = await findByRole(contextEl, locator)
|
|
4601
4594
|
if (roleElements) return roleElements
|
|
4602
4595
|
|
|
4603
|
-
const matchedLocator =
|
|
4596
|
+
const matchedLocator = Locator.from(locator)
|
|
4604
4597
|
if (!matchedLocator.isFuzzy()) {
|
|
4605
4598
|
return findElements.call(this, contextEl, matchedLocator)
|
|
4606
4599
|
}
|
|
4607
4600
|
|
|
4601
|
+
const checkboxByRole = await findByRole(contextEl, { role: 'checkbox', name: matchedLocator.value })
|
|
4602
|
+
if (checkboxByRole) return checkboxByRole
|
|
4603
|
+
|
|
4608
4604
|
const literal = xpathLocator.literal(matchedLocator.value)
|
|
4609
4605
|
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
4610
4606
|
if (els.length) {
|
|
@@ -4626,17 +4622,15 @@ async function proceedIsChecked(assertType, option) {
|
|
|
4626
4622
|
}
|
|
4627
4623
|
|
|
4628
4624
|
async function findFields(locator) {
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
const roleElements = await handleRoleLocator(page, locator)
|
|
4633
|
-
if (roleElements) return roleElements
|
|
4634
|
-
}
|
|
4625
|
+
const page = await this.page
|
|
4626
|
+
const roleElements = await findByRole(page, locator)
|
|
4627
|
+
if (roleElements) return roleElements
|
|
4635
4628
|
|
|
4636
4629
|
const matchedLocator = new Locator(locator)
|
|
4637
4630
|
if (!matchedLocator.isFuzzy()) {
|
|
4638
4631
|
return this._locate(matchedLocator)
|
|
4639
4632
|
}
|
|
4633
|
+
|
|
4640
4634
|
const literal = xpathLocator.literal(locator)
|
|
4641
4635
|
|
|
4642
4636
|
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
@@ -11,22 +11,20 @@ function buildLocatorString(locator) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async function findElements(matcher, locator) {
|
|
14
|
-
const matchedLocator =
|
|
14
|
+
const matchedLocator = Locator.from(locator, 'css')
|
|
15
15
|
|
|
16
16
|
if (matchedLocator.type === 'react') return findReact(matcher, matchedLocator)
|
|
17
17
|
if (matchedLocator.type === 'vue') return findVue(matcher, matchedLocator)
|
|
18
|
-
if (matchedLocator.type === 'pw') return findByPlaywrightLocator(matcher, matchedLocator)
|
|
19
18
|
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator)
|
|
20
19
|
|
|
21
20
|
return matcher.locator(buildLocatorString(matchedLocator)).all()
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
async function findElement(matcher, locator) {
|
|
25
|
-
const matchedLocator =
|
|
24
|
+
const matchedLocator = Locator.from(locator, 'css')
|
|
26
25
|
|
|
27
26
|
if (matchedLocator.type === 'react') return findReact(matcher, matchedLocator)
|
|
28
27
|
if (matchedLocator.type === 'vue') return findVue(matcher, matchedLocator)
|
|
29
|
-
if (matchedLocator.type === 'pw') return findByPlaywrightLocator(matcher, matchedLocator, { first: true })
|
|
30
28
|
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator, { first: true })
|
|
31
29
|
|
|
32
30
|
return matcher.locator(buildLocatorString(matchedLocator)).first()
|
|
@@ -46,49 +44,30 @@ async function getVisibleElements(elements) {
|
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
async function findReact(matcher, locator) {
|
|
49
|
-
const
|
|
50
|
-
let locatorString = `_react=${
|
|
47
|
+
const props = locator.locator?.props
|
|
48
|
+
let locatorString = `_react=${locator.value}`
|
|
51
49
|
|
|
52
|
-
if (
|
|
53
|
-
locatorString += propBuilder(
|
|
50
|
+
if (props) {
|
|
51
|
+
locatorString += propBuilder(props)
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
return matcher.locator(locatorString).all()
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
async function findVue(matcher, locator) {
|
|
60
|
-
const
|
|
61
|
-
let locatorString = `_vue=${
|
|
58
|
+
const props = locator.locator?.props
|
|
59
|
+
let locatorString = `_vue=${locator.value}`
|
|
62
60
|
|
|
63
|
-
if (
|
|
64
|
-
locatorString += propBuilder(
|
|
61
|
+
if (props) {
|
|
62
|
+
locatorString += propBuilder(props)
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
return matcher.locator(locatorString).all()
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
async function findByPlaywrightLocator(matcher, locator, { first = false } = {}) {
|
|
71
|
-
const details = locator.locator ?? { pw: locator.value }
|
|
72
|
-
const locatorValue = details.pw
|
|
73
|
-
|
|
74
|
-
const handle = matcher.locator(locatorValue)
|
|
75
|
-
return first ? handle.first() : handle.all()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
68
|
async function findByRole(matcher, locator, { first = false } = {}) {
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const options = { ...rest }
|
|
82
|
-
|
|
83
|
-
if (includeHidden !== undefined) options.includeHidden = includeHidden
|
|
84
|
-
|
|
85
|
-
const accessibleName = name ?? text
|
|
86
|
-
if (accessibleName !== undefined) {
|
|
87
|
-
options.name = accessibleName
|
|
88
|
-
if (exact === true) options.exact = true
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const roleLocator = matcher.getByRole(role, options)
|
|
69
|
+
const roleOptions = locator.getRoleOptions()
|
|
70
|
+
const roleLocator = matcher.getByRole(roleOptions.role, roleOptions.options)
|
|
92
71
|
return first ? roleLocator.first() : roleLocator.all()
|
|
93
72
|
}
|
|
94
73
|
|
|
@@ -107,4 +86,4 @@ function propBuilder(props) {
|
|
|
107
86
|
return _props
|
|
108
87
|
}
|
|
109
88
|
|
|
110
|
-
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue,
|
|
89
|
+
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue, findByRole }
|
package/lib/locator.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createRequire } from 'module'
|
|
|
5
5
|
const require = createRequire(import.meta.url)
|
|
6
6
|
let cssToXPath
|
|
7
7
|
|
|
8
|
-
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', '
|
|
8
|
+
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'role']
|
|
9
9
|
/** @class */
|
|
10
10
|
class Locator {
|
|
11
11
|
/**
|
|
@@ -29,14 +29,7 @@ class Locator {
|
|
|
29
29
|
Object.assign(this, locator)
|
|
30
30
|
return
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
this.locator = locator
|
|
34
|
-
this.type = Object.keys(locator)[0]
|
|
35
|
-
this.value = locator[this.type]
|
|
36
|
-
this.strict = true
|
|
37
|
-
|
|
38
|
-
Locator.filters.forEach(f => f(locator, this))
|
|
39
|
-
|
|
32
|
+
this._applyObjectLocator(locator)
|
|
40
33
|
return
|
|
41
34
|
}
|
|
42
35
|
|
|
@@ -58,8 +51,9 @@ class Locator {
|
|
|
58
51
|
if (isShadow(locator)) {
|
|
59
52
|
this.type = 'shadow'
|
|
60
53
|
}
|
|
61
|
-
if (
|
|
62
|
-
|
|
54
|
+
if (isReactVueLocator(locator)) {
|
|
55
|
+
// React/Vue locators - keep as fuzzy type, helpers will handle them specially
|
|
56
|
+
this.type = 'fuzzy'
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
Locator.filters.forEach(f => f(locator, this))
|
|
@@ -81,18 +75,17 @@ class Locator {
|
|
|
81
75
|
return this.value
|
|
82
76
|
case 'shadow':
|
|
83
77
|
return { shadow: this.value }
|
|
84
|
-
case 'pw':
|
|
85
|
-
return { pw: this.value }
|
|
86
78
|
case 'role':
|
|
87
79
|
return `[role="${this.value}"]`
|
|
88
|
-
case 'json':
|
|
89
|
-
return { json: this.value }
|
|
90
80
|
}
|
|
91
81
|
return this.value
|
|
92
82
|
}
|
|
93
83
|
|
|
94
84
|
toStrict() {
|
|
95
85
|
if (!this.type) return null
|
|
86
|
+
if (this.type === 'role' && this.locator) {
|
|
87
|
+
return this.locator
|
|
88
|
+
}
|
|
96
89
|
return { [this.type]: this.value }
|
|
97
90
|
}
|
|
98
91
|
|
|
@@ -109,31 +102,24 @@ class Locator {
|
|
|
109
102
|
try {
|
|
110
103
|
const parsed = JSON.parse(trimmed)
|
|
111
104
|
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
112
|
-
this.
|
|
113
|
-
|
|
114
|
-
// Check if this is a multi-property JSON (like aria locators)
|
|
115
|
-
const keys = Object.keys(parsed)
|
|
116
|
-
if (keys.length > 1) {
|
|
117
|
-
// For multi-property objects, treat the entire JSON as the value
|
|
118
|
-
// with a special type to preserve all properties
|
|
119
|
-
this.type = 'json'
|
|
120
|
-
this.value = parsed
|
|
121
|
-
} else {
|
|
122
|
-
// Single property - use existing logic
|
|
123
|
-
this.type = keys[0]
|
|
124
|
-
this.value = parsed[keys[0]]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this.strict = true
|
|
128
|
-
Locator.filters.forEach(f => f(parsed, this))
|
|
105
|
+
this._applyObjectLocator(parsed)
|
|
129
106
|
return true
|
|
130
107
|
}
|
|
131
108
|
} catch (e) {
|
|
132
|
-
// continue with normal string processing
|
|
133
109
|
}
|
|
134
110
|
return false
|
|
135
111
|
}
|
|
136
112
|
|
|
113
|
+
_applyObjectLocator(locator) {
|
|
114
|
+
this.strict = true
|
|
115
|
+
this.locator = locator
|
|
116
|
+
const keys = Object.keys(locator)
|
|
117
|
+
const [type] = keys
|
|
118
|
+
this.type = type
|
|
119
|
+
this.value = keys.length > 1 ? locator : locator[type]
|
|
120
|
+
Locator.filters.forEach(f => f(locator, this))
|
|
121
|
+
}
|
|
122
|
+
|
|
137
123
|
/**
|
|
138
124
|
* @returns {string}
|
|
139
125
|
*/
|
|
@@ -169,13 +155,6 @@ class Locator {
|
|
|
169
155
|
return this.type === 'css'
|
|
170
156
|
}
|
|
171
157
|
|
|
172
|
-
/**
|
|
173
|
-
* @returns {boolean}
|
|
174
|
-
*/
|
|
175
|
-
isPlaywrightLocator() {
|
|
176
|
-
return this.type === 'pw'
|
|
177
|
-
}
|
|
178
|
-
|
|
179
158
|
/**
|
|
180
159
|
* @returns {boolean}
|
|
181
160
|
*/
|
|
@@ -184,12 +163,22 @@ class Locator {
|
|
|
184
163
|
}
|
|
185
164
|
|
|
186
165
|
/**
|
|
187
|
-
* @returns {
|
|
166
|
+
* @returns {{role: string, options: object}|null}
|
|
188
167
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
168
|
+
getRoleOptions() {
|
|
169
|
+
if (!this.isRole()) return null
|
|
170
|
+
const data = this.locator && typeof this.locator === 'object' ? this.locator : { role: this.value }
|
|
171
|
+
const { role, text, name, exact, includeHidden, ...rest } = data
|
|
172
|
+
let options = { ...rest }
|
|
173
|
+
const accessibleName = name ?? text
|
|
174
|
+
if (accessibleName !== undefined) options.name = accessibleName
|
|
175
|
+
if (exact !== undefined) options.exact = exact
|
|
176
|
+
if (includeHidden !== undefined) options.includeHidden = includeHidden
|
|
177
|
+
if (Object.keys(options).length === 0) options = undefined
|
|
178
|
+
return { role, options }
|
|
191
179
|
}
|
|
192
180
|
|
|
181
|
+
|
|
193
182
|
/**
|
|
194
183
|
* @returns {boolean}
|
|
195
184
|
*/
|
|
@@ -456,6 +445,16 @@ Locator.build = locator => {
|
|
|
456
445
|
return new Locator(locator, 'css')
|
|
457
446
|
}
|
|
458
447
|
|
|
448
|
+
/**
|
|
449
|
+
* @param {CodeceptJS.LocatorOrString|Locator} locator
|
|
450
|
+
* @param {string} [defaultType]
|
|
451
|
+
* @returns {Locator}
|
|
452
|
+
*/
|
|
453
|
+
Locator.from = (locator, defaultType = '') => {
|
|
454
|
+
if (locator instanceof Locator) return locator
|
|
455
|
+
return new Locator(locator, defaultType)
|
|
456
|
+
}
|
|
457
|
+
|
|
459
458
|
/**
|
|
460
459
|
* Filters to modify locators
|
|
461
460
|
* @type {Array<function(CodeceptJS.LocatorOrString, Locator): void>}
|
|
@@ -656,20 +655,12 @@ function removePrefix(xpath) {
|
|
|
656
655
|
* @param {string} locator
|
|
657
656
|
* @returns {boolean}
|
|
658
657
|
*/
|
|
659
|
-
function
|
|
658
|
+
function isReactVueLocator(locator) {
|
|
660
659
|
return locator.includes('_react') || locator.includes('_vue')
|
|
661
660
|
}
|
|
662
661
|
|
|
663
662
|
/**
|
|
664
663
|
* @private
|
|
665
|
-
* check if the locator is a role locator
|
|
666
|
-
* @param {{role: string}} locator
|
|
667
|
-
* @returns {boolean}
|
|
668
|
-
*/
|
|
669
|
-
function isRoleLocator(locator) {
|
|
670
|
-
return locator.role !== undefined && typeof locator.role === 'string' && Object.keys(locator).length >= 1
|
|
671
|
-
}
|
|
672
|
-
|
|
673
664
|
/**
|
|
674
665
|
* @private
|
|
675
666
|
* @param {CodeceptJS.LocatorOrString} locator
|