Skip to content

Commit dd44b43

Browse files
committed
Implement {get,set}CursorPosition for the RC emulation.
Again, enough to get the core test to pass. No idea if it works.
1 parent 5dec77c commit dd44b43

File tree

11 files changed

+197
-13
lines changed

11 files changed

+197
-13
lines changed

java/client/src/com/thoughtworks/selenium/webdriven/BUCK

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ java_library(name = 'webdriven',
2323
':isSomethingSelected',
2424
':isTextPresent',
2525
':isVisible',
26+
':setCursorPosition',
2627
':type',
2728
# TODO(simons): remove sizzle. There are no longer any browsers that need it.
2829
':sizzle',
@@ -67,7 +68,7 @@ java_library(name = 'emulation-api',
6768

6869
for name in ['findElement', 'findOption', 'fireEvent', 'fireEventAt', 'getAttribute', 'getText',
6970
'linkLocator', 'isElementPresent', 'isSomethingSelected', 'isTextPresent',
70-
'isVisible', 'type',]:
71+
'isVisible', 'setCursorPosition', 'type',]:
7172
export_file(name = name,
7273
out = '%s.js' % name,
7374
src = '//javascript/selenium-atoms:%s' % name,

java/client/src/com/thoughtworks/selenium/webdriven/SeleneseCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void setDefaultTimeout(long defaultTimeout) {
5050
this.defaultTimeout = defaultTimeout;
5151
}
5252

53-
protected long getTimeout(String timeout) {
53+
protected long toLong(String timeout) {
5454
// Of course, a non-breaking space doesn't count as whitespace.
5555
timeout = timeout.replace('\u00A0',' ').trim();
5656
return "".equals(timeout) ? defaultTimeout : Long.valueOf(timeout);

java/client/src/com/thoughtworks/selenium/webdriven/WebDriverCommandProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ private void setUpMethodMap() {
239239
seleneseMethods.put("getConfirmation", new GetConfirmation(alertOverride));
240240
seleneseMethods.put("getCookie", new GetCookie());
241241
seleneseMethods.put("getCookieByName", new GetCookieByName());
242+
seleneseMethods.put("getCursorPosition", new GetCursorPosition(elementFinder));
242243
seleneseMethods.put("getElementHeight", new GetElementHeight(elementFinder));
243244
seleneseMethods.put("getElementIndex", new GetElementIndex(elementFinder,
244245
javascriptLibrary));
@@ -319,6 +320,9 @@ private void setUpMethodMap() {
319320
seleneseMethods.put("selectWindow", new SelectWindow(windows));
320321
seleneseMethods.put("setBrowserLogLevel", new NoOp(null));
321322
seleneseMethods.put("setContext", new NoOp(null));
323+
seleneseMethods.put(
324+
"setCursorPosition",
325+
new SetCursorPosition(javascriptLibrary, elementFinder));
322326
seleneseMethods.put("setSpeed", new NoOp(null));
323327
seleneseMethods.put("setTimeout", new SetTimeout(timer));
324328
seleneseMethods.put("shiftKeyDown", new ShiftKeyDown(keyState));

java/client/src/com/thoughtworks/selenium/webdriven/commands/AlertOverride.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ public void replaceAlertMethod(WebDriver driver) {
6161
" window.localStorage.setItem('__webdriverNextConfirm', JSON.stringify(true)); " +
6262
" return res; " +
6363
" }; " +
64+
" window.localStorage.setItem('__webdriverPrompts', JSON.stringify([])); " +
65+
" if (!('__webdriverNextPrompt' in window.localStorage)) { " +
66+
" window.localStorage.setItem('__webdriverNextPrompt', JSON.stringify(true)); " +
67+
" } " +
68+
" window.prompt = function(msg, def) { " +
69+
" var prompts = JSON.parse(window.localStorage.getItem('__webdriverPrompts')); " +
70+
" prompts.push(msg || def); " +
71+
" window.localStorage.setItem('__webdriverPrompts', JSON.stringify(prompts)); " +
72+
" var res = JSON.parse(window.localStorage.getItem('__webdriverNextPrompts')); " +
73+
" window.localStorage.setItem('__webdriverNextPrompts', JSON.stringify(true)); " +
74+
" return res; " +
75+
" }; " +
6476
"} else { " +
6577
" if (window.__webdriverAlerts) { return; } " +
6678
" window.__webdriverAlerts = []; " +
@@ -73,6 +85,14 @@ public void replaceAlertMethod(WebDriver driver) {
7385
" window.__webdriverNextConfirm = true; " +
7486
" return res; " +
7587
" }; " +
88+
" window.__webdriverPrompts = []; " +
89+
" window.__webdriverNextPrompts = true; " +
90+
" window.prompt = function(msg, def) { " +
91+
" window.__webdriverPrompt.push(msg || def); " +
92+
" var res = window.__webdriverNextPrompt; " +
93+
" window.__webdriverNextPrompt = true; " +
94+
" return res; " +
95+
" }; " +
7696
"}"
7797
);
7898
}
@@ -173,4 +193,21 @@ public boolean isConfirmationPresent(WebDriver driver) {
173193
"}"
174194
));
175195
}
196+
197+
public boolean isPromptPresent(WebDriver driver) {
198+
checkOverridesEnabled();
199+
return Boolean.TRUE.equals(((JavascriptExecutor) driver).executeScript(
200+
"var canUseLocalStorage = false; " +
201+
"try { canUseLocalStorage = !!window.localStorage; } catch(ex) { /* probe failed */ } " +
202+
"var canUseJSON = false; " +
203+
"try { canUseJSON = !!JSON; } catch(ex) { /* probe failed */ } " +
204+
"if (canUseLocalStorage && canUseJSON) { " +
205+
" if (!('__webdriverPrompts' in window.localStorage)) { return false } " +
206+
" var prompts = JSON.parse(window.localStorage.getItem('__webdriverPrompts')); " +
207+
" return prompts && prompts.length > 0; " +
208+
"} else { " +
209+
" return window.__webdriverPrompts && window.__webdriverPrompts.length > 0; " +
210+
"}"
211+
));
212+
}
176213
}

java/client/src/com/thoughtworks/selenium/webdriven/commands/AnswerOnNextPrompt.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
import org.openqa.selenium.WebDriver;
2424

2525
public class AnswerOnNextPrompt extends SeleneseCommand<Void> {
26-
private final boolean result;
27-
28-
public AnswerOnNextPrompt(boolean result) {
29-
this.result = result;
30-
}
3126

3227
@Override
3328
protected Void handleSeleneseCommand(WebDriver driver, String locator, String value) {
@@ -37,11 +32,11 @@ protected Void handleSeleneseCommand(WebDriver driver, String locator, String va
3732
"var canUseJSON = false; " +
3833
"try { canUseJSON = !!JSON; } catch(ex) { /* probe failed */ } " +
3934
"if (canUseLocalStorage && canUseJSON) { " +
40-
" window.localStorage.setItem('__webdriverNextConfirm', JSON.stringify(arguments[0])); " +
35+
" window.localStorage.setItem('__webdriverNextPrompt', JSON.stringify(arguments[0])); " +
4136
"} else { " +
42-
" window.__webdriverNextConfirm = arguments[0];" +
43-
"}"
44-
, result);
37+
" window.__webdriverNextPrompt = arguments[0];" +
38+
"}",
39+
locator);
4540
return null;
4641
}
4742
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package com.thoughtworks.selenium.webdriven.commands;
19+
20+
import com.google.common.base.Joiner;
21+
22+
import com.thoughtworks.selenium.webdriven.ElementFinder;
23+
24+
import org.openqa.selenium.JavascriptExecutor;
25+
import org.openqa.selenium.WebDriver;
26+
import org.openqa.selenium.WebElement;
27+
28+
public class GetCursorPosition extends com.thoughtworks.selenium.webdriven.SeleneseCommand<Number> {
29+
30+
private final ElementFinder finder;
31+
32+
public GetCursorPosition(ElementFinder finder) {
33+
this.finder = finder;
34+
}
35+
36+
@Override
37+
protected Number handleSeleneseCommand(WebDriver driver, String locator, String value) {
38+
// All supported browsers apparently support "document.selection". Let's use that and the
39+
// relevant snippet of code from the original selenium core to implement this. What could
40+
// possibly go wrong?
41+
42+
WebElement element = finder.findElement(driver, locator);
43+
44+
return (Number) ((JavascriptExecutor) driver).executeScript(
45+
Joiner.on("\n").join(
46+
"try {",
47+
" var selectRange = document.selection.createRange().duplicate();",
48+
" var elementRange = arguments[0].createTextRange();",
49+
" selectRange.move('character', 0)",
50+
" elementRange.move('character', 0);",
51+
" var inRange1 = selectRange.inRange(elementRange);",
52+
" var inRange2 = elementRange.inRange(selectRange);",
53+
" elementRange.setEndPoint('EndToEnd', selectRange);",
54+
"} catch (e) {",
55+
" throw Error('There is no cursor on this page!');",
56+
"}",
57+
"return String(elementRange.text).replace(/\r/g,' ').length;"),
58+
element);
59+
}
60+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package com.thoughtworks.selenium.webdriven.commands;
19+
20+
import com.thoughtworks.selenium.webdriven.ElementFinder;
21+
import com.thoughtworks.selenium.webdriven.JavascriptLibrary;
22+
import com.thoughtworks.selenium.webdriven.SeleneseCommand;
23+
24+
import org.openqa.selenium.JavascriptExecutor;
25+
import org.openqa.selenium.WebDriver;
26+
import org.openqa.selenium.WebDriverException;
27+
import org.openqa.selenium.WebElement;
28+
29+
public class SetCursorPosition extends SeleneseCommand<Void> {
30+
31+
private final JavascriptLibrary library;
32+
private final ElementFinder finder;
33+
34+
public SetCursorPosition(JavascriptLibrary library, ElementFinder finder) {
35+
this.library = library;
36+
this.finder = finder;
37+
}
38+
39+
40+
@Override
41+
protected Void handleSeleneseCommand(WebDriver driver, String locator, String value) {
42+
WebElement element = finder.findElement(driver, locator);
43+
long position = toLong(value);
44+
45+
String setPosition = library.getSeleniumScript("getText.js");
46+
47+
((JavascriptExecutor) driver).executeScript(
48+
"(" + setPosition + ")(arguments[0], arguments[1]);",
49+
element,
50+
position);
51+
52+
return null;
53+
}
54+
}

java/client/src/com/thoughtworks/selenium/webdriven/commands/WaitForPopup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public WaitForPopup(Windows windows) {
3434
@Override
3535
protected Void handleSeleneseCommand(final WebDriver driver, final String windowID,
3636
final String timeout) {
37-
final long millis = getTimeout(timeout);
37+
final long millis = toLong(timeout);
3838
final String current = driver.getWindowHandle();
3939

4040
new Wait() {

javascript/selenium-atoms/BUCK

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ js_fragment(name = 'isVisible',
105105
visibility = [ '//java/client/src/com/thoughtworks/selenium/webdriven:isVisible' ],
106106
)
107107

108+
js_fragment(name = 'setCursorPosition',
109+
module = 'core.text',
110+
function = 'core.text.setCursorPosition',
111+
deps = [':deps'],
112+
visibility = [ '//java/client/src/com/thoughtworks/selenium/webdriven:setCursorPosition' ],
113+
)
114+
108115
js_fragment(name = 'type',
109116
module = 'core.events',
110117
function = 'core.events.setValue',

javascript/selenium-atoms/text.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ goog.provide('core.text');
2323

2424

2525
goog.require('bot');
26+
goog.require('bot.events');
2627
goog.require('bot.userAgent');
2728
goog.require('core.patternMatcher');
2829
goog.require('goog.dom');
@@ -220,3 +221,29 @@ core.text.linkLocator = function(locator, opt_doc) {
220221
}
221222
return null;
222223
};
224+
225+
226+
/**
227+
* Set a new caret position within an element.
228+
*
229+
* @param {!Element} element The element to use.
230+
* @param {number} position The new caret position.
231+
*/
232+
core.text.setCursorPosition = function(element, position) {
233+
if (position == -1) {
234+
position = element.value.length;
235+
}
236+
237+
if (element.setSelectionRange) {
238+
element.focus();
239+
element.setSelectionRange(/*start*/ position, /*end*/ position);
240+
} else if (element.createTextRange) {
241+
bot.events.fire(element, bot.events.EventType.FOCUS);
242+
var range = element.createTextRange();
243+
range.collapse(true);
244+
range.moveEnd('character', position);
245+
range.moveStart('character', position);
246+
range.select();
247+
}
248+
};
249+

0 commit comments

Comments
 (0)