codeceptjs 4.0.1-beta.25 → 4.0.1-beta.26
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/bin/codecept.js +1 -2
- package/lib/command/{shell.js → interactive.js} +3 -31
- package/lib/config.js +3 -2
- package/lib/container.js +17 -3
- package/lib/helper/Playwright.js +186 -263
- package/lib/helper/Puppeteer.js +33 -115
- package/lib/helper/WebDriver.js +22 -114
- package/lib/helper/extras/PlaywrightLocator.js +34 -13
- package/lib/helper/extras/PlaywrightReactVueLocator.js +52 -0
- package/lib/locator.js +31 -117
- package/lib/mocha/test.js +4 -2
- package/lib/output.js +2 -2
- package/lib/utils/typescript.js +61 -34
- package/package.json +8 -8
- package/typings/index.d.ts +1 -1
- package/typings/promiseBasedTypes.d.ts +5475 -3929
- package/typings/types.d.ts +5767 -4092
- package/lib/helper/errors/MultipleElementsFound.js +0 -135
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -1617,30 +1617,33 @@ class Puppeteer extends Helper {
|
|
|
1617
1617
|
* {{> selectOption }}
|
|
1618
1618
|
*/
|
|
1619
1619
|
async selectOption(select, option) {
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
this.debugSection('SelectOption', `Strict: ${JSON.stringify(select)}`)
|
|
1626
|
-
const els = await this._locate(matchedLocator)
|
|
1627
|
-
assertElementExists(els, select, 'Selectable element')
|
|
1628
|
-
return proceedSelect.call(this, context, els[0], option)
|
|
1620
|
+
const els = await findVisibleFields.call(this, select)
|
|
1621
|
+
assertElementExists(els, select, 'Selectable field')
|
|
1622
|
+
const el = els[0]
|
|
1623
|
+
if ((await el.getProperty('tagName').then(t => t.jsonValue())) !== 'SELECT') {
|
|
1624
|
+
throw new Error('Element is not <select>')
|
|
1629
1625
|
}
|
|
1626
|
+
highlightActiveElement.call(this, els[0], await this._getContext())
|
|
1627
|
+
if (!Array.isArray(option)) option = [option]
|
|
1628
|
+
|
|
1629
|
+
for (const key in option) {
|
|
1630
|
+
const opt = xpathLocator.literal(option[key])
|
|
1631
|
+
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) })
|
|
1632
|
+
if (optEl.length) {
|
|
1633
|
+
this._evaluateHandeInContext(el => (el.selected = true), optEl[0])
|
|
1634
|
+
continue
|
|
1635
|
+
}
|
|
1636
|
+
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) })
|
|
1637
|
+
if (optEl.length) {
|
|
1638
|
+
this._evaluateHandeInContext(el => (el.selected = true), optEl[0])
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
await this._evaluateHandeInContext(element => {
|
|
1642
|
+
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1643
|
+
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1644
|
+
}, el)
|
|
1630
1645
|
|
|
1631
|
-
|
|
1632
|
-
this.debugSection('SelectOption', `Fuzzy: "${matchedLocator.value}"`)
|
|
1633
|
-
let els = await findByRole.call(this, context, { role: 'combobox', name: matchedLocator.value })
|
|
1634
|
-
if (els?.length) return proceedSelect.call(this, context, els[0], option)
|
|
1635
|
-
|
|
1636
|
-
// Fuzzy: try listbox
|
|
1637
|
-
els = await findByRole.call(this, context, { role: 'listbox', name: matchedLocator.value })
|
|
1638
|
-
if (els?.length) return proceedSelect.call(this, context, els[0], option)
|
|
1639
|
-
|
|
1640
|
-
// Fuzzy: try native select
|
|
1641
|
-
els = await findVisibleFields.call(this, select)
|
|
1642
|
-
assertElementExists(els, select, 'Selectable element')
|
|
1643
|
-
return proceedSelect.call(this, context, els[0], option)
|
|
1646
|
+
return this._waitForAction()
|
|
1644
1647
|
}
|
|
1645
1648
|
|
|
1646
1649
|
/**
|
|
@@ -1690,10 +1693,6 @@ class Puppeteer extends Helper {
|
|
|
1690
1693
|
* {{ react }}
|
|
1691
1694
|
*/
|
|
1692
1695
|
async see(text, context = null) {
|
|
1693
|
-
// If only one argument passed and it's an object without custom toString(), treat as locator
|
|
1694
|
-
if (!context && text && typeof text === 'object' && !Array.isArray(text) && text.toString === Object.prototype.toString) {
|
|
1695
|
-
return this.seeElement(text)
|
|
1696
|
-
}
|
|
1697
1696
|
return proceedSee.call(this, 'assert', text, context)
|
|
1698
1697
|
}
|
|
1699
1698
|
|
|
@@ -2956,7 +2955,7 @@ async function findElements(matcher, locator) {
|
|
|
2956
2955
|
async function findElement(matcher, locator) {
|
|
2957
2956
|
if (locator.react) return findReactElements.call(this, locator)
|
|
2958
2957
|
locator = new Locator(locator, 'css')
|
|
2959
|
-
|
|
2958
|
+
|
|
2960
2959
|
// Check if locator is a role locator and call findByRole
|
|
2961
2960
|
if (locator.isRole()) {
|
|
2962
2961
|
const elements = await findByRole.call(this, matcher, locator)
|
|
@@ -2968,10 +2967,13 @@ async function findElement(matcher, locator) {
|
|
|
2968
2967
|
const elements = await matcher.$$(locator.simplify())
|
|
2969
2968
|
return elements[0]
|
|
2970
2969
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2970
|
+
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
2971
|
+
if (puppeteer.default?.defaultBrowserRevision) {
|
|
2972
|
+
const elements = await matcher.$$(`xpath/${locator.value}`)
|
|
2973
|
+
return elements[0]
|
|
2974
|
+
}
|
|
2975
|
+
// For Puppeteer 24.x+, $x method was removed - use ::-p-xpath() selector
|
|
2976
|
+
const elements = await matcher.$$(`::-p-xpath(${locator.value})`)
|
|
2975
2977
|
return elements[0]
|
|
2976
2978
|
}
|
|
2977
2979
|
|
|
@@ -3161,90 +3163,6 @@ async function findFields(locator) {
|
|
|
3161
3163
|
return this._locate({ css: matchedLocator.value })
|
|
3162
3164
|
}
|
|
3163
3165
|
|
|
3164
|
-
async function proceedSelect(context, el, option) {
|
|
3165
|
-
const role = await el.evaluate(e => e.getAttribute('role'))
|
|
3166
|
-
const options = Array.isArray(option) ? option : [option]
|
|
3167
|
-
|
|
3168
|
-
if (role === 'combobox') {
|
|
3169
|
-
this.debugSection('SelectOption', 'Expanding combobox')
|
|
3170
|
-
highlightActiveElement.call(this, el, context)
|
|
3171
|
-
const [ariaOwns, ariaControls] = await el.evaluate(e => [e.getAttribute('aria-owns'), e.getAttribute('aria-controls')])
|
|
3172
|
-
await el.click()
|
|
3173
|
-
await this._waitForAction()
|
|
3174
|
-
|
|
3175
|
-
const listboxId = ariaOwns || ariaControls
|
|
3176
|
-
let listbox = listboxId ? await context.$(`#${listboxId}`) : null
|
|
3177
|
-
if (!listbox) {
|
|
3178
|
-
const listboxes = await context.$$('::-p-aria([role="listbox"])')
|
|
3179
|
-
listbox = listboxes[0]
|
|
3180
|
-
}
|
|
3181
|
-
if (!listbox) throw new Error('Cannot find listbox for combobox')
|
|
3182
|
-
|
|
3183
|
-
for (const opt of options) {
|
|
3184
|
-
const optionEls = await listbox.$$('::-p-aria([role="option"])')
|
|
3185
|
-
let optEl = null
|
|
3186
|
-
for (const optionEl of optionEls) {
|
|
3187
|
-
const text = await optionEl.evaluate(e => e.textContent.trim())
|
|
3188
|
-
if (text === opt || text.includes(opt)) {
|
|
3189
|
-
optEl = optionEl
|
|
3190
|
-
break
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
|
-
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3194
|
-
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3195
|
-
highlightActiveElement.call(this, optEl, context)
|
|
3196
|
-
await optEl.click()
|
|
3197
|
-
}
|
|
3198
|
-
return this._waitForAction()
|
|
3199
|
-
}
|
|
3200
|
-
|
|
3201
|
-
if (role === 'listbox') {
|
|
3202
|
-
highlightActiveElement.call(this, el, context)
|
|
3203
|
-
for (const opt of options) {
|
|
3204
|
-
const optionEls = await el.$$('::-p-aria([role="option"])')
|
|
3205
|
-
let optEl = null
|
|
3206
|
-
for (const optionEl of optionEls) {
|
|
3207
|
-
const text = await optionEl.evaluate(e => e.textContent.trim())
|
|
3208
|
-
if (text === opt || text.includes(opt)) {
|
|
3209
|
-
optEl = optionEl
|
|
3210
|
-
break
|
|
3211
|
-
}
|
|
3212
|
-
}
|
|
3213
|
-
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3214
|
-
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3215
|
-
highlightActiveElement.call(this, optEl, context)
|
|
3216
|
-
await optEl.click()
|
|
3217
|
-
}
|
|
3218
|
-
return this._waitForAction()
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
|
-
// Native <select> element
|
|
3222
|
-
const tagName = await el.evaluate(e => e.tagName)
|
|
3223
|
-
if (tagName !== 'SELECT') {
|
|
3224
|
-
throw new Error('Element is not <select>')
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
highlightActiveElement.call(this, el, context)
|
|
3228
|
-
for (const key in options) {
|
|
3229
|
-
const opt = xpathLocator.literal(options[key])
|
|
3230
|
-
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) })
|
|
3231
|
-
if (optEl.length) {
|
|
3232
|
-
this._evaluateHandeInContext(e => (e.selected = true), optEl[0])
|
|
3233
|
-
continue
|
|
3234
|
-
}
|
|
3235
|
-
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) })
|
|
3236
|
-
if (optEl.length) {
|
|
3237
|
-
this._evaluateHandeInContext(e => (e.selected = true), optEl[0])
|
|
3238
|
-
}
|
|
3239
|
-
}
|
|
3240
|
-
await this._evaluateHandeInContext(element => {
|
|
3241
|
-
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
3242
|
-
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
3243
|
-
}, el)
|
|
3244
|
-
|
|
3245
|
-
return this._waitForAction()
|
|
3246
|
-
}
|
|
3247
|
-
|
|
3248
3166
|
async function proceedDragAndDrop(sourceLocator, destinationLocator) {
|
|
3249
3167
|
const src = await this._locateElement(sourceLocator)
|
|
3250
3168
|
if (!src) {
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1302,29 +1302,33 @@ class WebDriver extends Helper {
|
|
|
1302
1302
|
* {{> selectOption }}
|
|
1303
1303
|
*/
|
|
1304
1304
|
async selectOption(select, option) {
|
|
1305
|
-
const
|
|
1305
|
+
const res = await findFields.call(this, select)
|
|
1306
|
+
assertElementExists(res, select, 'Selectable field')
|
|
1307
|
+
const elem = usingFirstElement(res)
|
|
1308
|
+
highlightActiveElement.call(this, elem)
|
|
1306
1309
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
this.debugSection('SelectOption', `Strict: ${JSON.stringify(select)}`)
|
|
1310
|
-
const els = await this._locate(matchedLocator)
|
|
1311
|
-
assertElementExists(els, select, 'Selectable element')
|
|
1312
|
-
return proceedSelect.call(this, els[0], option)
|
|
1310
|
+
if (!Array.isArray(option)) {
|
|
1311
|
+
option = [option]
|
|
1313
1312
|
}
|
|
1314
1313
|
|
|
1315
|
-
//
|
|
1316
|
-
this.
|
|
1317
|
-
let els = await this._locateByRole({ role: 'combobox', text: matchedLocator.value })
|
|
1318
|
-
if (els?.length) return proceedSelect.call(this, els[0], option)
|
|
1314
|
+
// select options by visible text
|
|
1315
|
+
let els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
|
|
1319
1316
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1317
|
+
const clickOptionFn = async el => {
|
|
1318
|
+
if (el[0]) el = el[0]
|
|
1319
|
+
const elementId = getElementId(el)
|
|
1320
|
+
if (elementId) return this.browser.elementClick(elementId)
|
|
1321
|
+
}
|
|
1323
1322
|
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1323
|
+
if (Array.isArray(els) && els.length) {
|
|
1324
|
+
return forEachAsync(els, clickOptionFn)
|
|
1325
|
+
}
|
|
1326
|
+
// select options by value
|
|
1327
|
+
els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
|
|
1328
|
+
if (els.length === 0) {
|
|
1329
|
+
throw new ElementNotFound(select, `Option "${option}" in`, 'was not found neither by a visible text nor by a value')
|
|
1330
|
+
}
|
|
1331
|
+
return forEachAsync(els, clickOptionFn)
|
|
1328
1332
|
}
|
|
1329
1333
|
|
|
1330
1334
|
/**
|
|
@@ -1563,10 +1567,6 @@ class WebDriver extends Helper {
|
|
|
1563
1567
|
* {{ react }}
|
|
1564
1568
|
*/
|
|
1565
1569
|
async see(text, context = null) {
|
|
1566
|
-
// If only one argument passed and it's an object without custom toString(), treat as locator
|
|
1567
|
-
if (!context && text && typeof text === 'object' && !Array.isArray(text) && text.toString === Object.prototype.toString) {
|
|
1568
|
-
return this.seeElement(text)
|
|
1569
|
-
}
|
|
1570
1570
|
return proceedSee.call(this, 'assert', text, context)
|
|
1571
1571
|
}
|
|
1572
1572
|
|
|
@@ -2403,10 +2403,6 @@ class WebDriver extends Helper {
|
|
|
2403
2403
|
})
|
|
2404
2404
|
}
|
|
2405
2405
|
|
|
2406
|
-
async _waitForAction() {
|
|
2407
|
-
return this.wait(0.1)
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
2406
|
/**
|
|
2411
2407
|
* {{> waitForEnabled }}
|
|
2412
2408
|
*/
|
|
@@ -3022,94 +3018,6 @@ async function findFields(locator) {
|
|
|
3022
3018
|
return await this._locate(locator.value) // by css or xpath
|
|
3023
3019
|
}
|
|
3024
3020
|
|
|
3025
|
-
async function proceedSelect(el, option) {
|
|
3026
|
-
const elementId = getElementId(el)
|
|
3027
|
-
const role = await this.browser.getElementAttribute(elementId, 'role')
|
|
3028
|
-
const options = Array.isArray(option) ? option : [option]
|
|
3029
|
-
|
|
3030
|
-
if (role === 'combobox') {
|
|
3031
|
-
this.debugSection('SelectOption', 'Expanding combobox')
|
|
3032
|
-
highlightActiveElement.call(this, el)
|
|
3033
|
-
const ariaOwns = await this.browser.getElementAttribute(elementId, 'aria-owns')
|
|
3034
|
-
const ariaControls = await this.browser.getElementAttribute(elementId, 'aria-controls')
|
|
3035
|
-
await this.browser.elementClick(elementId)
|
|
3036
|
-
await this._waitForAction()
|
|
3037
|
-
|
|
3038
|
-
const listboxId = ariaOwns || ariaControls
|
|
3039
|
-
let listboxEls = listboxId ? await this.browser.$$(`#${listboxId}`) : []
|
|
3040
|
-
if (!listboxEls.length) {
|
|
3041
|
-
listboxEls = await this.browser.findElementsFromElement(elementId, 'xpath', 'following-sibling::*[@role="listbox"]')
|
|
3042
|
-
}
|
|
3043
|
-
if (!listboxEls.length) {
|
|
3044
|
-
listboxEls = await this.browser.findElementsFromElement(elementId, 'xpath', 'ancestor::*[@role="listbox"]')
|
|
3045
|
-
}
|
|
3046
|
-
if (!listboxEls.length) throw new Error('Cannot find listbox for combobox')
|
|
3047
|
-
const listbox = listboxEls[0]
|
|
3048
|
-
const listboxElId = getElementId(listbox)
|
|
3049
|
-
|
|
3050
|
-
for (const opt of options) {
|
|
3051
|
-
const optionEls = await this.browser.findElementsFromElement(listboxElId, 'css selector', '[role="option"]')
|
|
3052
|
-
let optEl = null
|
|
3053
|
-
for (const optionEl of optionEls) {
|
|
3054
|
-
const optElId = getElementId(optionEl)
|
|
3055
|
-
const text = await this.browser.getElementText(optElId)
|
|
3056
|
-
if (text === opt || (text && text.includes(opt))) {
|
|
3057
|
-
optEl = optionEl
|
|
3058
|
-
break
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3062
|
-
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3063
|
-
highlightActiveElement.call(this, optEl)
|
|
3064
|
-
await this.browser.elementClick(getElementId(optEl))
|
|
3065
|
-
}
|
|
3066
|
-
return this._waitForAction()
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
if (role === 'listbox') {
|
|
3070
|
-
highlightActiveElement.call(this, el)
|
|
3071
|
-
for (const opt of options) {
|
|
3072
|
-
const optionEls = await this.browser.findElementsFromElement(elementId, 'css selector', '[role="option"]')
|
|
3073
|
-
let optEl = null
|
|
3074
|
-
for (const optionEl of optionEls) {
|
|
3075
|
-
const optElId = getElementId(optionEl)
|
|
3076
|
-
const text = await this.browser.getElementText(optElId)
|
|
3077
|
-
if (text === opt || (text && text.includes(opt))) {
|
|
3078
|
-
optEl = optionEl
|
|
3079
|
-
break
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3083
|
-
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3084
|
-
highlightActiveElement.call(this, optEl)
|
|
3085
|
-
await this.browser.elementClick(getElementId(optEl))
|
|
3086
|
-
}
|
|
3087
|
-
return this._waitForAction()
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
// Native <select> element
|
|
3091
|
-
highlightActiveElement.call(this, el)
|
|
3092
|
-
|
|
3093
|
-
// select options by visible text
|
|
3094
|
-
let els = await forEachAsync(options, async opt => this.browser.findElementsFromElement(elementId, 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
|
|
3095
|
-
|
|
3096
|
-
const clickOptionFn = async optEl => {
|
|
3097
|
-
if (optEl[0]) optEl = optEl[0]
|
|
3098
|
-
const optElId = getElementId(optEl)
|
|
3099
|
-
if (optElId) return this.browser.elementClick(optElId)
|
|
3100
|
-
}
|
|
3101
|
-
|
|
3102
|
-
if (Array.isArray(els) && els.length) {
|
|
3103
|
-
return forEachAsync(els, clickOptionFn)
|
|
3104
|
-
}
|
|
3105
|
-
// select options by value
|
|
3106
|
-
els = await forEachAsync(options, async opt => this.browser.findElementsFromElement(elementId, 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
|
|
3107
|
-
if (els.length === 0) {
|
|
3108
|
-
throw new ElementNotFound(el, `Option "${options}" in`, 'was not found neither by a visible text nor by a value')
|
|
3109
|
-
}
|
|
3110
|
-
return forEachAsync(els, clickOptionFn)
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
3021
|
async function proceedSeeField(assertType, field, value) {
|
|
3114
3022
|
const res = await findFields.call(this, field)
|
|
3115
3023
|
assertElementExists(res, field, 'Field')
|
|
@@ -11,20 +11,22 @@ function buildLocatorString(locator) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async function findElements(matcher, locator) {
|
|
14
|
-
const matchedLocator = Locator
|
|
14
|
+
const matchedLocator = new Locator(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)
|
|
18
19
|
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator)
|
|
19
20
|
|
|
20
21
|
return matcher.locator(buildLocatorString(matchedLocator)).all()
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
async function findElement(matcher, locator) {
|
|
24
|
-
const matchedLocator = Locator
|
|
25
|
+
const matchedLocator = new Locator(locator, 'css')
|
|
25
26
|
|
|
26
27
|
if (matchedLocator.type === 'react') return findReact(matcher, matchedLocator)
|
|
27
28
|
if (matchedLocator.type === 'vue') return findVue(matcher, matchedLocator)
|
|
29
|
+
if (matchedLocator.type === 'pw') return findByPlaywrightLocator(matcher, matchedLocator, { first: true })
|
|
28
30
|
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator, { first: true })
|
|
29
31
|
|
|
30
32
|
return matcher.locator(buildLocatorString(matchedLocator)).first()
|
|
@@ -44,30 +46,49 @@ async function getVisibleElements(elements) {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
async function findReact(matcher, locator) {
|
|
47
|
-
const
|
|
48
|
-
let locatorString = `_react=${
|
|
49
|
+
const details = locator.locator ?? { react: locator.value }
|
|
50
|
+
let locatorString = `_react=${details.react}`
|
|
49
51
|
|
|
50
|
-
if (props) {
|
|
51
|
-
locatorString += propBuilder(props)
|
|
52
|
+
if (details.props) {
|
|
53
|
+
locatorString += propBuilder(details.props)
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
return matcher.locator(locatorString).all()
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
async function findVue(matcher, locator) {
|
|
58
|
-
const
|
|
59
|
-
let locatorString = `_vue=${
|
|
60
|
+
const details = locator.locator ?? { vue: locator.value }
|
|
61
|
+
let locatorString = `_vue=${details.vue}`
|
|
60
62
|
|
|
61
|
-
if (props) {
|
|
62
|
-
locatorString += propBuilder(props)
|
|
63
|
+
if (details.props) {
|
|
64
|
+
locatorString += propBuilder(details.props)
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
return matcher.locator(locatorString).all()
|
|
66
68
|
}
|
|
67
69
|
|
|
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
|
+
|
|
68
78
|
async function findByRole(matcher, locator, { first = false } = {}) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
79
|
+
const details = locator.locator ?? { role: locator.value }
|
|
80
|
+
const { role, text, name, exact, includeHidden, ...rest } = details
|
|
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)
|
|
71
92
|
return first ? roleLocator.first() : roleLocator.all()
|
|
72
93
|
}
|
|
73
94
|
|
|
@@ -86,4 +107,4 @@ function propBuilder(props) {
|
|
|
86
107
|
return _props
|
|
87
108
|
}
|
|
88
109
|
|
|
89
|
-
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue, findByRole }
|
|
110
|
+
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue, findByPlaywrightLocator, findByRole }
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
async function findReact(matcher, locator) {
|
|
2
|
+
// Handle both Locator objects and raw locator objects
|
|
3
|
+
const reactLocator = locator.locator || locator
|
|
4
|
+
let _locator = `_react=${reactLocator.react}`;
|
|
5
|
+
let props = '';
|
|
6
|
+
|
|
7
|
+
if (reactLocator.props) {
|
|
8
|
+
props += propBuilder(reactLocator.props);
|
|
9
|
+
_locator += props;
|
|
10
|
+
}
|
|
11
|
+
return matcher.locator(_locator).all();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function findVue(matcher, locator) {
|
|
15
|
+
// Handle both Locator objects and raw locator objects
|
|
16
|
+
const vueLocator = locator.locator || locator
|
|
17
|
+
let _locator = `_vue=${vueLocator.vue}`;
|
|
18
|
+
let props = '';
|
|
19
|
+
|
|
20
|
+
if (vueLocator.props) {
|
|
21
|
+
props += propBuilder(vueLocator.props);
|
|
22
|
+
_locator += props;
|
|
23
|
+
}
|
|
24
|
+
return matcher.locator(_locator).all();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function findByPlaywrightLocator(matcher, locator) {
|
|
28
|
+
// Handle both Locator objects and raw locator objects
|
|
29
|
+
const pwLocator = locator.locator || locator
|
|
30
|
+
if (pwLocator && pwLocator.toString && pwLocator.toString().includes(process.env.testIdAttribute)) {
|
|
31
|
+
return matcher.getByTestId(pwLocator.pw.value.split('=')[1]);
|
|
32
|
+
}
|
|
33
|
+
const pwValue = typeof pwLocator.pw === 'string' ? pwLocator.pw : pwLocator.pw
|
|
34
|
+
return matcher.locator(pwValue).all();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function propBuilder(props) {
|
|
38
|
+
let _props = '';
|
|
39
|
+
|
|
40
|
+
for (const [key, value] of Object.entries(props)) {
|
|
41
|
+
if (typeof value === 'object') {
|
|
42
|
+
for (const [k, v] of Object.entries(value)) {
|
|
43
|
+
_props += `[${key}.${k} = "${v}"]`;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
_props += `[${key} = "${value}"]`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return _props;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { findReact, findVue, findByPlaywrightLocator };
|