Bug 1396823 - Use unicode-segmentation to iterate graphemes instead of chars r=ato draft
authorGreg Fraley <gsfraley@gmail.com>
Tue, 06 Mar 2018 21:31:14 -0500
changeset 764006 0af1eb0af8d23346a1b20ac6a04964d974232850
parent 763746 ae94c562fe0417960b1365a36b87f60ec97005c4
push id101642
push userbmo:gsfraley@gmail.com
push dateWed, 07 Mar 2018 02:31:36 +0000
reviewersato
bugs1396823
milestone60.0a1
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
@@ -2037,16 +2037,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;