Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/PlaywrightNative.Tests/PageKeyboardTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,30 @@ await page.EvaluateAsync<string>("document.querySelector('textarea').value").Con
Is.EqualTo("嗨a"));
}

[PlaywrightTest("page-keyboard.spec.ts", "should not type text with Alt modifier")]
[Test]
[Timeout(30_000)]
public async Task ShouldNotTypeTextWithAltModifier()
{
await using IBrowser browser = await BrowserLauncher.LaunchAsync().ConfigureAwait(false);
IPage page = await NewPageAsync(browser).ConfigureAwait(false);
await page.SetContentAsync("<input><textarea></textarea><div contenteditable></div>").ConfigureAwait(false);
foreach (ILocator locator in new[] { page.Locator("input"), page.Locator("textarea"), page.Locator("div") })
{
await locator.PressAsync("Alt+KeyA").ConfigureAwait(false);
await locator.PressAsync("Alt+Digit1").ConfigureAwait(false);
await locator.PressAsync("Alt+Shift+KeyA").ConfigureAwait(false);
await page.Keyboard.DownAsync("Alt").ConfigureAwait(false);
await page.Keyboard.PressAsync("b").ConfigureAwait(false);
await page.Keyboard.UpAsync("Alt").ConfigureAwait(false);
await locator.PressAsync("c").ConfigureAwait(false);
}

await Assertions.Expect(page.Locator("input")).ToHaveValueAsync("c").ConfigureAwait(false);
await Assertions.Expect(page.Locator("textarea")).ToHaveValueAsync("c").ConfigureAwait(false);
await Assertions.Expect(page.Locator("div")).ToHaveTextAsync("c").ConfigureAwait(false);
}

[PlaywrightTest("page-keyboard.spec.ts", "insertText should only emit input event")]
[Test]
[Timeout(30_000)]
Expand Down Expand Up @@ -707,6 +731,57 @@ public async Task ShouldSpecifyLocation()
Assert.That(await lastEvent.EvaluateAsync<string>("e => e.code").ConfigureAwait(false), Is.EqualTo("NumpadSubtract"));
}

[PlaywrightTest("page-keyboard.spec.ts", "should specify numpad location on keyup")]
[Test]
[Timeout(30_000)]
public async Task ShouldSpecifyNumpadLocationOnKeyup()
{
await using IBrowser browser = await BrowserLauncher.LaunchAsync().ConfigureAwait(false);
IPage page = await NewPageAsync(browser).ConfigureAwait(false);
await page.GoToAsync(Prefix + "/input/keyboard.html").ConfigureAwait(false);
await page.Keyboard.PressAsync("NumpadSubtract").ConfigureAwait(false);
Assert.That(
await GetResultAsync(page).ConfigureAwait(false),
Is.EqualTo(string.Join(
"\n",
"Keydown: - NumpadSubtract NUMPAD []",
"Keypress: - NumpadSubtract NUMPAD 45 []",
"Keyup: - NumpadSubtract NUMPAD []")));
}

[PlaywrightTest("page-keyboard.spec.ts", "should press NumpadDecimal as Delete")]
[Test]
[Timeout(30_000)]
public async Task ShouldPressNumpadDecimalAsDelete()
{
await using IBrowser browser = await BrowserLauncher.LaunchAsync().ConfigureAwait(false);
IPage page = await NewPageAsync(browser).ConfigureAwait(false);
await page.SetContentAsync("<textarea></textarea>").ConfigureAwait(false);
IJSHandle lastEvent = await CaptureLastKeydownAsync(page).ConfigureAwait(false);
ILocator textarea = page.Locator("textarea");

await textarea.PressAsync("NumpadDecimal").ConfigureAwait(false);
Assert.That(await lastEvent.EvaluateAsync<string>("e => e.key").ConfigureAwait(false), Is.EqualTo("Delete"));
Assert.That(await lastEvent.EvaluateAsync<string>("e => e.code").ConfigureAwait(false), Is.EqualTo("NumpadDecimal"));
Assert.That(await lastEvent.EvaluateAsync<int>("e => e.location").ConfigureAwait(false), Is.EqualTo(3));
await Assertions.Expect(textarea).ToHaveValueAsync(string.Empty).ConfigureAwait(false);
}

[PlaywrightTest("page-keyboard.spec.ts", "should type numpad digits with Shift")]
[Test]
[Timeout(30_000)]
public async Task ShouldTypeNumpadDigitsWithShift()
{
await using IBrowser browser = await BrowserLauncher.LaunchAsync().ConfigureAwait(false);
IPage page = await NewPageAsync(browser).ConfigureAwait(false);
await page.SetContentAsync("<textarea></textarea>").ConfigureAwait(false);
ILocator textarea = page.Locator("textarea");
await textarea.PressAsync("Shift+Numpad1").ConfigureAwait(false);
await textarea.PressAsync("Shift+NumpadDecimal").ConfigureAwait(false);
await textarea.PressAsync("Shift+Numpad9").ConfigureAwait(false);
await Assertions.Expect(textarea).ToHaveValueAsync("1.9").ConfigureAwait(false);
}

[PlaywrightTest("page-keyboard.spec.ts", "should press Enter")]
[Test]
[Timeout(30_000)]
Expand Down
1 change: 1 addition & 0 deletions src/PlaywrightNative/Chromium/CRRawKeyboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public Task KeyUpAsync(IReadOnlyCollection<Input.KeyboardModifier> modifiers, In
windowsVirtualKeyCode = key.KeyCodeWithoutLocation == 0 ? key.KeyCode : key.KeyCodeWithoutLocation,
code = key.Code,
location = key.Location,
isKeypad = key.Location == 3,
});
}

Expand Down
42 changes: 41 additions & 1 deletion src/PlaywrightNative/Input/USKeyboardLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,24 @@ private static Dictionary<string, KeyDefinition> BuildLayout()
AddKey(map, "MediaTrackPrevious", 177, "MediaTrackPrevious", string.Empty);
AddKey(map, "MediaPlayPause", 179, "MediaPlayPause", string.Empty);

// Numpad. Digit and decimal keys act as navigation keys unless Shift is held.
AddKey(map, "NumLock", 144, "NumLock", string.Empty);
AddKey(map, "NumpadDivide", 111, "/", "/", location: 3);
AddKey(map, "NumpadMultiply", 106, "*", "*", location: 3);
AddKey(map, "NumpadSubtract", 109, "-", "-", location: 3);
AddNumpad(map, "Numpad7", 36, 103, "Home", "7");
AddNumpad(map, "Numpad8", 38, 104, "ArrowUp", "8");
AddNumpad(map, "Numpad9", 33, 105, "PageUp", "9");
AddNumpad(map, "Numpad4", 37, 100, "ArrowLeft", "4");
AddNumpad(map, "Numpad5", 12, 101, "Clear", "5");
AddNumpad(map, "Numpad6", 39, 102, "ArrowRight", "6");
AddKey(map, "NumpadAdd", 107, "+", "+", location: 3);
AddNumpad(map, "Numpad1", 35, 97, "End", "1");
AddNumpad(map, "Numpad2", 40, 98, "ArrowDown", "2");
AddNumpad(map, "Numpad3", 34, 99, "PageDown", "3");
AddNumpad(map, "Numpad0", 45, 96, "Insert", "0");
AddNumpad(map, "NumpadDecimal", 46, 110, "Delete", ".");
AddKey(map, "NumpadEnter", 13, "Enter", "\r", location: 3);
AddKey(map, "NumpadSubtract", 109, "-", string.Empty, location: 3);

// Modifier keys — both location variants share keyCodeWithoutLocation.
map["ShiftLeft"] = new KeyDefinition { KeyCode = 160, KeyCodeWithoutLocation = 16, Code = "ShiftLeft", Key = "Shift", Location = 1 };
Expand Down Expand Up @@ -214,6 +230,30 @@ private static KeyDefinition AddKey(Dictionary<string, KeyDefinition> map, strin
return def;
}

private static void AddNumpad(Dictionary<string, KeyDefinition> map, string code, int keyCode, int shiftKeyCode, string key, string shiftKey)
{
KeyDefinition shifted = new()
{
KeyCode = shiftKeyCode,
KeyCodeWithoutLocation = shiftKeyCode,
Code = code,
Key = shiftKey,
Text = shiftKey,
Location = 3,
};

map[code] = new KeyDefinition
{
KeyCode = keyCode,
KeyCodeWithoutLocation = keyCode,
Code = code,
Key = key,
Text = string.Empty,
Location = 3,
Shifted = shifted,
};
}

private static void AddPrintable(Dictionary<string, KeyDefinition> map, string code, int keyCode, string key, string shiftedKey)
{
KeyDefinition shifted = new()
Expand Down
Loading