From f259711c4ed4158281906cb04deca4ca8c4ad3b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Oct 2026 06:19:47 +0000 Subject: [PATCH 1/5] Initial plan From 7303590e9ec9da5c1005a2b331d324397925643b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Oct 2026 06:22:18 +0000 Subject: [PATCH 2/5] Apply remaining changes Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com> --- lib/helper/Playwright.js | 17 ++++++++++------- test/helper/webapi.js | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 6cfcf420a..1bc4c7737 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -3576,13 +3576,16 @@ class Playwright extends Helper { const locator = new Locator(context, 'css') try { if (!locator.isXPath()) { - return contextObject - .locator(`${locator.simplify()} >> text=${text}`) - .first() - .waitFor({ timeout: waitTimeout, state: 'visible' }) - .catch(e => { - throw new Error(errorMessage) - }) + const deadline = Date.now() + waitTimeout + do { + const els = await this._locate(locator) + for (const el of els) { + const texts = await el.evaluateAll(nodes => nodes.map(node => node.innerText ?? node.textContent)) + if (texts.some(t => typeof t === 'string' && t.includes(text))) return + } + await new Promise(resolve => setTimeout(resolve, 100)) + } while (Date.now() < deadline) + throw new Error(errorMessage) } if (locator.isXPath()) { diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 9f1b08ad9..ad3acb105 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -1678,6 +1678,11 @@ export function tests() { await I.see('Dynamic text') }) + it('should wait for text in context with locator.at', async () => { + await I.amOnPage('/dynamic') + await I.waitForText('Dynamic text', 2, new Locator('#text').at(1)) + }) + it('should fail if no context', async function () { let failed = false await I.amOnPage('/dynamic') From d3e588ca97ec6343fb07580ada2bcb73973d0999 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Oct 2026 06:22:18 +0000 Subject: [PATCH 3/5] Apply remaining changes - Wrap polling loop errors in friendly message to match old behavior - Note: text= is now case-sensitive (was case-insensitive/whitespace-normalized) Intentional to match XPath branch semantics Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com> --- lib/helper/Playwright.js | 17 ++++++++++------- test/helper/webapi.js | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 6cfcf420a..1bc4c7737 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -3576,13 +3576,16 @@ class Playwright extends Helper { const locator = new Locator(context, 'css') try { if (!locator.isXPath()) { - return contextObject - .locator(`${locator.simplify()} >> text=${text}`) - .first() - .waitFor({ timeout: waitTimeout, state: 'visible' }) - .catch(e => { - throw new Error(errorMessage) - }) + const deadline = Date.now() + waitTimeout + do { + const els = await this._locate(locator) + for (const el of els) { + const texts = await el.evaluateAll(nodes => nodes.map(node => node.innerText ?? node.textContent)) + if (texts.some(t => typeof t === 'string' && t.includes(text))) return + } + await new Promise(resolve => setTimeout(resolve, 100)) + } while (Date.now() < deadline) + throw new Error(errorMessage) } if (locator.isXPath()) { diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 9f1b08ad9..ad3acb105 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -1678,6 +1678,11 @@ export function tests() { await I.see('Dynamic text') }) + it('should wait for text in context with locator.at', async () => { + await I.amOnPage('/dynamic') + await I.waitForText('Dynamic text', 2, new Locator('#text').at(1)) + }) + it('should fail if no context', async function () { let failed = false await I.amOnPage('/dynamic') From c0082f882579ff5c72eaea8956f20429bfe34aea Mon Sep 17 00:00:00 2001 From: kobenguyent Date: Mon, 9 Nov 2026 17:08:21 +0100 Subject: [PATCH 4/5] address PR --- docs/helpers/Playwright.md | 2 +- lib/helper/Playwright.js | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/helpers/Playwright.md b/docs/helpers/Playwright.md index 7a1420acf..33c0e1f63 100644 --- a/docs/helpers/Playwright.md +++ b/docs/helpers/Playwright.md @@ -2729,7 +2729,7 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal'); * `sec` **[number][17]** (optional, `1` by default) time in seconds to wait * `context` **([string][7] | [object][8])?** (optional) element located by CSS|XPath|strict locator. -Returns **void** automatically synchronized promise through #recorder +Returns **void** automatically synchronized promise through #recorder. **Note:** Text matching is case-sensitive. For case-insensitive matching, use XPath or CSS locators with normalized text. ### waitForURL diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 1bc4c7737..f47c6c1c2 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -3565,6 +3565,8 @@ class Playwright extends Helper { /** * {{> waitForText }} + * + * . **Note:** Text matching is case-sensitive. For case-insensitive matching, use XPath or CSS locators with normalized text. */ async waitForText(text, sec = null, context = null) { const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout @@ -3576,15 +3578,19 @@ class Playwright extends Helper { const locator = new Locator(context, 'css') try { if (!locator.isXPath()) { - const deadline = Date.now() + waitTimeout - do { - const els = await this._locate(locator) - for (const el of els) { - const texts = await el.evaluateAll(nodes => nodes.map(node => node.innerText ?? node.textContent)) - if (texts.some(t => typeof t === 'string' && t.includes(text))) return - } - await new Promise(resolve => setTimeout(resolve, 100)) - } while (Date.now() < deadline) + try { + const deadline = Date.now() + waitTimeout + do { + const els = await this._locate(locator) + for (const el of els) { + const texts = await el.evaluateAll(nodes => nodes.map(node => node.innerText ?? node.textContent)) + if (texts.some(t => typeof t === 'string' && t.includes(text))) return + } + await new Promise(resolve => setTimeout(resolve, 100)) + } while (Date.now() < deadline) + } catch (e) { + throw new Error(errorMessage) + } throw new Error(errorMessage) } From d08860a34cacf1bffad668ab70fe12d6419a4ce2 Mon Sep 17 00:00:00 2001 From: kobenguyent Date: Mon, 9 Nov 2026 17:20:12 +0100 Subject: [PATCH 5/5] address PR --- lib/helper/Playwright.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index 228147cdb..a7dc7d12a 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -3578,7 +3578,6 @@ class Playwright extends Helper { const locator = new Locator(context, 'css') try { if (!locator.isXPath()) { -<<<<<<< HEAD try { const deadline = Date.now() + waitTimeout do { @@ -3589,21 +3588,10 @@ class Playwright extends Helper { } await new Promise(resolve => setTimeout(resolve, 100)) } while (Date.now() < deadline) + throw new Error(errorMessage) } catch (e) { throw new Error(errorMessage) } -======= - const deadline = Date.now() + waitTimeout - do { - const els = await this._locate(locator) - for (const el of els) { - const texts = await el.evaluateAll(nodes => nodes.map(node => node.innerText ?? node.textContent)) - if (texts.some(t => typeof t === 'string' && t.includes(text))) return - } - await new Promise(resolve => setTimeout(resolve, 100)) - } while (Date.now() < deadline) ->>>>>>> 7303590e9ec9da5c1005a2b331d324397925643b - throw new Error(errorMessage) } if (locator.isXPath()) {