From 8fda0b9c006c80745fe8d0f1c0885d40f2c389a1 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Tue, 7 Jan 2025 04:13:13 +0200 Subject: [PATCH] fixed regression in waitfortext (#4717) * fixed regression in waitfortext * fixed wait test for wait for text * fixed retries for PW waitForText test --------- Co-authored-by: DavertMik --- lib/helper/Playwright.js | 61 ++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 94ab68958..c40365c54 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -2688,6 +2688,9 @@ class Playwright extends Helper { if ((this.context && this.context.constructor.name === 'FrameLocator') || this.context) { return this.context } + if (this.frame) { + return this.frame + } return this.page } @@ -2752,26 +2755,21 @@ class Playwright extends Helper { async waitForText(text, sec = null, context = null) { const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec.` - let waiter const contextObject = await this._getContext() if (context) { const locator = new Locator(context, 'css') - if (!locator.isXPath()) { - try { - await contextObject + try { + if (!locator.isXPath()) { + return contextObject .locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`) .first() .waitFor({ timeout: waitTimeout, state: 'visible' }) - } catch (e) { - throw new Error(`${errorMessage}\n${e.message}`) } - } - if (locator.isXPath()) { - try { - await contextObject.waitForFunction( + if (locator.isXPath()) { + return contextObject.waitForFunction( ([locator, text, $XPath]) => { eval($XPath) // eslint-disable-line no-eval const el = $XPath(null, locator) @@ -2781,27 +2779,34 @@ class Playwright extends Helper { [locator.value, text, $XPath.toString()], { timeout: waitTimeout }, ) - } catch (e) { - throw new Error(`${errorMessage}\n${e.message}`) } + } catch (e) { + throw new Error(`${errorMessage}\n${e.message}`) } - } else { - // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented - - const _contextObject = this.frame ? this.frame : contextObject - let count = 0 - do { - waiter = await _contextObject - .locator(`:has-text(${JSON.stringify(text)})`) - .first() - .isVisible() - if (waiter) break - await this.wait(1) - count += 1000 - } while (count <= waitTimeout) - - if (!waiter) throw new Error(`${errorMessage}`) } + + const timeoutGap = waitTimeout + 1000 + + // We add basic timeout to make sure we don't wait forever + // We apply 2 strategies here: wait for text as innert text on page (wide strategy) - older + // or we use native Playwright matcher to wait for text in element (narrow strategy) - newer + // If a user waits for text on a page they are mostly expect it to be there, so wide strategy can be helpful even PW strategy is available + return Promise.race([ + new Promise((_, reject) => { + setTimeout(() => reject(errorMessage), waitTimeout) + }), + this.page.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: timeoutGap }), + promiseRetry( + async retry => { + const textPresent = await contextObject + .locator(`:has-text(${JSON.stringify(text)})`) + .first() + .isVisible() + if (!textPresent) retry(errorMessage) + }, + { retries: 1000, minTimeout: 500, maxTimeout: 500, factor: 1 }, + ), + ]) } /**