diff --git a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts index 704bfba3e8..f3b6a72483 100644 --- a/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts +++ b/packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts @@ -157,6 +157,23 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram continue; } } + if (part.name === 'internal:control' && (part.body as string) === 'enter-frame') { + const lastTokens = tokens[tokens.length - 1]; + const lastPart = parts[index - 1]; + + const extendedTokens = lastTokens.map(token => + factory.chainLocators([token, factory.generateLocator(base, 'frame', '')]) + ); + extendedTokens.push( + factory.generateLocator(base, 'frame-locator', stringifySelector({ parts: [lastPart] })) + ); + if (['xpath', 'css'].includes(lastPart.name)) + extendedTokens.push(factory.generateLocator(base, 'frame-locator', stringifySelector({ parts: [lastPart] }, true))); + + lastTokens.splice(0, lastTokens.length, ...extendedTokens) + nextBase = 'frame-locator'; + continue; + } const nextPart = parts[index + 1]; @@ -190,32 +207,6 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram locatorPartWithEngine = factory.generateLocator(base, 'default', selectorPart); } - if (nextPart && nextPart.name === 'internal:control' && (nextPart.body as string) === 'enter-frame') { - // two options plus engine name: - // - locator('iframe').contentFrame() - // - locator('css|xpath=iframe').contentFrame() - // - frameLocator('iframe') - // - frameLocator('css|xpath=iframe') - - const contentFrame = factory.generateLocator(base, 'frame', '') - const options = [ - factory.chainLocators([locatorPart, contentFrame]), - factory.generateLocator(base, 'frame-locator', selectorPart), - ] - - if (locatorPartWithEngine) { - options.push( - factory.chainLocators([locatorPartWithEngine, contentFrame]), - factory.generateLocator(base, 'frame-locator', stringifySelector({ parts: [part] }, /* forceEngineName */ true)), - ) - } - - tokens.push(options); - nextBase = 'frame-locator'; - index++; - continue; - } - tokens.push([locatorPart, locatorPartWithEngine].filter(Boolean) as string[]); } diff --git a/tests/library/locator-generator.spec.ts b/tests/library/locator-generator.spec.ts index 560861ae8b..4b9f9007c9 100644 --- a/tests/library/locator-generator.spec.ts +++ b/tests/library/locator-generator.spec.ts @@ -589,7 +589,7 @@ it('parseLocator frames', async () => { expect.soft(parseLocator('javascript', `locator('iframe').contentFrame().getByText('foo')`, '')).toBe(`iframe >> internal:control=enter-frame >> internal:text=\"foo\"i`); expect.soft(parseLocator('javascript', `frameLocator('iframe').getByText('foo')`, '')).toBe(`iframe >> internal:control=enter-frame >> internal:text=\"foo\"i`); expect.soft(parseLocator('javascript', `frameLocator('css=iframe').getByText('foo')`, '')).toBe(`css=iframe >> internal:control=enter-frame >> internal:text=\"foo\"i`); - expect.soft(parseLocator('javascript', `page.getByTitle('iframe title').contentFrame()`)).toBe(`internal:text=\"iframe title\"i >> internal:control=enter-frame`); + expect.soft(parseLocator('javascript', `getByTitle('iframe title').contentFrame()`)).toBe(`internal:attr=[title=\"iframe title\"i] >> internal:control=enter-frame`); expect.soft(parseLocator('python', `locator("iframe").content_frame.get_by_text("foo")`, '')).toBe(`iframe >> internal:control=enter-frame >> internal:text=\"foo\"i`); expect.soft(parseLocator('python', `frame_locator("iframe").get_by_text("foo")`, '')).toBe(`iframe >> internal:control=enter-frame >> internal:text=\"foo\"i`);