From dd44b4368f4eb134febbdff741e46723b4cc3eed Mon Sep 17 00:00:00 2001 From: Simon Stewart Date: Wed, 27 Jul 2016 11:55:44 +0100 Subject: [PATCH] Implement `{get,set}CursorPosition` for the RC emulation. Again, enough to get the core test to pass. No idea if it works. --- .../com/thoughtworks/selenium/webdriven/BUCK | 3 +- .../selenium/webdriven/SeleneseCommand.java | 2 +- .../webdriven/WebDriverCommandProcessor.java | 4 ++ .../webdriven/commands/AlertOverride.java | 37 ++++++++++++ .../commands/AnswerOnNextPrompt.java | 13 ++-- .../webdriven/commands/GetCursorPosition.java | 60 +++++++++++++++++++ .../webdriven/commands/SetCursorPosition.java | 54 +++++++++++++++++ .../webdriven/commands/WaitForPopup.java | 2 +- javascript/selenium-atoms/BUCK | 7 +++ javascript/selenium-atoms/text.js | 27 +++++++++ javascript/selenium-core/BUCK | 1 - 11 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 java/client/src/com/thoughtworks/selenium/webdriven/commands/GetCursorPosition.java create mode 100644 java/client/src/com/thoughtworks/selenium/webdriven/commands/SetCursorPosition.java diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/BUCK b/java/client/src/com/thoughtworks/selenium/webdriven/BUCK index 7e9245f801b3a..a57ce6720558c 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/BUCK +++ b/java/client/src/com/thoughtworks/selenium/webdriven/BUCK @@ -23,6 +23,7 @@ java_library(name = 'webdriven', ':isSomethingSelected', ':isTextPresent', ':isVisible', + ':setCursorPosition', ':type', # TODO(simons): remove sizzle. There are no longer any browsers that need it. ':sizzle', @@ -67,7 +68,7 @@ java_library(name = 'emulation-api', for name in ['findElement', 'findOption', 'fireEvent', 'fireEventAt', 'getAttribute', 'getText', 'linkLocator', 'isElementPresent', 'isSomethingSelected', 'isTextPresent', - 'isVisible', 'type',]: + 'isVisible', 'setCursorPosition', 'type',]: export_file(name = name, out = '%s.js' % name, src = '//javascript/selenium-atoms:%s' % name, diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/SeleneseCommand.java b/java/client/src/com/thoughtworks/selenium/webdriven/SeleneseCommand.java index 1c2bba39dfa86..3edd05a99cd1f 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/SeleneseCommand.java +++ b/java/client/src/com/thoughtworks/selenium/webdriven/SeleneseCommand.java @@ -50,7 +50,7 @@ public void setDefaultTimeout(long defaultTimeout) { this.defaultTimeout = defaultTimeout; } - protected long getTimeout(String timeout) { + protected long toLong(String timeout) { // Of course, a non-breaking space doesn't count as whitespace. timeout = timeout.replace('\u00A0',' ').trim(); return "".equals(timeout) ? defaultTimeout : Long.valueOf(timeout); diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/WebDriverCommandProcessor.java b/java/client/src/com/thoughtworks/selenium/webdriven/WebDriverCommandProcessor.java index fe4ff693c8412..888dd9ffe3673 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/WebDriverCommandProcessor.java +++ b/java/client/src/com/thoughtworks/selenium/webdriven/WebDriverCommandProcessor.java @@ -239,6 +239,7 @@ private void setUpMethodMap() { seleneseMethods.put("getConfirmation", new GetConfirmation(alertOverride)); seleneseMethods.put("getCookie", new GetCookie()); seleneseMethods.put("getCookieByName", new GetCookieByName()); + seleneseMethods.put("getCursorPosition", new GetCursorPosition(elementFinder)); seleneseMethods.put("getElementHeight", new GetElementHeight(elementFinder)); seleneseMethods.put("getElementIndex", new GetElementIndex(elementFinder, javascriptLibrary)); @@ -319,6 +320,9 @@ private void setUpMethodMap() { seleneseMethods.put("selectWindow", new SelectWindow(windows)); seleneseMethods.put("setBrowserLogLevel", new NoOp(null)); seleneseMethods.put("setContext", new NoOp(null)); + seleneseMethods.put( + "setCursorPosition", + new SetCursorPosition(javascriptLibrary, elementFinder)); seleneseMethods.put("setSpeed", new NoOp(null)); seleneseMethods.put("setTimeout", new SetTimeout(timer)); seleneseMethods.put("shiftKeyDown", new ShiftKeyDown(keyState)); diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/commands/AlertOverride.java b/java/client/src/com/thoughtworks/selenium/webdriven/commands/AlertOverride.java index 10d1cab119d41..7b3f1898af62f 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/commands/AlertOverride.java +++ b/java/client/src/com/thoughtworks/selenium/webdriven/commands/AlertOverride.java @@ -61,6 +61,18 @@ public void replaceAlertMethod(WebDriver driver) { " window.localStorage.setItem('__webdriverNextConfirm', JSON.stringify(true)); " + " return res; " + " }; " + + " window.localStorage.setItem('__webdriverPrompts', JSON.stringify([])); " + + " if (!('__webdriverNextPrompt' in window.localStorage)) { " + + " window.localStorage.setItem('__webdriverNextPrompt', JSON.stringify(true)); " + + " } " + + " window.prompt = function(msg, def) { " + + " var prompts = JSON.parse(window.localStorage.getItem('__webdriverPrompts')); " + + " prompts.push(msg || def); " + + " window.localStorage.setItem('__webdriverPrompts', JSON.stringify(prompts)); " + + " var res = JSON.parse(window.localStorage.getItem('__webdriverNextPrompts')); " + + " window.localStorage.setItem('__webdriverNextPrompts', JSON.stringify(true)); " + + " return res; " + + " }; " + "} else { " + " if (window.__webdriverAlerts) { return; } " + " window.__webdriverAlerts = []; " + @@ -73,6 +85,14 @@ public void replaceAlertMethod(WebDriver driver) { " window.__webdriverNextConfirm = true; " + " return res; " + " }; " + + " window.__webdriverPrompts = []; " + + " window.__webdriverNextPrompts = true; " + + " window.prompt = function(msg, def) { " + + " window.__webdriverPrompt.push(msg || def); " + + " var res = window.__webdriverNextPrompt; " + + " window.__webdriverNextPrompt = true; " + + " return res; " + + " }; " + "}" ); } @@ -173,4 +193,21 @@ public boolean isConfirmationPresent(WebDriver driver) { "}" )); } + + public boolean isPromptPresent(WebDriver driver) { + checkOverridesEnabled(); + return Boolean.TRUE.equals(((JavascriptExecutor) driver).executeScript( + "var canUseLocalStorage = false; " + + "try { canUseLocalStorage = !!window.localStorage; } catch(ex) { /* probe failed */ } " + + "var canUseJSON = false; " + + "try { canUseJSON = !!JSON; } catch(ex) { /* probe failed */ } " + + "if (canUseLocalStorage && canUseJSON) { " + + " if (!('__webdriverPrompts' in window.localStorage)) { return false } " + + " var prompts = JSON.parse(window.localStorage.getItem('__webdriverPrompts')); " + + " return prompts && prompts.length > 0; " + + "} else { " + + " return window.__webdriverPrompts && window.__webdriverPrompts.length > 0; " + + "}" + )); + } } diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/commands/AnswerOnNextPrompt.java b/java/client/src/com/thoughtworks/selenium/webdriven/commands/AnswerOnNextPrompt.java index 8868ba5d4984b..b3e52757c23cd 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/commands/AnswerOnNextPrompt.java +++ b/java/client/src/com/thoughtworks/selenium/webdriven/commands/AnswerOnNextPrompt.java @@ -23,11 +23,6 @@ import org.openqa.selenium.WebDriver; public class AnswerOnNextPrompt extends SeleneseCommand { - private final boolean result; - - public AnswerOnNextPrompt(boolean result) { - this.result = result; - } @Override protected Void handleSeleneseCommand(WebDriver driver, String locator, String value) { @@ -37,11 +32,11 @@ protected Void handleSeleneseCommand(WebDriver driver, String locator, String va "var canUseJSON = false; " + "try { canUseJSON = !!JSON; } catch(ex) { /* probe failed */ } " + "if (canUseLocalStorage && canUseJSON) { " + - " window.localStorage.setItem('__webdriverNextConfirm', JSON.stringify(arguments[0])); " + + " window.localStorage.setItem('__webdriverNextPrompt', JSON.stringify(arguments[0])); " + "} else { " + - " window.__webdriverNextConfirm = arguments[0];" + - "}" - , result); + " window.__webdriverNextPrompt = arguments[0];" + + "}", + locator); return null; } } diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/commands/GetCursorPosition.java b/java/client/src/com/thoughtworks/selenium/webdriven/commands/GetCursorPosition.java new file mode 100644 index 0000000000000..728a1b789b95a --- /dev/null +++ b/java/client/src/com/thoughtworks/selenium/webdriven/commands/GetCursorPosition.java @@ -0,0 +1,60 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.thoughtworks.selenium.webdriven.commands; + +import com.google.common.base.Joiner; + +import com.thoughtworks.selenium.webdriven.ElementFinder; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class GetCursorPosition extends com.thoughtworks.selenium.webdriven.SeleneseCommand { + + private final ElementFinder finder; + + public GetCursorPosition(ElementFinder finder) { + this.finder = finder; + } + + @Override + protected Number handleSeleneseCommand(WebDriver driver, String locator, String value) { + // All supported browsers apparently support "document.selection". Let's use that and the + // relevant snippet of code from the original selenium core to implement this. What could + // possibly go wrong? + + WebElement element = finder.findElement(driver, locator); + + return (Number) ((JavascriptExecutor) driver).executeScript( + Joiner.on("\n").join( + "try {", + " var selectRange = document.selection.createRange().duplicate();", + " var elementRange = arguments[0].createTextRange();", + " selectRange.move('character', 0)", + " elementRange.move('character', 0);", + " var inRange1 = selectRange.inRange(elementRange);", + " var inRange2 = elementRange.inRange(selectRange);", + " elementRange.setEndPoint('EndToEnd', selectRange);", + "} catch (e) {", + " throw Error('There is no cursor on this page!');", + "}", + "return String(elementRange.text).replace(/\r/g,' ').length;"), + element); + } +} diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/commands/SetCursorPosition.java b/java/client/src/com/thoughtworks/selenium/webdriven/commands/SetCursorPosition.java new file mode 100644 index 0000000000000..cc07b6ddff2f0 --- /dev/null +++ b/java/client/src/com/thoughtworks/selenium/webdriven/commands/SetCursorPosition.java @@ -0,0 +1,54 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.thoughtworks.selenium.webdriven.commands; + +import com.thoughtworks.selenium.webdriven.ElementFinder; +import com.thoughtworks.selenium.webdriven.JavascriptLibrary; +import com.thoughtworks.selenium.webdriven.SeleneseCommand; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; + +public class SetCursorPosition extends SeleneseCommand { + + private final JavascriptLibrary library; + private final ElementFinder finder; + + public SetCursorPosition(JavascriptLibrary library, ElementFinder finder) { + this.library = library; + this.finder = finder; + } + + + @Override + protected Void handleSeleneseCommand(WebDriver driver, String locator, String value) { + WebElement element = finder.findElement(driver, locator); + long position = toLong(value); + + String setPosition = library.getSeleniumScript("getText.js"); + + ((JavascriptExecutor) driver).executeScript( + "(" + setPosition + ")(arguments[0], arguments[1]);", + element, + position); + + return null; + } +} diff --git a/java/client/src/com/thoughtworks/selenium/webdriven/commands/WaitForPopup.java b/java/client/src/com/thoughtworks/selenium/webdriven/commands/WaitForPopup.java index 4253eae103cc9..12f0dc730d735 100644 --- a/java/client/src/com/thoughtworks/selenium/webdriven/commands/WaitForPopup.java +++ b/java/client/src/com/thoughtworks/selenium/webdriven/commands/WaitForPopup.java @@ -34,7 +34,7 @@ public WaitForPopup(Windows windows) { @Override protected Void handleSeleneseCommand(final WebDriver driver, final String windowID, final String timeout) { - final long millis = getTimeout(timeout); + final long millis = toLong(timeout); final String current = driver.getWindowHandle(); new Wait() { diff --git a/javascript/selenium-atoms/BUCK b/javascript/selenium-atoms/BUCK index 68b39bc7c9ece..0345d7be3802c 100644 --- a/javascript/selenium-atoms/BUCK +++ b/javascript/selenium-atoms/BUCK @@ -105,6 +105,13 @@ js_fragment(name = 'isVisible', visibility = [ '//java/client/src/com/thoughtworks/selenium/webdriven:isVisible' ], ) +js_fragment(name = 'setCursorPosition', + module = 'core.text', + function = 'core.text.setCursorPosition', + deps = [':deps'], + visibility = [ '//java/client/src/com/thoughtworks/selenium/webdriven:setCursorPosition' ], +) + js_fragment(name = 'type', module = 'core.events', function = 'core.events.setValue', diff --git a/javascript/selenium-atoms/text.js b/javascript/selenium-atoms/text.js index 9a5fd11df6a0b..3adefcd03d7ab 100644 --- a/javascript/selenium-atoms/text.js +++ b/javascript/selenium-atoms/text.js @@ -23,6 +23,7 @@ goog.provide('core.text'); goog.require('bot'); +goog.require('bot.events'); goog.require('bot.userAgent'); goog.require('core.patternMatcher'); goog.require('goog.dom'); @@ -220,3 +221,29 @@ core.text.linkLocator = function(locator, opt_doc) { } return null; }; + + +/** + * Set a new caret position within an element. + * + * @param {!Element} element The element to use. + * @param {number} position The new caret position. + */ +core.text.setCursorPosition = function(element, position) { + if (position == -1) { + position = element.value.length; + } + + if (element.setSelectionRange) { + element.focus(); + element.setSelectionRange(/*start*/ position, /*end*/ position); + } else if (element.createTextRange) { + bot.events.fire(element, bot.events.EventType.FOCUS); + var range = element.createTextRange(); + range.collapse(true); + range.moveEnd('character', position); + range.moveStart('character', position); + range.select(); + } +}; + diff --git a/javascript/selenium-core/BUCK b/javascript/selenium-core/BUCK index 8abe8e40c5516..7c5fb85a3d3d3 100644 --- a/javascript/selenium-core/BUCK +++ b/javascript/selenium-core/BUCK @@ -5,7 +5,6 @@ zip_file( ':core', ], visibility = [ - '//java/server/src/org/openqa/selenium/server:selenium-core', '//java/server/test/org/openqa/selenium:core-scripts', ], )