From 30c79fcfcb0486c5ee2641abc343ccd76f97ecfa Mon Sep 17 00:00:00 2001 From: Matthew Horan Date: Mon, 10 Dec 2012 22:53:03 -0500 Subject: [PATCH] Click elements with native events --- spec/integration/session_spec.rb | 130 +++++++++++++++++++++++++++++++ src/JavascriptInvocation.cpp | 25 +++++- src/JavascriptInvocation.h | 7 +- src/WebPage.cpp | 2 +- src/capybara.js | 22 +----- 5 files changed, 165 insertions(+), 21 deletions(-) diff --git a/spec/integration/session_spec.rb b/spec/integration/session_spec.rb index 05451dd..684fa8f 100644 --- a/spec/integration/session_spec.rb +++ b/spec/integration/session_spec.rb @@ -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 + + + +

Main Frame

+ + + + HTML + end + + get '/a' do + <<-HTML + + + +

Page A

+ + + + HTML + end + + get '/b' do + <<-HTML + + + +

Page B

+
+ +
+ + + HTML + end + + post '/c' do + <<-HTML + + + +

Page C

+ + + 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 + + + + + +
+
+ + + + 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 diff --git a/src/JavascriptInvocation.cpp b/src/JavascriptInvocation.cpp index 56446ff..41f5900 100644 --- a/src/JavascriptInvocation.cpp +++ b/src/JavascriptInvocation.cpp @@ -1,8 +1,11 @@ #include "JavascriptInvocation.h" +#include "WebPage.h" +#include -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); +} diff --git a/src/JavascriptInvocation.h b/src/JavascriptInvocation.h index b3722bd..08357fa 100644 --- a/src/JavascriptInvocation.h +++ b/src/JavascriptInvocation.h @@ -1,6 +1,9 @@ #include #include #include +#include + +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; }; diff --git a/src/WebPage.cpp b/src/WebPage.cpp index 54babdd..f9af911 100644 --- a/src/WebPage.cpp +++ b/src/WebPage.cpp @@ -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); diff --git a/src/capybara.js b/src/capybara.js index e39105f..32ca93b 100644 --- a/src/capybara.js +++ b/src/capybara.js @@ -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) {