codeceptjs 4.0.1-beta.21 → 4.0.1-beta.22
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 +2 -1
- package/lib/command/{interactive.js → shell.js} +31 -3
- package/lib/helper/Playwright.js +58 -24
- package/lib/helper/Puppeteer.js +106 -25
- package/lib/helper/WebDriver.js +103 -22
- package/lib/locator.js +1 -1
- package/package.json +1 -1
package/bin/codecept.js
CHANGED
|
@@ -91,7 +91,8 @@ program
|
|
|
91
91
|
.option(commandFlags.profile.flag, commandFlags.profile.description)
|
|
92
92
|
.option(commandFlags.ai.flag, commandFlags.ai.description)
|
|
93
93
|
.option(commandFlags.config.flag, commandFlags.config.description)
|
|
94
|
-
.
|
|
94
|
+
.option('--file [path]', 'JavaScript file to execute in shell context')
|
|
95
|
+
.action(commandHandler('../lib/command/shell.js'))
|
|
95
96
|
|
|
96
97
|
program.command('list [path]').alias('l').description('List all actions for I.').action(commandHandler('../lib/command/list.js'))
|
|
97
98
|
|
|
@@ -5,9 +5,17 @@ import Container from '../container.js'
|
|
|
5
5
|
import event from '../event.js'
|
|
6
6
|
import pause from '../pause.js'
|
|
7
7
|
import output from '../output.js'
|
|
8
|
+
import { fileURLToPath } from 'url'
|
|
9
|
+
import { createRequire } from 'module'
|
|
10
|
+
import path from 'path'
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url)
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
14
|
+
const __dirname = path.dirname(__filename)
|
|
15
|
+
|
|
8
16
|
const webHelpers = Container.STANDARD_ACTING_HELPERS
|
|
9
17
|
|
|
10
|
-
export default async function (
|
|
18
|
+
export default async function (shellPath, options) {
|
|
11
19
|
// Backward compatibility for --profile
|
|
12
20
|
process.profile = options.profile
|
|
13
21
|
process.env.profile = options.profile
|
|
@@ -17,7 +25,7 @@ export default async function (path, options) {
|
|
|
17
25
|
const testsPath = getTestRoot(configFile)
|
|
18
26
|
|
|
19
27
|
const codecept = new Codecept(config, options)
|
|
20
|
-
codecept.init(testsPath)
|
|
28
|
+
await codecept.init(testsPath)
|
|
21
29
|
|
|
22
30
|
try {
|
|
23
31
|
await codecept.bootstrap()
|
|
@@ -53,7 +61,27 @@ export default async function (path, options) {
|
|
|
53
61
|
break
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
|
-
|
|
64
|
+
|
|
65
|
+
if (options.file) {
|
|
66
|
+
const scriptPath = path.resolve(options.file)
|
|
67
|
+
output.print(`Executing script: ${scriptPath}`)
|
|
68
|
+
|
|
69
|
+
// Use the same I actor that pause() uses
|
|
70
|
+
const I = Container.support('I')
|
|
71
|
+
global.I = I
|
|
72
|
+
globalThis.I = I
|
|
73
|
+
|
|
74
|
+
recorder.add('execute script', async () => {
|
|
75
|
+
try {
|
|
76
|
+
await import(scriptPath)
|
|
77
|
+
output.print('Script executed successfully')
|
|
78
|
+
} catch (err) {
|
|
79
|
+
output.error(`Error executing script: ${err.message}`)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
} else {
|
|
83
|
+
pause()
|
|
84
|
+
}
|
|
57
85
|
recorder.add(() => event.emit(event.test.after, {}))
|
|
58
86
|
recorder.add(() => event.emit(event.suite.after, {}))
|
|
59
87
|
recorder.add(() => event.emit(event.all.result, {}))
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -2489,35 +2489,30 @@ class Playwright extends Helper {
|
|
|
2489
2489
|
* {{> selectOption }}
|
|
2490
2490
|
*/
|
|
2491
2491
|
async selectOption(select, option) {
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2492
|
+
const context = await this.context
|
|
2493
|
+
const matchedLocator = new Locator(select)
|
|
2494
2494
|
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
if (!els || els.length === 0) {
|
|
2503
|
-
els = await findFields.call(this, select)
|
|
2495
|
+
// Strict locator
|
|
2496
|
+
if (!matchedLocator.isFuzzy()) {
|
|
2497
|
+
this.debugSection('SelectOption', `Strict: ${JSON.stringify(select)}`)
|
|
2498
|
+
const els = await this._locate(matchedLocator)
|
|
2499
|
+
assertElementExists(els, select, 'Selectable element')
|
|
2500
|
+
return proceedSelect.call(this, context, els[0], option)
|
|
2504
2501
|
}
|
|
2505
|
-
assertElementExists(els, select, 'Selectable field')
|
|
2506
|
-
const el = els[0]
|
|
2507
|
-
|
|
2508
|
-
await highlightActiveElement.call(this, el)
|
|
2509
|
-
let optionToSelect = ''
|
|
2510
2502
|
|
|
2511
|
-
try
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
}
|
|
2503
|
+
// Fuzzy: try combobox
|
|
2504
|
+
this.debugSection('SelectOption', `Fuzzy: "${matchedLocator.value}"`)
|
|
2505
|
+
let els = await findByRole(context, { role: 'combobox', name: matchedLocator.value })
|
|
2506
|
+
if (els?.length) return proceedSelect.call(this, context, els[0], option)
|
|
2516
2507
|
|
|
2517
|
-
|
|
2508
|
+
// Fuzzy: try listbox
|
|
2509
|
+
els = await findByRole(context, { role: 'listbox', name: matchedLocator.value })
|
|
2510
|
+
if (els?.length) return proceedSelect.call(this, context, els[0], option)
|
|
2518
2511
|
|
|
2519
|
-
|
|
2520
|
-
|
|
2512
|
+
// Fuzzy: try native select
|
|
2513
|
+
els = await findFields.call(this, select)
|
|
2514
|
+
assertElementExists(els, select, 'Selectable element')
|
|
2515
|
+
return proceedSelect.call(this, context, els[0], option)
|
|
2521
2516
|
}
|
|
2522
2517
|
|
|
2523
2518
|
/**
|
|
@@ -4649,6 +4644,45 @@ async function findFields(locator) {
|
|
|
4649
4644
|
return this._locate({ css: locator })
|
|
4650
4645
|
}
|
|
4651
4646
|
|
|
4647
|
+
async function proceedSelect(context, el, option) {
|
|
4648
|
+
const role = await el.getAttribute('role')
|
|
4649
|
+
const options = Array.isArray(option) ? option : [option]
|
|
4650
|
+
|
|
4651
|
+
if (role === 'combobox') {
|
|
4652
|
+
this.debugSection('SelectOption', 'Expanding combobox')
|
|
4653
|
+
await highlightActiveElement.call(this, el)
|
|
4654
|
+
const [ariaOwns, ariaControls] = await Promise.all([el.getAttribute('aria-owns'), el.getAttribute('aria-controls')])
|
|
4655
|
+
await el.click()
|
|
4656
|
+
await this._waitForAction()
|
|
4657
|
+
|
|
4658
|
+
const listboxId = ariaOwns || ariaControls
|
|
4659
|
+
let listbox = listboxId ? context.locator(`#${listboxId}`).first() : null
|
|
4660
|
+
if (!listbox || !(await listbox.count())) listbox = context.getByRole('listbox').first()
|
|
4661
|
+
|
|
4662
|
+
for (const opt of options) {
|
|
4663
|
+
const optEl = listbox.getByRole('option', { name: opt }).first()
|
|
4664
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
4665
|
+
await highlightActiveElement.call(this, optEl)
|
|
4666
|
+
await optEl.click()
|
|
4667
|
+
}
|
|
4668
|
+
return this._waitForAction()
|
|
4669
|
+
}
|
|
4670
|
+
|
|
4671
|
+
if (role === 'listbox') {
|
|
4672
|
+
for (const opt of options) {
|
|
4673
|
+
const optEl = el.getByRole('option', { name: opt }).first()
|
|
4674
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
4675
|
+
await highlightActiveElement.call(this, optEl)
|
|
4676
|
+
await optEl.click()
|
|
4677
|
+
}
|
|
4678
|
+
return this._waitForAction()
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4681
|
+
await highlightActiveElement.call(this, el)
|
|
4682
|
+
await el.selectOption(option)
|
|
4683
|
+
return this._waitForAction()
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4652
4686
|
async function proceedSeeInField(assertType, field, value) {
|
|
4653
4687
|
const els = await findFields.call(this, field)
|
|
4654
4688
|
assertElementExists(els, field, 'Field')
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -1617,33 +1617,30 @@ class Puppeteer extends Helper {
|
|
|
1617
1617
|
* {{> selectOption }}
|
|
1618
1618
|
*/
|
|
1619
1619
|
async selectOption(select, option) {
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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
|
-
}
|
|
1620
|
+
const context = await this._getContext()
|
|
1621
|
+
const matchedLocator = new Locator(select)
|
|
1622
|
+
|
|
1623
|
+
// Strict locator
|
|
1624
|
+
if (!matchedLocator.isFuzzy()) {
|
|
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)
|
|
1640
1629
|
}
|
|
1641
|
-
await this._evaluateHandeInContext(element => {
|
|
1642
|
-
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1643
|
-
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1644
|
-
}, el)
|
|
1645
1630
|
|
|
1646
|
-
|
|
1631
|
+
// Fuzzy: try combobox
|
|
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)
|
|
1647
1644
|
}
|
|
1648
1645
|
|
|
1649
1646
|
/**
|
|
@@ -3160,6 +3157,90 @@ async function findFields(locator) {
|
|
|
3160
3157
|
return this._locate({ css: matchedLocator.value })
|
|
3161
3158
|
}
|
|
3162
3159
|
|
|
3160
|
+
async function proceedSelect(context, el, option) {
|
|
3161
|
+
const role = await el.evaluate(e => e.getAttribute('role'))
|
|
3162
|
+
const options = Array.isArray(option) ? option : [option]
|
|
3163
|
+
|
|
3164
|
+
if (role === 'combobox') {
|
|
3165
|
+
this.debugSection('SelectOption', 'Expanding combobox')
|
|
3166
|
+
highlightActiveElement.call(this, el, context)
|
|
3167
|
+
const [ariaOwns, ariaControls] = await el.evaluate(e => [e.getAttribute('aria-owns'), e.getAttribute('aria-controls')])
|
|
3168
|
+
await el.click()
|
|
3169
|
+
await this._waitForAction()
|
|
3170
|
+
|
|
3171
|
+
const listboxId = ariaOwns || ariaControls
|
|
3172
|
+
let listbox = listboxId ? await context.$(`#${listboxId}`) : null
|
|
3173
|
+
if (!listbox) {
|
|
3174
|
+
const listboxes = await context.$$('::-p-aria([role="listbox"])')
|
|
3175
|
+
listbox = listboxes[0]
|
|
3176
|
+
}
|
|
3177
|
+
if (!listbox) throw new Error('Cannot find listbox for combobox')
|
|
3178
|
+
|
|
3179
|
+
for (const opt of options) {
|
|
3180
|
+
const optionEls = await listbox.$$('::-p-aria([role="option"])')
|
|
3181
|
+
let optEl = null
|
|
3182
|
+
for (const optionEl of optionEls) {
|
|
3183
|
+
const text = await optionEl.evaluate(e => e.textContent.trim())
|
|
3184
|
+
if (text === opt || text.includes(opt)) {
|
|
3185
|
+
optEl = optionEl
|
|
3186
|
+
break
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3190
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3191
|
+
highlightActiveElement.call(this, optEl, context)
|
|
3192
|
+
await optEl.click()
|
|
3193
|
+
}
|
|
3194
|
+
return this._waitForAction()
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
if (role === 'listbox') {
|
|
3198
|
+
highlightActiveElement.call(this, el, context)
|
|
3199
|
+
for (const opt of options) {
|
|
3200
|
+
const optionEls = await el.$$('::-p-aria([role="option"])')
|
|
3201
|
+
let optEl = null
|
|
3202
|
+
for (const optionEl of optionEls) {
|
|
3203
|
+
const text = await optionEl.evaluate(e => e.textContent.trim())
|
|
3204
|
+
if (text === opt || text.includes(opt)) {
|
|
3205
|
+
optEl = optionEl
|
|
3206
|
+
break
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3210
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3211
|
+
highlightActiveElement.call(this, optEl, context)
|
|
3212
|
+
await optEl.click()
|
|
3213
|
+
}
|
|
3214
|
+
return this._waitForAction()
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
// Native <select> element
|
|
3218
|
+
const tagName = await el.evaluate(e => e.tagName)
|
|
3219
|
+
if (tagName !== 'SELECT') {
|
|
3220
|
+
throw new Error('Element is not <select>')
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
highlightActiveElement.call(this, el, context)
|
|
3224
|
+
for (const key in options) {
|
|
3225
|
+
const opt = xpathLocator.literal(options[key])
|
|
3226
|
+
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) })
|
|
3227
|
+
if (optEl.length) {
|
|
3228
|
+
this._evaluateHandeInContext(e => (e.selected = true), optEl[0])
|
|
3229
|
+
continue
|
|
3230
|
+
}
|
|
3231
|
+
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) })
|
|
3232
|
+
if (optEl.length) {
|
|
3233
|
+
this._evaluateHandeInContext(e => (e.selected = true), optEl[0])
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
await this._evaluateHandeInContext(element => {
|
|
3237
|
+
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
3238
|
+
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
3239
|
+
}, el)
|
|
3240
|
+
|
|
3241
|
+
return this._waitForAction()
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3163
3244
|
async function proceedDragAndDrop(sourceLocator, destinationLocator) {
|
|
3164
3245
|
const src = await this._locateElement(sourceLocator)
|
|
3165
3246
|
if (!src) {
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1302,33 +1302,29 @@ class WebDriver extends Helper {
|
|
|
1302
1302
|
* {{> selectOption }}
|
|
1303
1303
|
*/
|
|
1304
1304
|
async selectOption(select, option) {
|
|
1305
|
-
const
|
|
1306
|
-
assertElementExists(res, select, 'Selectable field')
|
|
1307
|
-
const elem = usingFirstElement(res)
|
|
1308
|
-
highlightActiveElement.call(this, elem)
|
|
1305
|
+
const matchedLocator = new Locator(select)
|
|
1309
1306
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1307
|
+
// Strict locator
|
|
1308
|
+
if (!matchedLocator.isFuzzy()) {
|
|
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)
|
|
1312
1313
|
}
|
|
1313
1314
|
|
|
1314
|
-
//
|
|
1315
|
-
|
|
1315
|
+
// Fuzzy: try combobox
|
|
1316
|
+
this.debugSection('SelectOption', `Fuzzy: "${matchedLocator.value}"`)
|
|
1317
|
+
let els = await this._locateByRole({ role: 'combobox', text: matchedLocator.value })
|
|
1318
|
+
if (els?.length) return proceedSelect.call(this, els[0], option)
|
|
1316
1319
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
if (elementId) return this.browser.elementClick(elementId)
|
|
1321
|
-
}
|
|
1320
|
+
// Fuzzy: try listbox
|
|
1321
|
+
els = await this._locateByRole({ role: 'listbox', text: matchedLocator.value })
|
|
1322
|
+
if (els?.length) return proceedSelect.call(this, els[0], option)
|
|
1322
1323
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
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)
|
|
1324
|
+
// Fuzzy: try native select
|
|
1325
|
+
els = await findFields.call(this, select)
|
|
1326
|
+
assertElementExists(els, select, 'Selectable element')
|
|
1327
|
+
return proceedSelect.call(this, els[0], option)
|
|
1332
1328
|
}
|
|
1333
1329
|
|
|
1334
1330
|
/**
|
|
@@ -3018,6 +3014,91 @@ async function findFields(locator) {
|
|
|
3018
3014
|
return await this._locate(locator.value) // by css or xpath
|
|
3019
3015
|
}
|
|
3020
3016
|
|
|
3017
|
+
async function proceedSelect(el, option) {
|
|
3018
|
+
const elementId = getElementId(el)
|
|
3019
|
+
const role = await this.browser.getElementAttribute(elementId, 'role')
|
|
3020
|
+
const options = Array.isArray(option) ? option : [option]
|
|
3021
|
+
|
|
3022
|
+
if (role === 'combobox') {
|
|
3023
|
+
this.debugSection('SelectOption', 'Expanding combobox')
|
|
3024
|
+
highlightActiveElement.call(this, el)
|
|
3025
|
+
const ariaOwns = await this.browser.getElementAttribute(elementId, 'aria-owns')
|
|
3026
|
+
const ariaControls = await this.browser.getElementAttribute(elementId, 'aria-controls')
|
|
3027
|
+
await this.browser.elementClick(elementId)
|
|
3028
|
+
await this._waitForAction()
|
|
3029
|
+
|
|
3030
|
+
const listboxId = ariaOwns || ariaControls
|
|
3031
|
+
let listboxEls = listboxId ? await this.browser.$$(`#${listboxId}`) : []
|
|
3032
|
+
if (!listboxEls.length) {
|
|
3033
|
+
listboxEls = await this.browser.$$('[role="listbox"]')
|
|
3034
|
+
}
|
|
3035
|
+
if (!listboxEls.length) throw new Error('Cannot find listbox for combobox')
|
|
3036
|
+
const listbox = listboxEls[0]
|
|
3037
|
+
const listboxElId = getElementId(listbox)
|
|
3038
|
+
|
|
3039
|
+
for (const opt of options) {
|
|
3040
|
+
const optionEls = await this.browser.findElementsFromElement(listboxElId, 'css selector', '[role="option"]')
|
|
3041
|
+
let optEl = null
|
|
3042
|
+
for (const optionEl of optionEls) {
|
|
3043
|
+
const optElId = getElementId(optionEl)
|
|
3044
|
+
const text = await this.browser.getElementText(optElId)
|
|
3045
|
+
if (text === opt || (text && text.includes(opt))) {
|
|
3046
|
+
optEl = optionEl
|
|
3047
|
+
break
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3051
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3052
|
+
highlightActiveElement.call(this, optEl)
|
|
3053
|
+
await this.browser.elementClick(getElementId(optEl))
|
|
3054
|
+
}
|
|
3055
|
+
return this._waitForAction()
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
if (role === 'listbox') {
|
|
3059
|
+
highlightActiveElement.call(this, el)
|
|
3060
|
+
for (const opt of options) {
|
|
3061
|
+
const optionEls = await this.browser.findElementsFromElement(elementId, 'css selector', '[role="option"]')
|
|
3062
|
+
let optEl = null
|
|
3063
|
+
for (const optionEl of optionEls) {
|
|
3064
|
+
const optElId = getElementId(optionEl)
|
|
3065
|
+
const text = await this.browser.getElementText(optElId)
|
|
3066
|
+
if (text === opt || (text && text.includes(opt))) {
|
|
3067
|
+
optEl = optionEl
|
|
3068
|
+
break
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
if (!optEl) throw new Error(`Cannot find option "${opt}" in listbox`)
|
|
3072
|
+
this.debugSection('SelectOption', `Clicking: "${opt}"`)
|
|
3073
|
+
highlightActiveElement.call(this, optEl)
|
|
3074
|
+
await this.browser.elementClick(getElementId(optEl))
|
|
3075
|
+
}
|
|
3076
|
+
return this._waitForAction()
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
// Native <select> element
|
|
3080
|
+
highlightActiveElement.call(this, el)
|
|
3081
|
+
|
|
3082
|
+
// select options by visible text
|
|
3083
|
+
let els = await forEachAsync(options, async opt => this.browser.findElementsFromElement(elementId, 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
|
|
3084
|
+
|
|
3085
|
+
const clickOptionFn = async optEl => {
|
|
3086
|
+
if (optEl[0]) optEl = optEl[0]
|
|
3087
|
+
const optElId = getElementId(optEl)
|
|
3088
|
+
if (optElId) return this.browser.elementClick(optElId)
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
if (Array.isArray(els) && els.length) {
|
|
3092
|
+
return forEachAsync(els, clickOptionFn)
|
|
3093
|
+
}
|
|
3094
|
+
// select options by value
|
|
3095
|
+
els = await forEachAsync(options, async opt => this.browser.findElementsFromElement(elementId, 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
|
|
3096
|
+
if (els.length === 0) {
|
|
3097
|
+
throw new ElementNotFound(el, `Option "${options}" in`, 'was not found neither by a visible text nor by a value')
|
|
3098
|
+
}
|
|
3099
|
+
return forEachAsync(els, clickOptionFn)
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3021
3102
|
async function proceedSeeField(assertType, field, value) {
|
|
3022
3103
|
const res = await findFields.call(this, field)
|
|
3023
3104
|
assertElementExists(res, field, 'Field')
|
package/lib/locator.js
CHANGED
|
@@ -500,7 +500,7 @@ Locator.clickable = {
|
|
|
500
500
|
`.//*[@aria-label = ${literal}]`,
|
|
501
501
|
`.//*[@title = ${literal}]`,
|
|
502
502
|
`.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
|
|
503
|
-
`.//*[@role='
|
|
503
|
+
`.//*[@role='link' | @role='tab' | @role='menuitem' | @role='menuitemcheckbox' | @role='menuitemradio' | @role='option' | @role='radio' | @role='checkbox' | @role='switch' | @role='treeitem' | @role='gridcell' | @role='columnheader' | @role='rowheader' | @role='scrollbar' | @role='slider' | @role='spinbutton'][normalize-space(.)='${literal}']`,
|
|
504
504
|
]),
|
|
505
505
|
|
|
506
506
|
/**
|