1
0
Fork 0
mirror of https://github.com/teampoltergeist/poltergeist.git synced 2022-11-09 12:05:00 -05:00

Don't alter focus with send_keys if we're already in the target element.

If the click event is fired on the target element, then the caret
position that was previously set by, e.g.,

  send_keys(:Left)

is lost. This addresses that by first checking if the target element
already contains the current selection, meaning we have focus within the
target element and don't need to first give it focus.

Fixes #493.
This commit is contained in:
Adam Prescott 2014-05-16 20:01:22 -04:00
parent 05531dfd62
commit b67c643b74
7 changed files with 44 additions and 5 deletions

View file

@ -235,6 +235,16 @@ class PoltergeistAgent.Node
isDisabled: ->
@element.disabled || @element.tagName == 'OPTION' && @element.parentNode.disabled
containsSelection: ->
selectedNode = document.getSelection().focusNode
return false if !selectedNode
if selectedNode.nodeType == 3
selectedNode = selectedNode.parentNode
@element.contains(selectedNode)
frameOffset: ->
win = window
offset = { top: 0, left: 0 }

View file

@ -262,9 +262,13 @@ class Poltergeist.Browser
this.sendResponse(true)
send_keys: (page_id, id, keys) ->
target = this.node(page_id, id)
# Programmatically generated focus doesn't work for `sendKeys`.
# That's why we need something more realistic like user behavior.
this.node(page_id, id).mouseEvent('click')
if !target.containsSelection()
target.mouseEvent('click')
for sequence in keys
key = if sequence.key? then @page.native.event.key[sequence.key] else sequence
@page.sendEvent('keypress', key)

View file

@ -365,6 +365,18 @@ PoltergeistAgent.Node = (function() {
return this.element.disabled || this.element.tagName === 'OPTION' && this.element.parentNode.disabled;
};
Node.prototype.containsSelection = function() {
var selectedNode;
selectedNode = document.getSelection().focusNode;
if (!selectedNode) {
return false;
}
if (selectedNode.nodeType === 3) {
selectedNode = selectedNode.parentNode;
}
return this.element.contains(selectedNode);
};
Node.prototype.frameOffset = function() {
var offset, rect, style, win;
win = window;

View file

@ -351,8 +351,11 @@ Poltergeist.Browser = (function() {
};
Browser.prototype.send_keys = function(page_id, id, keys) {
var key, sequence, _i, _len;
this.node(page_id, id).mouseEvent('click');
var key, sequence, target, _i, _len;
target = this.node(page_id, id);
if (!target.containsSelection()) {
target.mouseEvent('click');
}
for (_i = 0, _len = keys.length; _i < _len; _i++) {
sequence = keys[_i];
key = sequence.key != null ? this.page["native"].event.key[sequence.key] : sequence;

View file

@ -3,7 +3,7 @@ var __slice = [].slice;
Poltergeist.Node = (function() {
var name, _fn, _i, _len, _ref;
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes', 'isVisible', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText'];
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes', 'isVisible', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText', 'containsSelection'];
function Node(page, id) {
this.page = page;

View file

@ -4,7 +4,7 @@ class Poltergeist.Node
@DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete',
'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes',
'isVisible', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest',
'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText']
'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText', 'containsSelection']
constructor: (@page, @id) ->

View file

@ -779,6 +779,16 @@ module Capybara::Poltergeist
expect(input.text).to eq('Input')
end
it 'persists focus across calls' do
input = @session.find(:css, '#empty_div')
input.native.send_keys('helo')
input.native.send_keys(:Left)
input.native.send_keys('l')
expect(input.text).to eq('hello')
end
it 'sends keys to filled contenteditable div' do
input = @session.find(:css, '#filled_div')