codeceptjs 4.0.1-beta.23 → 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.
@@ -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 } from './extras/PlaywrightLocator.js'
33
+ import { findReact, findVue, findByPlaywrightLocator } from './extras/PlaywrightReactVueLocator.js'
34
34
  import WebElement from '../element/WebElement.js'
35
35
 
36
36
  let playwright
@@ -44,6 +44,36 @@ if (typeof global.__playwrightSelectorsRegistered === 'undefined') {
44
44
  global.__playwrightSelectorsRegistered = false
45
45
  }
46
46
 
47
+ /**
48
+ * Creates a Playwright selector engine factory for a custom locator strategy.
49
+ * @param {string} name - Strategy name for error messages
50
+ * @param {Function} func - The locator function (selector, root) => Element|Element[]
51
+ * @returns {Function} Selector engine factory
52
+ */
53
+ function createCustomSelectorEngine(name, func) {
54
+ return () => ({
55
+ create: () => null,
56
+ query(root, selector) {
57
+ if (!root) return null
58
+ try {
59
+ const result = func(selector, root)
60
+ return Array.isArray(result) ? result[0] : result
61
+ } catch (e) {
62
+ return null
63
+ }
64
+ },
65
+ queryAll(root, selector) {
66
+ if (!root) return []
67
+ try {
68
+ const result = func(selector, root)
69
+ return Array.isArray(result) ? result : result ? [result] : []
70
+ } catch (e) {
71
+ return []
72
+ }
73
+ },
74
+ })
75
+ }
76
+
47
77
  const popupStore = new Popup()
48
78
  const consoleLogStore = new Console()
49
79
  const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron']
@@ -358,23 +388,13 @@ class Playwright extends Helper {
358
388
 
359
389
  // Filter out invalid customLocatorStrategies (empty arrays, objects without functions)
360
390
  // This can happen in worker threads where config is serialized/deserialized
361
- let validCustomLocators = null
362
- if (typeof config.customLocatorStrategies === 'object' && config.customLocatorStrategies !== null) {
363
- // Check if it's an empty array or object with no function properties
364
- const entries = Object.entries(config.customLocatorStrategies)
365
- const hasFunctions = entries.some(([_, value]) => typeof value === 'function')
366
- if (hasFunctions) {
367
- validCustomLocators = config.customLocatorStrategies
368
- }
369
- }
370
-
371
- this.customLocatorStrategies = validCustomLocators
391
+ this.customLocatorStrategies = this._parseCustomLocatorStrategies(config.customLocatorStrategies)
372
392
  this._customLocatorsRegistered = false
373
393
 
374
394
  // Add custom locator strategies to global registry for early registration
375
395
  if (this.customLocatorStrategies) {
376
- for (const [strategyName, strategyFunction] of Object.entries(this.customLocatorStrategies)) {
377
- globalCustomLocatorStrategies.set(strategyName, strategyFunction)
396
+ for (const [name, func] of Object.entries(this.customLocatorStrategies)) {
397
+ globalCustomLocatorStrategies.set(name, func)
378
398
  }
379
399
  }
380
400
 
@@ -565,54 +585,23 @@ class Playwright extends Helper {
565
585
  }
566
586
 
567
587
  // Register all custom locator strategies from the global registry
568
- for (const [strategyName, strategyFunction] of globalCustomLocatorStrategies.entries()) {
569
- if (!registeredCustomLocatorStrategies.has(strategyName)) {
570
- try {
571
- // Create a selector engine factory function exactly like createValueEngine pattern
572
- // Capture variables in closure to avoid reference issues
573
- const createCustomEngine = ((name, func) => {
574
- return () => {
575
- return {
576
- create() {
577
- return null
578
- },
579
- query(root, selector) {
580
- try {
581
- if (!root) return null
582
- const result = func(selector, root)
583
- return Array.isArray(result) ? result[0] : result
584
- } catch (error) {
585
- console.warn(`Error in custom locator "${name}":`, error)
586
- return null
587
- }
588
- },
589
- queryAll(root, selector) {
590
- try {
591
- if (!root) return []
592
- const result = func(selector, root)
593
- return Array.isArray(result) ? result : result ? [result] : []
594
- } catch (error) {
595
- console.warn(`Error in custom locator "${name}":`, error)
596
- return []
597
- }
598
- },
599
- }
600
- }
601
- })(strategyName, strategyFunction)
588
+ await this._registerGlobalCustomLocators()
589
+ } catch (e) {
590
+ console.warn(e)
591
+ }
592
+ }
602
593
 
603
- await playwright.selectors.register(strategyName, createCustomEngine)
604
- registeredCustomLocatorStrategies.add(strategyName)
605
- } catch (error) {
606
- if (!error.message.includes('already registered')) {
607
- console.warn(`Failed to register custom locator strategy '${strategyName}':`, error)
608
- } else {
609
- console.log(`Custom locator strategy '${strategyName}' already registered`)
610
- }
611
- }
594
+ async _registerGlobalCustomLocators() {
595
+ for (const [name, func] of globalCustomLocatorStrategies.entries()) {
596
+ if (registeredCustomLocatorStrategies.has(name)) continue
597
+ try {
598
+ await playwright.selectors.register(name, createCustomSelectorEngine(name, func))
599
+ registeredCustomLocatorStrategies.add(name)
600
+ } catch (e) {
601
+ if (!e.message.includes('already registered')) {
602
+ this.debugSection('Custom Locator', `Failed to register '${name}': ${e.message}`)
612
603
  }
613
604
  }
614
- } catch (e) {
615
- console.warn(e)
616
605
  }
617
606
  }
618
607
 
@@ -1277,28 +1266,31 @@ class Playwright extends Helper {
1277
1266
  return this.browser
1278
1267
  }
1279
1268
 
1269
+ _hasCustomLocatorStrategies() {
1270
+ return !!(this.customLocatorStrategies && Object.keys(this.customLocatorStrategies).length > 0)
1271
+ }
1272
+
1273
+ _parseCustomLocatorStrategies(strategies) {
1274
+ if (typeof strategies !== 'object' || strategies === null) return null
1275
+ const hasValidFunctions = Object.values(strategies).some(v => typeof v === 'function')
1276
+ return hasValidFunctions ? strategies : null
1277
+ }
1278
+
1280
1279
  _lookupCustomLocator(customStrategy) {
1281
- if (typeof this.customLocatorStrategies !== 'object' || this.customLocatorStrategies === null) {
1282
- return null
1283
- }
1280
+ if (!this._hasCustomLocatorStrategies()) return null
1284
1281
  const strategy = this.customLocatorStrategies[customStrategy]
1285
1282
  return typeof strategy === 'function' ? strategy : null
1286
1283
  }
1287
1284
 
1288
1285
  _isCustomLocator(locator) {
1289
1286
  const locatorObj = new Locator(locator)
1290
- if (locatorObj.isCustom()) {
1291
- const customLocator = this._lookupCustomLocator(locatorObj.type)
1292
- if (customLocator) {
1293
- return true
1294
- }
1295
- throw new Error('Please define "customLocatorStrategies" as an Object and the Locator Strategy as a "function".')
1296
- }
1297
- return false
1287
+ if (!locatorObj.isCustom()) return false
1288
+ if (this._lookupCustomLocator(locatorObj.type)) return true
1289
+ throw new Error('Please define "customLocatorStrategies" as an Object and the Locator Strategy as a "function".')
1298
1290
  }
1299
1291
 
1300
1292
  _isCustomLocatorStrategyDefined() {
1301
- return !!(this.customLocatorStrategies && Object.keys(this.customLocatorStrategies).length > 0)
1293
+ return this._hasCustomLocatorStrategies()
1302
1294
  }
1303
1295
 
1304
1296
  /**
@@ -1321,49 +1313,16 @@ class Playwright extends Helper {
1321
1313
  }
1322
1314
 
1323
1315
  async _registerCustomLocatorStrategies() {
1324
- if (!this.customLocatorStrategies) return
1325
-
1326
- for (const [strategyName, strategyFunction] of Object.entries(this.customLocatorStrategies)) {
1327
- if (!registeredCustomLocatorStrategies.has(strategyName)) {
1328
- try {
1329
- const createCustomEngine = ((name, func) => {
1330
- return () => {
1331
- return {
1332
- create(root, target) {
1333
- return null
1334
- },
1335
- query(root, selector) {
1336
- try {
1337
- if (!root) return null
1338
- const result = func(selector, root)
1339
- return Array.isArray(result) ? result[0] : result
1340
- } catch (error) {
1341
- console.warn(`Error in custom locator "${name}":`, error)
1342
- return null
1343
- }
1344
- },
1345
- queryAll(root, selector) {
1346
- try {
1347
- if (!root) return []
1348
- const result = func(selector, root)
1349
- return Array.isArray(result) ? result : result ? [result] : []
1350
- } catch (error) {
1351
- console.warn(`Error in custom locator "${name}":`, error)
1352
- return []
1353
- }
1354
- },
1355
- }
1356
- }
1357
- })(strategyName, strategyFunction)
1316
+ if (!this._hasCustomLocatorStrategies()) return
1358
1317
 
1359
- await playwright.selectors.register(strategyName, createCustomEngine)
1360
- registeredCustomLocatorStrategies.add(strategyName)
1361
- } catch (error) {
1362
- if (!error.message.includes('already registered')) {
1363
- console.warn(`Failed to register custom locator strategy '${strategyName}':`, error)
1364
- } else {
1365
- console.log(`Custom locator strategy '${strategyName}' already registered`)
1366
- }
1318
+ for (const [name, func] of Object.entries(this.customLocatorStrategies)) {
1319
+ if (registeredCustomLocatorStrategies.has(name)) continue
1320
+ try {
1321
+ await playwright.selectors.register(name, createCustomSelectorEngine(name, func))
1322
+ registeredCustomLocatorStrategies.add(name)
1323
+ } catch (e) {
1324
+ if (!e.message.includes('already registered')) {
1325
+ this.debugSection('Custom Locator', `Failed to register '${name}': ${e.message}`)
1367
1326
  }
1368
1327
  }
1369
1328
  }
@@ -2489,30 +2448,23 @@ class Playwright extends Helper {
2489
2448
  * {{> selectOption }}
2490
2449
  */
2491
2450
  async selectOption(select, option) {
2492
- const context = await this.context
2493
- const matchedLocator = new Locator(select)
2451
+ const els = await findFields.call(this, select)
2452
+ assertElementExists(els, select, 'Selectable field')
2453
+ const el = els[0]
2494
2454
 
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)
2501
- }
2455
+ await highlightActiveElement.call(this, el)
2456
+ let optionToSelect = ''
2502
2457
 
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)
2458
+ try {
2459
+ optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
2460
+ } catch (e) {
2461
+ optionToSelect = option
2462
+ }
2507
2463
 
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)
2464
+ if (!Array.isArray(option)) option = [optionToSelect]
2511
2465
 
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)
2466
+ await el.selectOption(option)
2467
+ return this._waitForAction()
2516
2468
  }
2517
2469
 
2518
2470
  /**
@@ -2797,6 +2749,17 @@ class Playwright extends Helper {
2797
2749
  *
2798
2750
  */
2799
2751
  async grabTextFrom(locator) {
2752
+ // Handle role locators with text/exact options
2753
+ if (isRoleLocatorObject(locator)) {
2754
+ const elements = await handleRoleLocator(this.page, locator)
2755
+ if (elements && elements.length > 0) {
2756
+ const text = await elements[0].textContent()
2757
+ assertElementExists(text, JSON.stringify(locator))
2758
+ this.debugSection('Text', text)
2759
+ return text
2760
+ }
2761
+ }
2762
+
2800
2763
  const locatorObj = new Locator(locator, 'css')
2801
2764
 
2802
2765
  if (locatorObj.isCustom()) {
@@ -2809,32 +2772,21 @@ class Playwright extends Helper {
2809
2772
  assertElementExists(text, locatorObj.toString())
2810
2773
  this.debugSection('Text', text)
2811
2774
  return text
2812
- }
2813
-
2814
- if (locatorObj.isRole()) {
2815
- // Handle role locators with text/exact options
2816
- const roleElements = await findByRole(this.page, locator)
2817
- if (roleElements && roleElements.length > 0) {
2818
- const text = await roleElements[0].textContent()
2819
- assertElementExists(text, JSON.stringify(locator))
2775
+ } else {
2776
+ locator = this._contextLocator(locator)
2777
+ try {
2778
+ const text = await this.page.textContent(locator)
2779
+ assertElementExists(text, locator)
2820
2780
  this.debugSection('Text', text)
2821
2781
  return text
2782
+ } catch (error) {
2783
+ // Convert Playwright timeout errors to ElementNotFound for consistency
2784
+ if (error.message && error.message.includes('Timeout')) {
2785
+ throw new ElementNotFound(locator, 'text')
2786
+ }
2787
+ throw error
2822
2788
  }
2823
2789
  }
2824
-
2825
- locator = this._contextLocator(locator)
2826
- try {
2827
- const text = await this.page.textContent(locator)
2828
- assertElementExists(text, locator)
2829
- this.debugSection('Text', text)
2830
- return text
2831
- } catch (error) {
2832
- // Convert Playwright timeout errors to ElementNotFound for consistency
2833
- if (error.message && error.message.includes('Timeout')) {
2834
- throw new ElementNotFound(locator, 'text')
2835
- }
2836
- throw error
2837
- }
2838
2790
  }
2839
2791
 
2840
2792
  /**
@@ -4313,26 +4265,50 @@ function buildLocatorString(locator) {
4313
4265
  if (locator.isXPath()) {
4314
4266
  return `xpath=${locator.value}`
4315
4267
  }
4316
- return locator.simplify()
4268
+ return locator.simplify()
4317
4269
  }
4318
4270
 
4319
- async function findByRole(context, locator) {
4320
- const matchedLocator = Locator.from(locator)
4321
- if (!matchedLocator.isRole()) return null
4322
- const roleOptions = matchedLocator.getRoleOptions()
4323
- return context.getByRole(roleOptions.role, roleOptions.options).all()
4271
+ /**
4272
+ * Checks if a locator is a role locator object (e.g., {role: 'button', text: 'Submit', exact: true})
4273
+ */
4274
+ function isRoleLocatorObject(locator) {
4275
+ return locator && typeof locator === 'object' && locator.role && !locator.type
4276
+ }
4277
+
4278
+ /**
4279
+ * Handles role locator objects by converting them to Playwright's getByRole() API
4280
+ * Returns elements array if role locator, null otherwise
4281
+ */
4282
+ async function handleRoleLocator(context, locator) {
4283
+ if (!isRoleLocatorObject(locator)) return null
4284
+
4285
+ const options = {}
4286
+ if (locator.text) options.name = locator.text
4287
+ if (locator.exact !== undefined) options.exact = locator.exact
4288
+
4289
+ return context.getByRole(locator.role, Object.keys(options).length > 0 ? options : undefined).all()
4324
4290
  }
4325
4291
 
4326
4292
  async function findElements(matcher, locator) {
4327
- const matchedLocator = Locator.from(locator)
4328
- const roleElements = await findByRole(matcher, matchedLocator)
4293
+ // Check if locator is a Locator object with react/vue type, or a raw object with react/vue property
4294
+ const isReactLocator = locator.type === 'react' || (locator.locator && locator.locator.react) || locator.react
4295
+ const isVueLocator = locator.type === 'vue' || (locator.locator && locator.locator.vue) || locator.vue
4296
+ const isPwLocator = locator.type === 'pw' || (locator.locator && locator.locator.pw) || locator.pw
4297
+
4298
+ if (isReactLocator) return findReact(matcher, locator)
4299
+ if (isVueLocator) return findVue(matcher, locator)
4300
+ if (isPwLocator) return findByPlaywrightLocator.call(this, matcher, locator)
4301
+
4302
+ // Handle role locators with text/exact options (e.g., {role: 'button', text: 'Submit', exact: true})
4303
+ const roleElements = await handleRoleLocator(matcher, locator)
4329
4304
  if (roleElements) return roleElements
4330
4305
 
4331
- const isReactLocator = matchedLocator.type === 'react'
4332
- const isVueLocator = matchedLocator.type === 'vue'
4306
+ locator = new Locator(locator, 'css')
4333
4307
 
4334
- if (isReactLocator) return findReact(matcher, matchedLocator)
4335
- if (isVueLocator) return findVue(matcher, matchedLocator)
4308
+ // Handle custom locators directly instead of relying on Playwright selector engines
4309
+ if (locator.isCustom()) {
4310
+ return findCustomElements.call(this, matcher, locator)
4311
+ }
4336
4312
 
4337
4313
  // Check if we have a custom context locator and need to search within it
4338
4314
  if (this.contextLocator) {
@@ -4345,12 +4321,12 @@ async function findElements(matcher, locator) {
4345
4321
  }
4346
4322
 
4347
4323
  // Search within the first context element
4348
- const locatorString = buildLocatorString(matchedLocator)
4324
+ const locatorString = buildLocatorString(locator)
4349
4325
  return contextElements[0].locator(locatorString).all()
4350
4326
  }
4351
4327
  }
4352
4328
 
4353
- const locatorString = buildLocatorString(matchedLocator)
4329
+ const locatorString = buildLocatorString(locator)
4354
4330
 
4355
4331
  return matcher.locator(locatorString).all()
4356
4332
  }
@@ -4443,17 +4419,13 @@ async function findCustomElements(matcher, locator) {
4443
4419
  }
4444
4420
 
4445
4421
  async function findElement(matcher, locator) {
4446
- const matchedLocator = Locator.from(locator)
4447
- const roleElements = await findByRole(matcher, matchedLocator)
4448
- if (roleElements && roleElements.length > 0) return roleElements[0]
4449
-
4450
- const isReactLocator = matchedLocator.type === 'react'
4451
- const isVueLocator = matchedLocator.type === 'vue'
4422
+ if (locator.react) return findReact(matcher, locator)
4423
+ if (locator.vue) return findVue(matcher, locator)
4424
+ if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
4452
4425
 
4453
- if (isReactLocator) return findReact(matcher, matchedLocator)
4454
- if (isVueLocator) return findVue(matcher, matchedLocator)
4426
+ locator = new Locator(locator, 'css')
4455
4427
 
4456
- return matcher.locator(buildLocatorString(matchedLocator)).first()
4428
+ return matcher.locator(buildLocatorString(locator)).first()
4457
4429
  }
4458
4430
 
4459
4431
  async function getVisibleElements(elements) {
@@ -4505,14 +4477,26 @@ async function proceedClick(locator, context = null, options = {}) {
4505
4477
  }
4506
4478
 
4507
4479
  async function findClickable(matcher, locator) {
4508
- if (locator.react) return findReact(matcher, locator)
4509
- if (locator.vue) return findVue(matcher, locator)
4480
+ const matchedLocator = new Locator(locator)
4510
4481
 
4511
- locator = new Locator(locator)
4512
- if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
4482
+ if (!matchedLocator.isFuzzy()) return findElements.call(this, matcher, matchedLocator)
4513
4483
 
4514
4484
  let els
4515
- const literal = xpathLocator.literal(locator.value)
4485
+ const literal = xpathLocator.literal(matchedLocator.value)
4486
+
4487
+ try {
4488
+ els = await matcher.getByRole('button', { name: matchedLocator.value }).all()
4489
+ if (els.length) return els
4490
+ } catch (err) {
4491
+ // getByRole not supported or failed
4492
+ }
4493
+
4494
+ try {
4495
+ els = await matcher.getByRole('link', { name: matchedLocator.value }).all()
4496
+ if (els.length) return els
4497
+ } catch (err) {
4498
+ // getByRole not supported or failed
4499
+ }
4516
4500
 
4517
4501
  els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
4518
4502
  if (els.length) return els
@@ -4527,7 +4511,7 @@ async function findClickable(matcher, locator) {
4527
4511
  // Do nothing
4528
4512
  }
4529
4513
 
4530
- return findElements.call(this, matcher, locator.value) // by css or xpath
4514
+ return findElements.call(this, matcher, matchedLocator.value) // by css or xpath
4531
4515
  }
4532
4516
 
4533
4517
  async function proceedSee(assertType, text, context, strict = false) {
@@ -4567,17 +4551,14 @@ async function findCheckable(locator, context) {
4567
4551
  }
4568
4552
 
4569
4553
  // Handle role locators with text/exact options
4570
- const roleElements = await findByRole(contextEl, locator)
4554
+ const roleElements = await handleRoleLocator(contextEl, locator)
4571
4555
  if (roleElements) return roleElements
4572
4556
 
4573
- const matchedLocator = Locator.from(locator)
4557
+ const matchedLocator = new Locator(locator)
4574
4558
  if (!matchedLocator.isFuzzy()) {
4575
4559
  return findElements.call(this, contextEl, matchedLocator)
4576
4560
  }
4577
4561
 
4578
- const checkboxByRole = await findByRole(contextEl, { role: 'checkbox', name: matchedLocator.value })
4579
- if (checkboxByRole) return checkboxByRole
4580
-
4581
4562
  const literal = xpathLocator.literal(matchedLocator.value)
4582
4563
  let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
4583
4564
  if (els.length) {
@@ -4599,15 +4580,17 @@ async function proceedIsChecked(assertType, option) {
4599
4580
  }
4600
4581
 
4601
4582
  async function findFields(locator) {
4602
- const page = await this.page
4603
- const roleElements = await findByRole(page, locator)
4604
- if (roleElements) return roleElements
4583
+ // Handle role locators with text/exact options
4584
+ if (isRoleLocatorObject(locator)) {
4585
+ const page = await this.page
4586
+ const roleElements = await handleRoleLocator(page, locator)
4587
+ if (roleElements) return roleElements
4588
+ }
4605
4589
 
4606
4590
  const matchedLocator = new Locator(locator)
4607
4591
  if (!matchedLocator.isFuzzy()) {
4608
4592
  return this._locate(matchedLocator)
4609
4593
  }
4610
-
4611
4594
  const literal = xpathLocator.literal(locator)
4612
4595
 
4613
4596
  let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
@@ -4626,45 +4609,6 @@ async function findFields(locator) {
4626
4609
  return this._locate({ css: locator })
4627
4610
  }
4628
4611
 
4629
- async function proceedSelect(context, el, option) {
4630
- const role = await el.getAttribute('role')
4631
- const options = Array.isArray(option) ? option : [option]
4632
-
4633
- if (role === 'combobox') {
4634
- this.debugSection('SelectOption', 'Expanding combobox')
4635
- await highlightActiveElement.call(this, el)
4636
- const [ariaOwns, ariaControls] = await Promise.all([el.getAttribute('aria-owns'), el.getAttribute('aria-controls')])
4637
- await el.click()
4638
- await this._waitForAction()
4639
-
4640
- const listboxId = ariaOwns || ariaControls
4641
- let listbox = listboxId ? context.locator(`#${listboxId}`).first() : null
4642
- if (!listbox || !(await listbox.count())) listbox = context.getByRole('listbox').first()
4643
-
4644
- for (const opt of options) {
4645
- const optEl = listbox.getByRole('option', { name: opt }).first()
4646
- this.debugSection('SelectOption', `Clicking: "${opt}"`)
4647
- await highlightActiveElement.call(this, optEl)
4648
- await optEl.click()
4649
- }
4650
- return this._waitForAction()
4651
- }
4652
-
4653
- if (role === 'listbox') {
4654
- for (const opt of options) {
4655
- const optEl = el.getByRole('option', { name: opt }).first()
4656
- this.debugSection('SelectOption', `Clicking: "${opt}"`)
4657
- await highlightActiveElement.call(this, optEl)
4658
- await optEl.click()
4659
- }
4660
- return this._waitForAction()
4661
- }
4662
-
4663
- await highlightActiveElement.call(this, el)
4664
- await el.selectOption(option)
4665
- return this._waitForAction()
4666
- }
4667
-
4668
4612
  async function proceedSeeInField(assertType, field, value) {
4669
4613
  const els = await findFields.call(this, field)
4670
4614
  assertElementExists(els, field, 'Field')