Bug 1396823 - Use unicode-segmentation to iterate graphemes instead of chars r=ato
authorGreg Fraley <gsfraley@gmail.com>
Tue, 06 Mar 2018 21:31:14 -0500
changeset 406902 9b506268413c
parent 406901 8a29a154e88d
child 406903 b8df7526c0f6
push id60734
push useratolfsen@mozilla.com
push dateWed, 07 Mar 2018 14:17:03 +0000
treeherderautoland@9b506268413c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1396823
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1396823 - Use unicode-segmentation to iterate graphemes instead of chars r=ato MozReview-Commit-ID: 8QsOmtXDnGI
Cargo.lock
testing/web-platform/tests/webdriver/tests/actions/special_keys.py
testing/webdriver/Cargo.toml
testing/webdriver/src/actions.rs
testing/webdriver/src/lib.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2029,16 +2029,17 @@ name = "webdriver"
 version = "0.34.0"
 dependencies = [
  "cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.57.0"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/testing/web-platform/tests/webdriver/tests/actions/special_keys.py
+++ b/testing/web-platform/tests/webdriver/tests/actions/special_keys.py
@@ -1,15 +1,15 @@
 # META: timeout=long
 
 import pytest
 import time
 from tests.actions.support.keys import ALL_EVENTS, Keys
 from tests.actions.support.refine import filter_dict, get_keys, get_events
-
+from webdriver import error
 
 @pytest.mark.parametrize("name,expected", ALL_EVENTS.items())
 def test_webdriver_special_key_sends_keydown(session,
                                              key_reporter,
                                              key_chain,
                                              name,
                                              expected):
     if name.startswith("F"):
@@ -38,8 +38,42 @@ def test_webdriver_special_key_sends_key
         del expected["code"]
     assert first_event == expected
     # only printable characters should be recorded in input field
     entered_keys = get_keys(key_reporter)
     if len(expected["key"]) == 1:
         assert entered_keys == expected["key"]
     else:
         assert len(entered_keys) == 0
+
+
+@pytest.mark.parametrize("value", [
+    (u"f"),
+    (u"\u0BA8\u0BBF"),
+    (u"\u1100\u1161\u11A8"),
+])
+def test_multiple_codepoint_keys_behave_correctly(session,
+                                                  key_reporter,
+                                                  key_chain,
+                                                  value):
+    key_chain \
+        .key_down(value) \
+        .key_up(value) \
+        .perform()
+
+    assert get_keys(key_reporter) == value
+
+
+@pytest.mark.parametrize("value", [
+    (u"fa"),
+    (u"\u0BA8\u0BBFb"),
+    (u"\u0BA8\u0BBF\u0BA8"),
+    (u"\u1100\u1161\u11A8c")
+])
+def test_invalid_multiple_codepoint_keys_fail(session,
+                                              key_reporter,
+                                              key_chain,
+                                              value):
+    with pytest.raises(error.InvalidArgumentException):
+        key_chain \
+            .key_down(value) \
+            .key_up(value) \
+            .perform()
\ No newline at end of file
--- a/testing/webdriver/Cargo.toml
+++ b/testing/webdriver/Cargo.toml
@@ -12,8 +12,9 @@ license = "MPL-2.0"
 [dependencies]
 cookie = { version = "0.10", default-features = false }
 hyper = "0.10"
 log = "0.4"
 regex = "0.2"
 rustc-serialize = "0.3"
 time = "0.1"
 url = "1"
+unicode-segmentation = "1.1.0"
\ No newline at end of file
--- a/testing/webdriver/src/actions.rs
+++ b/testing/webdriver/src/actions.rs
@@ -1,12 +1,13 @@
 use command::Parameters;
 use common::{Nullable, WebElement};
 use error::{WebDriverResult, WebDriverError, ErrorStatus};
 use rustc_serialize::json::{ToJson, Json};
+use unicode_segmentation::UnicodeSegmentation;
 use std::collections::BTreeMap;
 use std::default::Default;
 
 #[derive(Debug, PartialEq)]
 pub struct ActionSequence {
     pub id: Nullable<String>,
     pub actions: ActionsType
 }
@@ -363,36 +364,36 @@ impl ToJson for KeyAction {
     fn to_json(&self) -> Json {
         match self {
             &KeyAction::Down(ref x) => x.to_json(),
             &KeyAction::Up(ref x) => x.to_json(),
         }
     }
 }
 
-fn validate_key_value(value_str: &str) -> WebDriverResult<char> {
-    let mut chars = value_str.chars();
-    let value = if let Some(c) = chars.next() {
-        c
+fn validate_key_value(value_str: &str) -> WebDriverResult<String> {
+    let mut graphemes = value_str.graphemes(true);
+    let value = if let Some(g) = graphemes.next() {
+        g
     } else {
         return Err(WebDriverError::new(
             ErrorStatus::InvalidArgument,
             "Parameter 'value' was an empty string"))
     };
-    if chars.next().is_some() {
+    if graphemes.next().is_some() {
         return Err(WebDriverError::new(
             ErrorStatus::InvalidArgument,
-            "Parameter 'value' contained multiple characters"))
+            "Parameter 'value' contained multiple graphemes"))
     };
-    Ok(value)
+    Ok(value.to_string())
 }
 
 #[derive(Debug, PartialEq)]
 pub struct KeyUpAction {
-    pub value: char
+    pub value: String
 }
 
 impl Parameters for KeyUpAction {
     fn from_json(body: &Json) -> WebDriverResult<KeyUpAction> {
         let value_str = try_opt!(
                 try_opt!(body.find("value"),
                          ErrorStatus::InvalidArgument,
                          "Missing value parameter").as_string(),
@@ -414,17 +415,17 @@ impl ToJson for KeyUpAction {
         data.insert("value".to_string(),
                     self.value.to_string().to_json());
         Json::Object(data)
     }
 }
 
 #[derive(Debug, PartialEq)]
 pub struct KeyDownAction {
-    pub value: char
+    pub value: String
 }
 
 impl Parameters for KeyDownAction {
     fn from_json(body: &Json) -> WebDriverResult<KeyDownAction> {
         let value_str = try_opt!(
                 try_opt!(body.find("value"),
                          ErrorStatus::InvalidArgument,
                          "Missing value parameter").as_string(),
--- a/testing/webdriver/src/lib.rs
+++ b/testing/webdriver/src/lib.rs
@@ -3,16 +3,17 @@
 #[macro_use]
 extern crate log;
 extern crate rustc_serialize;
 extern crate hyper;
 extern crate regex;
 extern crate cookie;
 extern crate time;
 extern crate url;
+extern crate unicode_segmentation;
 
 #[macro_use] pub mod macros;
 pub mod actions;
 pub mod httpapi;
 pub mod capabilities;
 pub mod command;
 pub mod common;
 pub mod error;