Skip to content

Commit 404e1e4

Browse files
committed
Fix waitForText timeout regression in Playwright helper
1 parent a462f17 commit 404e1e4

File tree

2 files changed

+38
-36
lines changed

2 files changed

+38
-36
lines changed

lib/helper/Playwright.js

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2795,42 +2795,20 @@ class Playwright extends Helper {
27952795
}
27962796
}
27972797

2798-
const timeoutGap = waitTimeout + 1000
2799-
2800-
// We add basic timeout to make sure we don't wait forever
2801-
// We apply 2 strategies here: wait for text as innert text on page (wide strategy) - older
2802-
// or we use native Playwright matcher to wait for text in element (narrow strategy) - newer
2803-
// 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
2804-
2805-
// Use a flag to stop retries when race resolves
2806-
let shouldStop = false
2807-
let timeoutId
2808-
2809-
const racePromise = Promise.race([
2810-
new Promise((_, reject) => {
2811-
timeoutId = setTimeout(() => reject(errorMessage), waitTimeout)
2812-
}),
2813-
this.page.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: timeoutGap }),
2814-
promiseRetry(
2815-
async (retry, number) => {
2816-
// Stop retrying if race has resolved
2817-
if (shouldStop) {
2818-
throw new Error('Operation cancelled')
2819-
}
2820-
const textPresent = await contextObject
2821-
.locator(`:has-text(${JSON.stringify(text)})`)
2822-
.first()
2823-
.isVisible()
2824-
if (!textPresent) retry(errorMessage)
2825-
},
2826-
{ retries: 10, minTimeout: 100, maxTimeout: 500, factor: 1.5 },
2827-
),
2828-
])
2829-
2830-
// Clean up when race resolves/rejects
2831-
return racePromise.finally(() => {
2832-
if (timeoutId) clearTimeout(timeoutId)
2833-
shouldStop = true
2798+
// Use a simple Promise.race with two complementary strategies:
2799+
// 1. page.waitForFunction - checks document.body.innerText (broad search)
2800+
// 2. contextObject.locator with :has-text - uses Playwright's native text matching
2801+
// Both approaches will wait for the full timeout duration
2802+
return Promise.race([
2803+
// Strategy 1: Check document body innerText
2804+
this.page.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: waitTimeout }),
2805+
// Strategy 2: Use Playwright's native text locator
2806+
contextObject
2807+
.locator(`:has-text(${JSON.stringify(text)})`)
2808+
.first()
2809+
.waitFor({ timeout: waitTimeout, state: 'visible' }),
2810+
]).catch(err => {
2811+
throw new Error(errorMessage)
28342812
})
28352813
}
28362814

test/helper/Playwright_test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,30 @@ describe('Playwright', function () {
778778
.then(() => I.seeInField('#text2', 'London')))
779779
})
780780

781+
describe('#waitForText timeout fix', () => {
782+
it('should wait for the full timeout duration when text is not found', async function () {
783+
this.timeout(10000) // Allow up to 10 seconds for this test
784+
785+
const startTime = Date.now()
786+
const timeoutSeconds = 3 // 3 second timeout
787+
788+
try {
789+
await I.amOnPage('/')
790+
await I.waitForText('ThisTextDoesNotExistAnywhere12345', timeoutSeconds)
791+
// Should not reach here
792+
throw new Error('waitForText should have thrown an error')
793+
} catch (error) {
794+
const elapsedTime = Date.now() - startTime
795+
const expectedTimeout = timeoutSeconds * 1000
796+
797+
// Verify it waited close to the full timeout (allow 500ms tolerance)
798+
assert.ok(elapsedTime >= expectedTimeout - 500, `Expected to wait at least ${expectedTimeout - 500}ms, but waited ${elapsedTime}ms`)
799+
assert.ok(elapsedTime <= expectedTimeout + 1000, `Expected to wait at most ${expectedTimeout + 1000}ms, but waited ${elapsedTime}ms`)
800+
assert.ok(error.message.includes('was not found on page after'), `Expected error message about text not found, got: ${error.message}`)
801+
}
802+
})
803+
})
804+
781805
describe('#grabHTMLFrom', () => {
782806
it('should grab inner html from an element using xpath query', () =>
783807
I.amOnPage('/')

0 commit comments

Comments
 (0)