microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
63.55k stars 3.43k forks source link

[Feature] Russian Keyboard Layout #7396

Open alex-popov-tech opened 2 years ago

alex-popov-tech commented 2 years ago

Note from maintainers

In most cases you should use locator.pressSequentially() method instead of locator.type(). This will work with any keyboard layout. You only need to type character by character if there is some special keyboard handling on the page.

const input = await page.locator('#kinput');
await input.fill('hello');
await input.fill('привiт');

Context:

## System:
 - OS: Linux 5.8 Ubuntu 20.04.2 LTS (Focal Fossa)
 - Memory: 15.31 GB / 31.30 GB
 - Container: Yes
## Binaries:
 - Node: 14.17.0 - /usr/bin/node
 - npm: 7.5.2 - /usr/local/bin/npm
## Languages:
 - Bash: 5.0.17 - /usr/bin/bash
## npmPackages:
 - playwright: 1.12.2 => 1.12.2 
## System:
 - OS: macOS 11.4
 - Memory: 670.91 MB / 32.00 GB
## Binaries:
 - Node: 15.3.0 - ~/.asdf/installs/nodejs/15.3.0/bin/node
 - Yarn: 1.22.10 - /usr/local/bin/yarn
 - npm: 7.0.14 - ~/.asdf/installs/nodejs/15.3.0/bin/npm
## Languages:
 - Bash: 3.2.57 - /bin/bash
## npmPackages:
 - playwright: 1.12.2 => 1.12.2 

Code Snippet

Help us help you! Put down a short code snippet that illustrates your bug and that we can run and debug locally. For example:

const {chromium, webkit, firefox} = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();
      await page.goto('https://learn.javascript.ru/article/keyboard-events/keyboard-dump/');
      const checkbox = await page.$('input[name="keyupStop"]');
      await checkbox.click();
      const input = await page.$('#kinput');
      await input.type(' working ', { delay: 500, noWaitAfter: false, timeout: 15000 });
      await input.type(' не работает ', { delay: 500, noWaitAfter: false, timeout: 15000 });
})();

Describe the bug

When I try to type russian characters keyup/down events are not triggered

JoelEinbinder commented 2 years ago

We currently hard code in a US keyboard layout. We can add in more layouts, but its a lot of work for each one so we've been waiting for a flood of user requests to prioritize it.

pavelfeldman commented 2 years ago

[GOOD FIRST ISSUE]

santapan345 commented 2 years ago

I want to work on this issue.

viraxslot commented 2 years ago

@JoelEinbinder hi, could you please suggest how to nicely implement this feature? Is it ok to:

I guess if somebody will want to add a new layout then more or less 2 lines should be changed. Of course besides the layout itself.

minamz commented 1 year ago

Might want to consider different keyboard layouts; like Standard(ЙЦУКЕН) and Phonetic(ЯШЕРТЫ)

buddhdev-harsh commented 1 year ago

We currently hard code in a US keyboard layout. We can add in more layouts, but its a lot of work for each one so we've been waiting for a flood of user requests to prioritize it.

can we add the same file but with a Russian keyboard Chars?

ruifigueira commented 11 months ago

I did a small PoC to support other keyboard layouts using a generator, to simplify the process. You can find it here:

https://github.com/microsoft/playwright/pull/24249

ruifigueira commented 11 months ago

I noticed that keyCodes found in usKeyboardLayout.ts don't match some keyCodes from my generator. For instance, Period currently has a keyCode of 190 for US, but my generator sets it to 46.

Looking at the code, it apparently uses Virtual-Key Codes in those cases.

I'll change my generator to use these values.

ruifigueira commented 11 months ago

For primary input layouts, the following list is available:

https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-input-locales-for-windows-language-packs?view=windows-11

So, if we decide to select the keyboard layout by locale, we can use this table (115 keyboard layouts):

KLID Layout Name Locales
00000409 US keyboard af-ZA, en-US, en-AU, en-BZ, en-CA, en-029, en-HK, en-JM, en-MY, en-PH, en-SG, en-ZA, en-TT, en-ZW, fil-PH, id-ID, jv, rw-RW, sw-KE, ms-MY, ms-BN, moh-CA, om-ET, pap-029, st-ZA, so-SO, uz-Latn-UZ, ts-ZA, xh-ZA, zu-ZA
0000041C Albanian keyboard sq-AL
0000040C French keyboard gsw-FR, br-FR, fr-FR, co-FR, en-GB, fr-CM, fr-CI, fr-HT, fr-ML, fr-MC, fr-MA, fr-RE, fr-SN, fr-CD, mg, oc-FR
00000401 Arabic (101) keyboard ar-SA, ar-BH, ar-EG, ar-IQ, ar-JO, ar-KW, ar-LB, ar-LY, ar-OM, ar-QA, ar-SY, ar-AE, ar-YE
00020401 Arabic (102) AZERTY keyboard ar-DZ, ar-MA, ar-TN
0002042B Armenian Phonetic keyboard hy-AM
0000044D Assamese - INSCRIPT keyboard as-IN
0000040A Spanish keyboard es-ES_tradnl, eu-ES, ca-ES, gl-ES, es-ES, ca-ES-valencia
0000042C Azerbaijani Latin keyboard az-Latn-AZ
0000082C Azerbaijani Cyrillic keyboard az-Cyrl-AZ
00000445 Bangla keyboard bn-BD
0000046D Bashkir keyboard ba-RU
00000423 Belarusian keyboard be-BY
00020445 Bangla - INSCRIPT keyboard bn-IN
00000439 Devanagari - INSCRIPT keyboard hi-IN, kok-IN, sa-IN
0000041A Standard keyboard bs-Latn-BA, hr-HR, hr-BA
0000201A Bosnian (Cyrillic) keyboard bs-Cyrl-BA
00030402 Bulgarian keyboard bg-BG
00130C00 Myanmar (Visual order) keyboard my-MM
00000410 Italian keyboard it-IT
0000085F Central Atlas Tamazight keyboard tzm-Latn-DZ
0000105F Tifinagh (Basic) keyboard tzm-Tfng-MA, zgh
00000492 Central Kurdish keyboard ku-Arab-IQ
00000419 Russian keyboard ru-RU
0000045C Cherokee Nation keyboard chr-Cher-US
00000407 German keyboard de-DE, en-GB, de-AT, de-LU
00000809 United Kingdom keyboard en-GB
00000405 Czech keyboard cs-CZ
00000406 Danish keyboard da-DK, en-US, fo-FO, kl-GL
00000465 Divehi Phonetic keyboard dv-MV
00020409 United States-International keyboard nl-NL, en-US, en-GB, fy-NL
00000813 Belgian (Period) keyboard nl-BE
00000C51 Dzongkha keyboard dz-BT
0000080C Belgian French keyboard fr-BE
0000040B Finnish keyboard en-GB, fi-FI
00004009 English (India) keyboard en-IN
00001809 Irish keyboard en-IE, ga-IE
00001409 NZ Aotearoa keyboard en-NZ
00000424 Slovenian keyboard en-GB, sl-SI
0000041D Swedish keyboard en-GB, sv-SE, sv-FI
00000425 Estonian keyboard et-EE
00001009 Canadian French keyboard fr-CA
0000100C Swiss French keyboard fr-LU, fr-CH, it-CH
00000488 Wolof keyboard ff-Latn-SN, wo-SN
00010437 Georgian (QWERTY) keyboard ka-GE
00000807 Swiss German keyboard de-LI, de-CH, rm-CH
00000408 Greek keyboard el-GR
00000474 Guarani keyboard gn-PY
00000447 Gujarati keyboard gu-IN
00000468 Hausa keyboard ha-Latn-NG
00000475 Hawaiian keyboard haw-US
0002040D Hebrew (Standard) keyboard he-IL
00010439 Hindi Traditional keyboard hi-IN
0000040E Hungarian keyboard hu-HU
0000040F Icelandic keyboard is-IS
00000470 Igbo keyboard ig-NG
0000085D Inuktitut - Latin keyboard iu-Latn-CA
0001045D Inuktitut - Naqittaut keyboard iu-Cans-CA
00110C00 Javanese keyboard jv-Java
0000044B Kannada keyboard kn-IN
00000420 Urdu keyboard ur-PK, pa-Arab-PK, sd-Arab-PK, ur-IN
0000043F Kazakh keyboard kk-KZ
00000453 Khmer keyboard km-KH
00000440 Kyrgyz Cyrillic keyboard ky-KG
0000080A Latin American keyboard quc-Latn-GT, arn-CL, quz-BO, quz-EC, quz-PE, es-AR, es-BO, es-CL, es-CO, es-CR, es-MX, es-DO, es-EC, es-SV, es-GT, es-HN, es-419, es-NI, es-PA, es-PY, es-PE, es-PR, es-US, es-UY, es-VE
00000454 Lao keyboard lo-LA
00020426 Latvian (Standard) keyboard lv-LV
00010427 Lithuanian keyboard lt-LT
0002042E Sorbian Standard keyboard dsb-DE, hsb-DE
0000046E Luxembourgish keyboard lb-LU
0001042F Macedonian - Standard keyboard mk-MK
0000044C Malayalam keyboard ml-IN
0000043A Maltese 47-Key keyboard mt-MT
00000481 Maori keyboard mi-NZ
0000044E Marathi keyboard mr-IN
00000429 Persian keyboard fa-IR
00000450 Mongolian Cyrillic keyboard mn-MN
00010850 Traditional Mongolian (Standard) keyboard mn-Mong-CN, mn-Mong-MN
00090C00 N’Ko keyboard nqo
00000461 Nepali keyboard ne-NP, ne-IN
0000043B Norwegian with Sami keyboard se-NO, smj-NO, sma-NO
00000414 Norwegian keyboard nb-NO, nn-NO
00000448 Odia keyboard or-IN
00000463 Pashto (Afghanistan) keyboard ps-AF
00050429 Persian (Standard) keyboard fa-AF
00000415 Polish (Programmers) keyboard pl-PL
00000416 Portuguese (Brazil ABNT) keyboard pt-BR
00000816 Portuguese keyboard pt-PT
00000446 Punjabi keyboard pa-IN
00010418 Romanian (Standard) keyboard ro-RO, ro-MD
00000485 Sakha keyboard sah-RU
0001083B Finnish with Sami keyboard smn-FI, sms-FI, se-FI
0000083B Swedish with Sami keyboard smj-SE, sma-SE, se-SE
00011809 Scottish Gaelic keyboard gd-GB
0000081A Serbian (Latin) keyboard sr-Latn-RS, sr-Latn-BA, sr-Latn-ME
00000C1A Serbian (Cyrillic) keyboard sr-Cyrl-RS, sr-Cyrl-BA, sr-Cyrl-ME
0000046C Sesotho sa Leboa keyboard nso-ZA
00000432 Setswana keyboard tn-ZA, tn-BW
0000045B Sinhala keyboard si-LK
0000041B Slovak keyboard sk-SK
0000045A Syriac keyboard syr-SY
00000428 Tajik keyboard tg-Cyrl-TJ
00020449 Tamil 99 keyboard ta-IN, ta-LK
00010444 Tatar keyboard tt-RU
0000044A Telugu keyboard te-IN
0000041E Thai Kedmanee keyboard th-TH
00010451 Tibetan (PRC) - Updated keyboard bo-CN
00000451 Tibetan (PRC) keyboard bo-CN
0000041F Turkish Q keyboard tr-TR
00000442 Turkmen keyboard tk-TM
00020422 Ukrainian (Enhanced) keyboard uk-UA
00010480 Uyghur keyboard ug-CN
00000843 Uzbek Cyrillic keyboard uz-Cyrl-UZ
00000452 United Kingdom Extended keyboard cy-GB
0000046A Yoruba keyboard yo-NG

There's some ambiguity yet for en-US, en-GB, hi-IN and bo-CN but I think we can assume the following default values:

Default Locale KLID Layout Name
✔️ hi-IN 00010439 Hindi Traditional keyboard
hi-IN 00000439 Devanagari - INSCRIPT keyboard
✔️ bo-CN 00010451 Tibetan (PRC) - Updated keyboard
bo-CN 00000451 Tibetan (PRC) keyboard
✔️ en-US 00000409 US keyboard
en-US 00000406 Danish keyboard
en-US 00020409 United States-International keyboard
en-GB 0000040C French keyboard
en-GB 00000407 German keyboard
✔️ en-GB 00000809 United Kingdom keyboard
en-GB 00020409 United States-International keyboard
en-GB 0000040B Finnish keyboard
en-GB 00000424 Slovenian keyboard
en-GB 0000041D Swedish keyboard
dgozman commented 8 months ago

We have updated our docs and this issue to emphasize that in most circumstances you do not need to call type() or pressSequentially(), but instead should just call fill(). This method works regardless of the keyboard layout.

If you have tried using pressSequentially() and it does not work for you, and you still need keyboard layouts support in type()/pressSequentially(), please comment here with your usecase. Ideally, share a repro so that we can figure out why fill() does not work for you. This will greatly help us prioritize this feature relative to other issues. Thank you!

polyrainbow commented 1 month ago

If you have tried using pressSequentially() and it does not work for you, and you still need keyboard layouts support in type()/pressSequentially(), please comment here with your usecase.

The app that I am testing had a bug where each keystroke resulted in a re-render of the contentEditable HTML element that the user was typing in. This was not noticeable for normal typing, but it prevented the usage of dead keys: When pressing a dead key, the element would re-render and thus resetting the keyboard mapping to default again, instead of changing the mapping temporarily for the next keystroke.

Dead keys are only present on some keyboard layouts, e.g. "US-International - PC". As I want to simulate the press of a dead key plus a follow-up keystroke for a test, I'd need support for specific layouts.

Example: Pressing Option+u and then u should result in ü (on macOS with layout "US-International - PC").

ruifigueira commented 1 month ago

I did implement other keyboard layouts in !24249 with deadkeys support but there were too many corner cases, so at the end that was not merged.

On chrome, you can use CDPSession to send all required events. For instance, you can check which events need to be sent in W3C Keyboard Event Viewer, and send the corresponding events with Input.dispatchKeyEvent.

polyrainbow commented 1 month ago

Thanks for the tip. I have tried to reproduce the behavior with CDPSession but I guess I'd need the missing Input.imeCommitComposition command to exactly reproduce it, as you also have pointed out in https://github.com/microsoft/playwright/issues/5777#issuecomment-1651735889

ruifigueira commented 1 month ago

Actually, for committing you can use the Input.insertText command: https://github.com/microsoft/playwright/issues/5777#issuecomment-1651784399

polyrainbow commented 1 month ago

It is true that Input.insertText triggers the compositionend event but it does not help me with my test, because Input.insertText does not care if there was a composition session in progress or not. The provided text is correctly inserted anyway.

For my Playwright test, I want to assert that a composition session was not erroneously aborted by the browser re-rendering the input element. If the fix is applied, the resulting text in the form is ü, without the fix, it is ¨u. Using Input.insertText in the test commits the composition session but it would result in the correct result (ü) anyway, no matter if it commits a composition or not. So that test always succeeds, with or without bugfix.

bricefriha commented 2 weeks ago

would it be possible to utilise something like .charCodeAt(0) to reduce all complexity?

However, I don't doubt there is a very good reason why we are not using it (and I'd love to hear it) 🙂