diff --git a/packages/playwright-core/src/server/codegen/java.ts b/packages/playwright-core/src/server/codegen/java.ts index 507a040bcec66..5b417c6c3a82d 100644 --- a/packages/playwright-core/src/server/codegen/java.ts +++ b/packages/playwright-core/src/server/codegen/java.ts @@ -122,7 +122,7 @@ export class JavaLanguageGenerator implements LanguageGenerator { case 'navigate': return `${subject}.navigate(${quote(action.url)});`; case 'select': - return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length > 1 ? action.options : action.options[0])});`; + return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`; case 'assertText': return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? 'containsText' : 'hasText'}(${quote(action.text)});`; case 'assertChecked': diff --git a/packages/playwright-core/src/server/codegen/javascript.ts b/packages/playwright-core/src/server/codegen/javascript.ts index 17f627b601e22..9105b8fc68551 100644 --- a/packages/playwright-core/src/server/codegen/javascript.ts +++ b/packages/playwright-core/src/server/codegen/javascript.ts @@ -106,7 +106,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator { case 'navigate': return `await ${subject}.goto(${quote(action.url)});`; case 'select': - return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length > 1 ? action.options : action.options[0])});`; + return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`; case 'assertText': return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${quote(action.text)});`; case 'assertChecked': diff --git a/tests/library/inspector/cli-codegen-1.spec.ts b/tests/library/inspector/cli-codegen-1.spec.ts index a876a25e47c2a..765ad241b676b 100644 --- a/tests/library/inspector/cli-codegen-1.spec.ts +++ b/tests/library/inspector/cli-codegen-1.spec.ts @@ -15,7 +15,7 @@ */ import { test, expect } from './inspectorTest'; -import type { ConsoleMessage } from 'playwright'; +import type { ConsoleMessage, Locator } from 'playwright'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); @@ -682,6 +682,93 @@ await page.Locator(\"#age\").SelectOptionAsync(new[] { \"2\" });`); expect(message.text()).toBe('2'); }); + const clickMultipleSelectOption = async (locator: Locator, withCtrlOrMeta = false) => { + const page = locator.page(); + + // Webkit can't click multiple select options + // https://github.com/microsoft/playwright/issues/32126 + if (page.context().browser().browserType().name() === 'webkit') { + const elem = await locator.elementHandle(); + const rect = await elem!.evaluate(e => { + return e.getBoundingClientRect()!; + }); + if (withCtrlOrMeta) + await page.keyboard.down('ControlOrMeta'); + + await page.mouse.click(rect.x + rect.width / 2, rect.y + rect.height / 2); + if (withCtrlOrMeta) + await page.keyboard.up('ControlOrMeta'); + + } else { + await locator.click({ modifiers: withCtrlOrMeta ? ['ControlOrMeta'] : [] }); + } + }; + + test('should select with multiple attribute', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); + + await recorder.setContentAndWait(``); + + const locator = await recorder.hoverOverElement('select'); + expect(locator).toBe(`locator('#age')`); + await clickMultipleSelectOption(page.getByRole('option', { name: '1' })); + + const [message, sources] = await Promise.all([ + page.waitForEvent('console', msg => msg.type() !== 'error' && msg.text().includes('2')), + recorder.waitForOutput('JavaScript', 'selectOption(['), + clickMultipleSelectOption(page.getByRole('option', { name: '2' }), true) + ]); + + expect(sources.get('JavaScript')!.text).toContain(` + await page.locator('#age').selectOption(['1', '2']);`); + + expect(sources.get('Java')!.text).toContain(` + page.locator("#age").selectOption(new String[] {"1", "2"});`); + + expect(sources.get('Python')!.text).toContain(` + page.locator("#age").select_option(["1", "2"])`); + + expect(sources.get('Python Async')!.text).toContain(` + await page.locator("#age").select_option(["1", "2"])`); + + expect(sources.get('C#')!.text).toContain(` +await page.Locator("#age").SelectOptionAsync(new[] { "1", "2" });`); + + expect(message.text()).toBe('[1,2]'); + }); + + test('should unselect with multiple attribute', async ({ openRecorder }) => { + const { page, recorder } = await openRecorder(); + + await recorder.setContentAndWait(``); + const locator = await recorder.hoverOverElement('select'); + expect(locator).toBe(`locator('#age')`); + await clickMultipleSelectOption(page.getByRole('option', { name: '1' })); + + const [message, sources] = await Promise.all([ + page.waitForEvent('console', msg => msg.type() !== 'error' && msg.text() === '[]'), + recorder.waitForOutput('JavaScript', 'selectOption(['), + clickMultipleSelectOption(page.getByRole('option', { name: '1' }), true) + ]); + + expect(sources.get('JavaScript')!.text).toContain(` + await page.locator('#age').selectOption([]);`); + + expect(sources.get('Java')!.text).toContain(` + page.locator("#age").selectOption(new String[0]);`); + + expect(sources.get('Python')!.text).toContain(` + page.locator("#age").select_option([])`); + + expect(sources.get('Python Async')!.text).toContain(` + await page.locator("#age").select_option([])`); + + expect(sources.get('C#')!.text).toContain(` +await page.Locator("#age").SelectOptionAsync(new[] { });`); + + expect(message.text()).toBe('[]'); + }); + test('should await popup', async ({ openRecorder }) => { const { page, recorder } = await openRecorder(); await recorder.setContentAndWait('link');