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');