Click elements with native events

This commit is contained in:
Matthew Horan 2012-12-10 22:53:03 -05:00
parent c47f0a12b3
commit 30c79fcfcb
5 changed files with 165 additions and 21 deletions

View File

@ -228,4 +228,134 @@ describe Capybara::Session do
subject.should have_content('admin')
end
end
context "iframe app" do
before(:all) do
@app = Class.new(ExampleApp) do
get '/' do
<<-HTML
<!DOCTYPE html>
<html>
<body>
<h1>Main Frame</h1>
<iframe src="/a" name="a_frame" width="500" height="500"></iframe>
</body>
</html>
HTML
end
get '/a' do
<<-HTML
<!DOCTYPE html>
<html>
<body>
<h1>Page A</h1>
<iframe src="/b" name="b_frame" width="500" height="500"></iframe>
</body>
</html>
HTML
end
get '/b' do
<<-HTML
<!DOCTYPE html>
<html>
<body>
<h1>Page B</h1>
<form action="/c" method="post">
<input id="button" name="commit" type="submit" value="B Button">
</form>
</body>
</html>
HTML
end
post '/c' do
<<-HTML
<!DOCTYPE html>
<html>
<body>
<h1>Page C</h1>
</body>
</html>
HTML
end
end
end
it 'supports clicking an element offset from the viewport origin' do
subject.visit '/'
subject.within_frame 'a_frame' do
subject.within_frame 'b_frame' do
subject.click_button 'B Button'
subject.should have_content('Page C')
end
end
end
end
context 'click tests' do
before(:all) do
@app = Class.new(ExampleApp) do
get '/' do
<<-HTML
<!DOCTYPE html>
<html>
<head>
<style>
body {
width: 800px;
margin: 0;
}
.target {
width: 200px;
height: 200px;
float: left;
margin: 100px;
}
</style>
<body>
<div id="one" class="target"></div>
<div id="two" class="target"></div>
<script type="text/javascript">
var targets = document.getElementsByClassName('target');
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
target.onclick = function(event) {
this.setAttribute('data-click-x', event.clientX);
this.setAttribute('data-click-y', event.clientY);
};
}
</script>
</body>
</html>
HTML
end
end
end
it 'clicks in the center of an element' do
subject.visit('/')
subject.find(:css, '#one').click
subject.find(:css, '#one')['data-click-x'].should == '199'
subject.find(:css, '#one')['data-click-y'].should == '199'
end
it 'clicks in the center of the viewable area of an element' do
subject.visit('/')
subject.driver.resize_window(200, 200)
subject.find(:css, '#one').click
subject.find(:css, '#one')['data-click-x'].should == '149'
subject.find(:css, '#one')['data-click-y'].should == '99'
end
it 'scrolls an element into view when clicked' do
subject.visit('/')
subject.driver.resize_window(200, 200)
subject.find(:css, '#two').click
subject.find(:css, '#two')['data-click-x'].should_not be_nil
subject.find(:css, '#two')['data-click-y'].should_not be_nil
end
end
end

View File

@ -1,8 +1,11 @@
#include "JavascriptInvocation.h"
#include "WebPage.h"
#include <QApplication>
JavascriptInvocation::JavascriptInvocation(const QString &functionName, const QStringList &arguments, QObject *parent) : QObject(parent) {
JavascriptInvocation::JavascriptInvocation(const QString &functionName, const QStringList &arguments, WebPage *page, QObject *parent) : QObject(parent) {
m_functionName = functionName;
m_arguments = arguments;
m_page = page;
}
QString &JavascriptInvocation::functionName() {
@ -12,3 +15,23 @@ QString &JavascriptInvocation::functionName() {
QStringList &JavascriptInvocation::arguments() {
return m_arguments;
}
void JavascriptInvocation::click(const QWebElement &element, int left, int top, int width, int height) {
QRect elementBox(left, top, width, height);
QWebFrame *parent = element.webFrame();
while (parent) {
elementBox = elementBox.translated(parent->geometry().topLeft());
parent = parent->parentFrame();
}
QRect viewport(QPoint(0, 0), m_page->viewportSize());
QPoint mousePos = elementBox.intersected(viewport).center();
QMouseEvent event(QEvent::MouseMove, mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QApplication::sendEvent(m_page, &event);
event = QMouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(m_page, &event);
event = QMouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(m_page, &event);
}

View File

@ -1,6 +1,9 @@
#include <QObject>
#include <QString>
#include <QStringList>
#include <QWebElement>
class WebPage;
class JavascriptInvocation : public QObject {
Q_OBJECT
@ -8,12 +11,14 @@ class JavascriptInvocation : public QObject {
Q_PROPERTY(QStringList arguments READ arguments)
public:
JavascriptInvocation(const QString &functionName, const QStringList &arguments, QObject *parent = 0);
JavascriptInvocation(const QString &functionName, const QStringList &arguments, WebPage *page, QObject *parent = 0);
QString &functionName();
QStringList &arguments();
Q_INVOKABLE void click(const QWebElement &element, int left, int top, int width, int height);
private:
QString m_functionName;
QStringList m_arguments;
WebPage *m_page;
};

View File

@ -120,7 +120,7 @@ bool WebPage::shouldInterruptJavaScript() {
QVariant WebPage::invokeCapybaraFunction(const char *name, const QStringList &arguments) {
QString qname(name);
QString objectName("CapybaraInvocation");
JavascriptInvocation invocation(qname, arguments);
JavascriptInvocation invocation(qname, arguments, this);
currentFrame()->addToJavaScriptWindowObject(objectName, &invocation);
QString javascript = QString("Capybara.invoke()");
return currentFrame()->evaluateJavaScript(javascript);

View File

@ -108,25 +108,11 @@ Capybara = {
return this.nodes[index].submit();
},
mousedown: function(index) {
var mousedownEvent = document.createEvent('MouseEvents');
mousedownEvent.initMouseEvent('mousedown', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
this.nodes[index].dispatchEvent(mousedownEvent);
},
mouseup: function(index) {
var mouseupEvent = document.createEvent('MouseEvents');
mouseupEvent.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
this.nodes[index].dispatchEvent(mouseupEvent);
},
click: function (index) {
this.mousedown(index);
this.focus(index);
this.mouseup(index);
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
this.nodes[index].dispatchEvent(clickEvent);
var node = this.nodes[index];
node.scrollIntoViewIfNeeded();
var rect = node.getClientRects()[0];
CapybaraInvocation.click(node, rect.left, rect.top, rect.width, rect.height);
},
trigger: function (index, eventName) {