codeceptjs 3.7.5-beta.1 → 3.7.5-beta.10

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.
@@ -38,6 +38,8 @@ const WebElement = require('../element/WebElement')
38
38
  let playwright
39
39
  let perfTiming
40
40
  let defaultSelectorEnginesInitialized = false
41
+ let registeredCustomLocatorStrategies = new Set()
42
+ let globalCustomLocatorStrategies = new Map()
41
43
 
42
44
  const popupStore = new Popup()
43
45
  const consoleLogStore = new Console()
@@ -96,6 +98,7 @@ const pathSeparator = path.sep
96
98
  * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
97
99
  * @prop {object} [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
98
100
  * @prop {string} [testIdAttribute=data-testid] - locate elements based on the testIdAttribute. See more of [locate by test id](https://playwright.dev/docs/locators#locate-by-test-id).
101
+ * @prop {object} [customLocatorStrategies] - custom locator strategies. An object with keys as strategy names and values as JavaScript functions. Example: `{ byRole: (selector, root) => { return root.querySelector(\`[role="\${selector}\"]\`) } }`
99
102
  */
100
103
  const config = {}
101
104
 
@@ -344,9 +347,23 @@ class Playwright extends Helper {
344
347
  this.recordingWebSocketMessages = false
345
348
  this.recordedWebSocketMessagesAtLeastOnce = false
346
349
  this.cdpSession = null
350
+ this.customLocatorStrategies = typeof config.customLocatorStrategies === 'object' && config.customLocatorStrategies !== null ? config.customLocatorStrategies : null
351
+ this._customLocatorsRegistered = false
352
+
353
+ // Add custom locator strategies to global registry for early registration
354
+ if (this.customLocatorStrategies) {
355
+ for (const [strategyName, strategyFunction] of Object.entries(this.customLocatorStrategies)) {
356
+ globalCustomLocatorStrategies.set(strategyName, strategyFunction)
357
+ }
358
+ }
347
359
 
348
360
  // override defaults with config
349
361
  this._setConfig(config)
362
+
363
+ // Call _init() to register selector engines - use setTimeout to avoid blocking constructor
364
+ setTimeout(() => {
365
+ this._init().catch(console.error)
366
+ }, 0)
350
367
  }
351
368
 
352
369
  _validateConfig(config) {
@@ -463,12 +480,61 @@ class Playwright extends Helper {
463
480
 
464
481
  async _init() {
465
482
  // register an internal selector engine for reading value property of elements in a selector
466
- if (defaultSelectorEnginesInitialized) return
467
- defaultSelectorEnginesInitialized = true
468
483
  try {
469
- await playwright.selectors.register('__value', createValueEngine)
470
- await playwright.selectors.register('__disabled', createDisabledEngine)
471
- if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute)
484
+ if (!defaultSelectorEnginesInitialized) {
485
+ await playwright.selectors.register('__value', createValueEngine)
486
+ await playwright.selectors.register('__disabled', createDisabledEngine)
487
+ if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute)
488
+ defaultSelectorEnginesInitialized = true
489
+ }
490
+
491
+ // Register all custom locator strategies from the global registry
492
+ for (const [strategyName, strategyFunction] of globalCustomLocatorStrategies.entries()) {
493
+ if (!registeredCustomLocatorStrategies.has(strategyName)) {
494
+ try {
495
+ // Create a selector engine factory function exactly like createValueEngine pattern
496
+ // Capture variables in closure to avoid reference issues
497
+ const createCustomEngine = ((name, func) => {
498
+ return () => {
499
+ return {
500
+ create() {
501
+ return null
502
+ },
503
+ query(root, selector) {
504
+ try {
505
+ if (!root) return null
506
+ const result = func(selector, root)
507
+ return Array.isArray(result) ? result[0] : result
508
+ } catch (error) {
509
+ console.warn(`Error in custom locator "${name}":`, error)
510
+ return null
511
+ }
512
+ },
513
+ queryAll(root, selector) {
514
+ try {
515
+ if (!root) return []
516
+ const result = func(selector, root)
517
+ return Array.isArray(result) ? result : result ? [result] : []
518
+ } catch (error) {
519
+ console.warn(`Error in custom locator "${name}":`, error)
520
+ return []
521
+ }
522
+ },
523
+ }
524
+ }
525
+ })(strategyName, strategyFunction)
526
+
527
+ await playwright.selectors.register(strategyName, createCustomEngine)
528
+ registeredCustomLocatorStrategies.add(strategyName)
529
+ } catch (error) {
530
+ if (!error.message.includes('already registered')) {
531
+ console.warn(`Failed to register custom locator strategy '${strategyName}':`, error)
532
+ } else {
533
+ console.log(`Custom locator strategy '${strategyName}' already registered`)
534
+ }
535
+ }
536
+ }
537
+ }
472
538
  } catch (e) {
473
539
  console.warn(e)
474
540
  }
@@ -827,6 +893,9 @@ class Playwright extends Helper {
827
893
  }
828
894
 
829
895
  async _startBrowser() {
896
+ // Ensure custom locator strategies are registered before browser launch
897
+ await this._init()
898
+
830
899
  if (this.isElectron) {
831
900
  this.browser = await playwright._electron.launch(this.playwrightOptions)
832
901
  } else if (this.isRemoteBrowser && this.isCDPConnection) {
@@ -862,6 +931,30 @@ class Playwright extends Helper {
862
931
  return this.browser
863
932
  }
864
933
 
934
+ _lookupCustomLocator(customStrategy) {
935
+ if (typeof this.customLocatorStrategies !== 'object' || this.customLocatorStrategies === null) {
936
+ return null
937
+ }
938
+ const strategy = this.customLocatorStrategies[customStrategy]
939
+ return typeof strategy === 'function' ? strategy : null
940
+ }
941
+
942
+ _isCustomLocator(locator) {
943
+ const locatorObj = new Locator(locator)
944
+ if (locatorObj.isCustom()) {
945
+ const customLocator = this._lookupCustomLocator(locatorObj.type)
946
+ if (customLocator) {
947
+ return true
948
+ }
949
+ throw new Error('Please define "customLocatorStrategies" as an Object and the Locator Strategy as a "function".')
950
+ }
951
+ return false
952
+ }
953
+
954
+ _isCustomLocatorStrategyDefined() {
955
+ return !!(this.customLocatorStrategies && Object.keys(this.customLocatorStrategies).length > 0)
956
+ }
957
+
865
958
  /**
866
959
  * Create a new browser context with a page. \
867
960
  * Usually it should be run from a custom helper after call of `_startBrowser()`
@@ -869,11 +962,64 @@ class Playwright extends Helper {
869
962
  */
870
963
  async _createContextPage(contextOptions) {
871
964
  this.browserContext = await this.browser.newContext(contextOptions)
965
+
966
+ // Register custom locator strategies for this context
967
+ await this._registerCustomLocatorStrategies()
968
+
872
969
  const page = await this.browserContext.newPage()
873
970
  targetCreatedHandler.call(this, page)
874
971
  await this._setPage(page)
875
972
  }
876
973
 
974
+ async _registerCustomLocatorStrategies() {
975
+ if (!this.customLocatorStrategies) return
976
+
977
+ for (const [strategyName, strategyFunction] of Object.entries(this.customLocatorStrategies)) {
978
+ if (!registeredCustomLocatorStrategies.has(strategyName)) {
979
+ try {
980
+ const createCustomEngine = ((name, func) => {
981
+ return () => {
982
+ return {
983
+ create(root, target) {
984
+ return null
985
+ },
986
+ query(root, selector) {
987
+ try {
988
+ if (!root) return null
989
+ const result = func(selector, root)
990
+ return Array.isArray(result) ? result[0] : result
991
+ } catch (error) {
992
+ console.warn(`Error in custom locator "${name}":`, error)
993
+ return null
994
+ }
995
+ },
996
+ queryAll(root, selector) {
997
+ try {
998
+ if (!root) return []
999
+ const result = func(selector, root)
1000
+ return Array.isArray(result) ? result : result ? [result] : []
1001
+ } catch (error) {
1002
+ console.warn(`Error in custom locator "${name}":`, error)
1003
+ return []
1004
+ }
1005
+ },
1006
+ }
1007
+ }
1008
+ })(strategyName, strategyFunction)
1009
+
1010
+ await playwright.selectors.register(strategyName, createCustomEngine)
1011
+ registeredCustomLocatorStrategies.add(strategyName)
1012
+ } catch (error) {
1013
+ if (!error.message.includes('already registered')) {
1014
+ console.warn(`Failed to register custom locator strategy '${strategyName}':`, error)
1015
+ } else {
1016
+ console.log(`Custom locator strategy '${strategyName}' already registered`)
1017
+ }
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+
877
1023
  _getType() {
878
1024
  return this.browser._type
879
1025
  }
@@ -885,7 +1031,10 @@ class Playwright extends Helper {
885
1031
  this.frame = null
886
1032
  popupStore.clear()
887
1033
  if (this.options.recordHar) await this.browserContext.close()
1034
+ this.browserContext = null
888
1035
  await this.browser.close()
1036
+ this.browser = null
1037
+ this.isRunning = false
889
1038
  }
890
1039
 
891
1040
  async _evaluateHandeInContext(...args) {
@@ -1266,9 +1415,9 @@ class Playwright extends Helper {
1266
1415
  async _locate(locator) {
1267
1416
  const context = await this._getContext()
1268
1417
 
1269
- if (this.frame) return findElements(this.frame, locator)
1418
+ if (this.frame) return findElements.call(this, this.frame, locator)
1270
1419
 
1271
- const els = await findElements(context, locator)
1420
+ const els = await findElements.call(this, context, locator)
1272
1421
 
1273
1422
  if (store.debugMode) {
1274
1423
  const previewElements = els.slice(0, 3)
@@ -2063,11 +2212,25 @@ class Playwright extends Helper {
2063
2212
  * @param {*} locator
2064
2213
  */
2065
2214
  _contextLocator(locator) {
2066
- locator = buildLocatorString(new Locator(locator, 'css'))
2215
+ const locatorObj = new Locator(locator, 'css')
2216
+
2217
+ // Handle custom locators differently
2218
+ if (locatorObj.isCustom()) {
2219
+ return buildCustomLocatorString(locatorObj)
2220
+ }
2221
+
2222
+ locator = buildLocatorString(locatorObj)
2067
2223
 
2068
2224
  if (this.contextLocator) {
2069
- const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
2070
- locator = `${contextLocator} >> ${locator}`
2225
+ const contextLocatorObj = new Locator(this.contextLocator, 'css')
2226
+ if (contextLocatorObj.isCustom()) {
2227
+ // For custom context locators, we can't use the >> syntax
2228
+ // Instead, we'll need to handle this differently in the calling methods
2229
+ return locator
2230
+ } else {
2231
+ const contextLocator = buildLocatorString(contextLocatorObj)
2232
+ locator = `${contextLocator} >> ${locator}`
2233
+ }
2071
2234
  }
2072
2235
 
2073
2236
  return locator
@@ -2078,11 +2241,25 @@ class Playwright extends Helper {
2078
2241
  *
2079
2242
  */
2080
2243
  async grabTextFrom(locator) {
2081
- locator = this._contextLocator(locator)
2082
- const text = await this.page.textContent(locator)
2083
- assertElementExists(text, locator)
2084
- this.debugSection('Text', text)
2085
- return text
2244
+ const locatorObj = new Locator(locator, 'css')
2245
+
2246
+ if (locatorObj.isCustom()) {
2247
+ // For custom locators, find the element first
2248
+ const elements = await findCustomElements.call(this, this.page, locatorObj)
2249
+ if (elements.length === 0) {
2250
+ throw new Error(`Element not found: ${locatorObj.toString()}`)
2251
+ }
2252
+ const text = await elements[0].textContent()
2253
+ assertElementExists(text, locatorObj.toString())
2254
+ this.debugSection('Text', text)
2255
+ return text
2256
+ } else {
2257
+ locator = this._contextLocator(locator)
2258
+ const text = await this.page.textContent(locator)
2259
+ assertElementExists(text, locator)
2260
+ this.debugSection('Text', text)
2261
+ return text
2262
+ }
2086
2263
  }
2087
2264
 
2088
2265
  /**
@@ -2095,7 +2272,6 @@ class Playwright extends Helper {
2095
2272
  for (const el of els) {
2096
2273
  texts.push(await el.innerText())
2097
2274
  }
2098
- this.debug(`Matched ${els.length} elements`)
2099
2275
  return texts
2100
2276
  }
2101
2277
 
@@ -2114,7 +2290,6 @@ class Playwright extends Helper {
2114
2290
  */
2115
2291
  async grabValueFromAll(locator) {
2116
2292
  const els = await findFields.call(this, locator)
2117
- this.debug(`Matched ${els.length} elements`)
2118
2293
  return Promise.all(els.map(el => el.inputValue()))
2119
2294
  }
2120
2295
 
@@ -2133,7 +2308,6 @@ class Playwright extends Helper {
2133
2308
  */
2134
2309
  async grabHTMLFromAll(locator) {
2135
2310
  const els = await this._locate(locator)
2136
- this.debug(`Matched ${els.length} elements`)
2137
2311
  return Promise.all(els.map(el => el.innerHTML()))
2138
2312
  }
2139
2313
 
@@ -2154,7 +2328,6 @@ class Playwright extends Helper {
2154
2328
  */
2155
2329
  async grabCssPropertyFromAll(locator, cssProperty) {
2156
2330
  const els = await this._locate(locator)
2157
- this.debug(`Matched ${els.length} elements`)
2158
2331
  const cssValues = await Promise.all(els.map(el => el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)))
2159
2332
 
2160
2333
  return cssValues
@@ -2265,7 +2438,6 @@ class Playwright extends Helper {
2265
2438
  */
2266
2439
  async grabAttributeFromAll(locator, attr) {
2267
2440
  const els = await this._locate(locator)
2268
- this.debug(`Matched ${els.length} elements`)
2269
2441
  const array = []
2270
2442
 
2271
2443
  for (let index = 0; index < els.length; index++) {
@@ -2285,7 +2457,6 @@ class Playwright extends Helper {
2285
2457
  const res = await this._locateElement(locator)
2286
2458
  assertElementExists(res, locator)
2287
2459
  const elem = res
2288
- this.debug(`Screenshot of ${new Locator(locator)} element has been saved to ${outputFile}`)
2289
2460
  return elem.screenshot({ path: outputFile, type: 'png' })
2290
2461
  }
2291
2462
 
@@ -2580,7 +2751,16 @@ class Playwright extends Helper {
2580
2751
 
2581
2752
  const context = await this._getContext()
2582
2753
  try {
2583
- await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
2754
+ if (locator.isCustom()) {
2755
+ // For custom locators, we need to use our custom element finding logic
2756
+ const elements = await findCustomElements.call(this, context, locator)
2757
+ if (elements.length === 0) {
2758
+ throw new Error(`Custom locator ${locator.type}=${locator.value} not found`)
2759
+ }
2760
+ await elements[0].waitFor({ timeout: waitTimeout, state: 'attached' })
2761
+ } else {
2762
+ await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
2763
+ }
2584
2764
  } catch (e) {
2585
2765
  throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`)
2586
2766
  }
@@ -2594,9 +2774,30 @@ class Playwright extends Helper {
2594
2774
  async waitForVisible(locator, sec) {
2595
2775
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
2596
2776
  locator = new Locator(locator, 'css')
2777
+
2597
2778
  const context = await this._getContext()
2598
2779
  let count = 0
2599
2780
 
2781
+ // Handle custom locators
2782
+ if (locator.isCustom()) {
2783
+ let waiter
2784
+ do {
2785
+ const elements = await findCustomElements.call(this, context, locator)
2786
+ if (elements.length > 0) {
2787
+ waiter = await elements[0].isVisible()
2788
+ } else {
2789
+ waiter = false
2790
+ }
2791
+ if (!waiter) {
2792
+ await this.wait(1)
2793
+ count += 1000
2794
+ }
2795
+ } while (!waiter && count <= waitTimeout)
2796
+
2797
+ if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
2798
+ return
2799
+ }
2800
+
2600
2801
  // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
2601
2802
  let waiter
2602
2803
  if (this.frame) {
@@ -2623,6 +2824,7 @@ class Playwright extends Helper {
2623
2824
  async waitForInvisible(locator, sec) {
2624
2825
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
2625
2826
  locator = new Locator(locator, 'css')
2827
+
2626
2828
  const context = await this._getContext()
2627
2829
  let waiter
2628
2830
  let count = 0
@@ -2653,6 +2855,7 @@ class Playwright extends Helper {
2653
2855
  async waitToHide(locator, sec) {
2654
2856
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
2655
2857
  locator = new Locator(locator, 'css')
2858
+
2656
2859
  const context = await this._getContext()
2657
2860
  let waiter
2658
2861
  let count = 0
@@ -2774,9 +2977,18 @@ class Playwright extends Helper {
2774
2977
  if (context) {
2775
2978
  const locator = new Locator(context, 'css')
2776
2979
  try {
2980
+ if (locator.isCustom()) {
2981
+ // For custom locators, find the elements first then check for text within them
2982
+ const elements = await findCustomElements.call(this, contextObject, locator)
2983
+ if (elements.length === 0) {
2984
+ throw new Error(`Context element not found: ${locator.toString()}`)
2985
+ }
2986
+ return elements[0].locator(`text=${text}`).first().waitFor({ timeout: waitTimeout, state: 'visible' })
2987
+ }
2988
+
2777
2989
  if (!locator.isXPath()) {
2778
2990
  return contextObject
2779
- .locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`)
2991
+ .locator(`${locator.simplify()} >> text=${text}`)
2780
2992
  .first()
2781
2993
  .waitFor({ timeout: waitTimeout, state: 'visible' })
2782
2994
  .catch(e => {
@@ -3421,9 +3633,15 @@ class Playwright extends Helper {
3421
3633
 
3422
3634
  module.exports = Playwright
3423
3635
 
3636
+ function buildCustomLocatorString(locator) {
3637
+ // Note: this.debug not available in standalone function, using console.log
3638
+ console.log(`Building custom locator string: ${locator.type}=${locator.value}`)
3639
+ return `${locator.type}=${locator.value}`
3640
+ }
3641
+
3424
3642
  function buildLocatorString(locator) {
3425
3643
  if (locator.isCustom()) {
3426
- return `${locator.type}=${locator.value}`
3644
+ return buildCustomLocatorString(locator)
3427
3645
  }
3428
3646
  if (locator.isXPath()) {
3429
3647
  return `xpath=${locator.value}`
@@ -3435,15 +3653,119 @@ async function findElements(matcher, locator) {
3435
3653
  if (locator.react) return findReact(matcher, locator)
3436
3654
  if (locator.vue) return findVue(matcher, locator)
3437
3655
  if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
3656
+
3438
3657
  locator = new Locator(locator, 'css')
3439
3658
 
3440
- return matcher.locator(buildLocatorString(locator)).all()
3659
+ // Handle custom locators directly instead of relying on Playwright selector engines
3660
+ if (locator.isCustom()) {
3661
+ return findCustomElements.call(this, matcher, locator)
3662
+ }
3663
+
3664
+ // Check if we have a custom context locator and need to search within it
3665
+ if (this.contextLocator) {
3666
+ const contextLocatorObj = new Locator(this.contextLocator, 'css')
3667
+ if (contextLocatorObj.isCustom()) {
3668
+ // Find the context elements first
3669
+ const contextElements = await findCustomElements.call(this, matcher, contextLocatorObj)
3670
+ if (contextElements.length === 0) {
3671
+ return []
3672
+ }
3673
+
3674
+ // Search within the first context element
3675
+ const locatorString = buildLocatorString(locator)
3676
+ return contextElements[0].locator(locatorString).all()
3677
+ }
3678
+ }
3679
+
3680
+ const locatorString = buildLocatorString(locator)
3681
+
3682
+ return matcher.locator(locatorString).all()
3683
+ }
3684
+
3685
+ async function findCustomElements(matcher, locator) {
3686
+ const customLocatorStrategies = this.customLocatorStrategies || globalCustomLocatorStrategies
3687
+ const strategyFunction = customLocatorStrategies.get ? customLocatorStrategies.get(locator.type) : customLocatorStrategies[locator.type]
3688
+
3689
+ if (!strategyFunction) {
3690
+ throw new Error(`Custom locator strategy "${locator.type}" is not defined. Please define "customLocatorStrategies" in your configuration.`)
3691
+ }
3692
+
3693
+ // Execute the custom locator function in the browser context using page.evaluate
3694
+ const page = matcher.constructor.name === 'Page' ? matcher : await matcher.page()
3695
+
3696
+ const elements = await page.evaluate(
3697
+ ({ strategyCode, selector }) => {
3698
+ const strategy = new Function('return ' + strategyCode)()
3699
+ const result = strategy(selector, document)
3700
+
3701
+ // Convert NodeList or single element to array
3702
+ if (result && result.nodeType) {
3703
+ return [result]
3704
+ } else if (result && result.length !== undefined) {
3705
+ return Array.from(result)
3706
+ } else if (Array.isArray(result)) {
3707
+ return result
3708
+ }
3709
+
3710
+ return []
3711
+ },
3712
+ {
3713
+ strategyCode: strategyFunction.toString(),
3714
+ selector: locator.value,
3715
+ },
3716
+ )
3717
+
3718
+ // Convert the found elements back to Playwright locators
3719
+ if (elements.length === 0) {
3720
+ return []
3721
+ }
3722
+
3723
+ // Create CSS selectors for the found elements and return as locators
3724
+ const locators = []
3725
+ const timestamp = Date.now()
3726
+
3727
+ for (let i = 0; i < elements.length; i++) {
3728
+ // Use a unique attribute approach to target specific elements
3729
+ const uniqueAttr = `data-codecept-custom-${timestamp}-${i}`
3730
+
3731
+ await page.evaluate(
3732
+ ({ index, uniqueAttr, strategyCode, selector }) => {
3733
+ // Re-execute the strategy to find elements and mark the specific one
3734
+ const strategy = new Function('return ' + strategyCode)()
3735
+ const result = strategy(selector, document)
3736
+
3737
+ let elementsArray = []
3738
+ if (result && result.nodeType) {
3739
+ elementsArray = [result]
3740
+ } else if (result && result.length !== undefined) {
3741
+ elementsArray = Array.from(result)
3742
+ } else if (Array.isArray(result)) {
3743
+ elementsArray = result
3744
+ }
3745
+
3746
+ if (elementsArray[index]) {
3747
+ elementsArray[index].setAttribute(uniqueAttr, 'true')
3748
+ }
3749
+ },
3750
+ {
3751
+ index: i,
3752
+ uniqueAttr,
3753
+ strategyCode: strategyFunction.toString(),
3754
+ selector: locator.value,
3755
+ },
3756
+ )
3757
+
3758
+ locators.push(page.locator(`[${uniqueAttr}="true"]`))
3759
+ }
3760
+
3761
+ return locators
3441
3762
  }
3442
3763
 
3443
3764
  async function findElement(matcher, locator) {
3444
3765
  if (locator.react) return findReact(matcher, locator)
3445
3766
  if (locator.vue) return findVue(matcher, locator)
3446
3767
  if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
3768
+
3447
3769
  locator = new Locator(locator, 'css')
3448
3770
 
3449
3771
  return matcher.locator(buildLocatorString(locator)).first()
@@ -3764,9 +4086,7 @@ async function targetCreatedHandler(page) {
3764
4086
  if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
3765
4087
  try {
3766
4088
  await page.setViewportSize(parseWindowSize(this.options.windowSize))
3767
- } catch (err) {
3768
- this.debug('Target can be already closed, ignoring...')
3769
- }
4089
+ } catch (err) {}
3770
4090
  }
3771
4091
  }
3772
4092
 
package/lib/mocha/test.js CHANGED
@@ -121,6 +121,7 @@ function serializeTest(test, error = null) {
121
121
  }
122
122
 
123
123
  return {
124
+ file: test.file ? relativeDir(test.file) : undefined,
124
125
  opts: test.opts || {},
125
126
  tags: test.tags || [],
126
127
  uid: test.uid,