Summary
within() combined with I.see() (no explicit context argument) times out after upgrading to Playwright 1.60.
Root cause
proceedSee() in lib/helper/Playwright.js determines whether this.context is a scoped Locator or a Page/Frame using:
allText = el.constructor.name !== 'Locator'
? [await el.locator('body').innerText()]
: [await el.innerText()]
Playwright 1.60 renamed the internal class from Locator to _Locator. Because of this the condition always evaluates to true, causing CodeceptJS to attempt el.locator('body') inside whatever element was passed to within() — e.g. a <tr> or a <div>. The body locator never resolves inside those elements, so the step times out.
Error observed:
TimeoutError: locator.innerText: Timeout 30000ms exceeded.
Call log:
- waiting for locator('xpath=(...//tr)[position()=1]').first().locator('body')
Affected versions
- Playwright: 1.60 (introduced by the internal rename)
- CodeceptJS: 3.7.8 (latest at time of writing)
- Works correctly on Playwright 1.59
Fix
Replace the fragile constructor.name check with a stable public-API distinction: Page and Frame both expose url(), Locator intentionally does not.
// Before
allText = el.constructor.name !== 'Locator'
? [await el.locator('body').innerText()]
: [await el.innerText()]
// After
allText = typeof el.url === 'function'
? [await el.locator('body').innerText()]
: [await el.innerText()]
typeof el.url === 'function' produces the correct branching on all Playwright versions (Page/Frame have always exposed url(); Locator has never had it), so it is fully backward-compatible.
Reproduction
within(locate('.some-div'), () => {
I.see('Expected text'); // times out in Playwright 1.60
});
This issue was identified and reported by Claude (Anthropic's AI assistant), on behalf of the user.
Summary
within()combined withI.see()(no explicit context argument) times out after upgrading to Playwright 1.60.Root cause
proceedSee()inlib/helper/Playwright.jsdetermines whetherthis.contextis a scopedLocatoror aPage/Frameusing:Playwright 1.60 renamed the internal class from
Locatorto_Locator. Because of this the condition always evaluates totrue, causing CodeceptJS to attemptel.locator('body')inside whatever element was passed towithin()— e.g. a<tr>or a<div>. Thebodylocator never resolves inside those elements, so the step times out.Error observed:
Affected versions
Fix
Replace the fragile
constructor.namecheck with a stable public-API distinction:PageandFrameboth exposeurl(),Locatorintentionally does not.typeof el.url === 'function'produces the correct branching on all Playwright versions (Page/Frame have always exposedurl(); Locator has never had it), so it is fully backward-compatible.Reproduction
This issue was identified and reported by Claude (Anthropic's AI assistant), on behalf of the user.