From 9f2c71c5d2ed50aaf8f1473b9261cf2dc70cb3f0 Mon Sep 17 00:00:00 2001 From: Joe Ferris and Matt Horan Date: Sat, 9 Nov 2013 15:12:16 -0500 Subject: [PATCH] Render a mouse pointer when saving screenshots * Helpful for ClickFailed errors --- src/JavascriptInvocation.cpp | 25 ++++++++----------------- src/JavascriptInvocation.h | 1 - src/WebPage.cpp | 18 ++++++++++++++++++ src/WebPage.h | 3 +++ src/capybara.js | 1 + src/pointer.png | Bin 0 -> 3573 bytes src/webkit_server.qrc | 1 + 7 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 src/pointer.png diff --git a/src/JavascriptInvocation.cpp b/src/JavascriptInvocation.cpp index 432365c..49003c5 100644 --- a/src/JavascriptInvocation.cpp +++ b/src/JavascriptInvocation.cpp @@ -39,40 +39,31 @@ InvocationResult JavascriptInvocation::invoke(QWebFrame *frame) { void JavascriptInvocation::leftClick(int x, int y) { QPoint mousePos(x, y); - hover(mousePos); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton); + m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::LeftButton); + m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton); } void JavascriptInvocation::rightClick(int x, int y) { QPoint mousePos(x, y); - hover(mousePos); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::RightButton); + m_page->mouseEvent(QEvent::MouseButtonPress, mousePos, Qt::RightButton); // swallowContextMenuEvent tries to fire contextmenu event in html page QContextMenuEvent *event = new QContextMenuEvent(QContextMenuEvent::Mouse, mousePos); m_page->swallowContextMenuEvent(event); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::RightButton); + m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::RightButton); } void JavascriptInvocation::doubleClick(int x, int y) { QPoint mousePos(x, y); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonDblClick, mousePos, Qt::LeftButton); - JavascriptInvocation::mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton); -} - -void JavascriptInvocation::mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button) { - QMouseEvent event(type, position, button, button, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); + m_page->mouseEvent(QEvent::MouseButtonDblClick, mousePos, Qt::LeftButton); + m_page->mouseEvent(QEvent::MouseButtonRelease, mousePos, Qt::LeftButton); } bool JavascriptInvocation::clickTest(QWebElement element, int absoluteX, int absoluteY) { - QPoint mousePos(absoluteX, absoluteY); - QWebHitTestResult res = m_page->mainFrame()->hitTestContent(mousePos); - return res.frame() == element.webFrame(); + return m_page->clickTest(element, absoluteX, absoluteY); } QVariantMap JavascriptInvocation::clickPosition(QWebElement element, int left, int top, int width, int height) { @@ -106,7 +97,7 @@ void JavascriptInvocation::hover(int absoluteX, int absoluteY) { } void JavascriptInvocation::hover(const QPoint &mousePos) { - mouseEvent(QEvent::MouseMove, mousePos, Qt::NoButton); + m_page->mouseEvent(QEvent::MouseMove, mousePos, Qt::NoButton); } int JavascriptInvocation::keyCodeFor(const QChar &key) { diff --git a/src/JavascriptInvocation.h b/src/JavascriptInvocation.h index 970c1b9..fd50747 100644 --- a/src/JavascriptInvocation.h +++ b/src/JavascriptInvocation.h @@ -34,7 +34,6 @@ class JavascriptInvocation : public QObject { QStringList m_arguments; WebPage *m_page; QVariant m_error; - void mouseEvent(QEvent::Type type, const QPoint & position, Qt::MouseButton button); void hover(const QPoint &); int keyCodeFor(const QChar &); }; diff --git a/src/WebPage.cpp b/src/WebPage.cpp index 64afb13..cf19278 100644 --- a/src/WebPage.cpp +++ b/src/WebPage.cpp @@ -10,6 +10,7 @@ #include #include #include +#include WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) { m_loading = false; @@ -214,6 +215,19 @@ QString WebPage::failureString() { return message + m_errorPageMessage; } +void WebPage::mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button) { + m_mousePosition = position; + QMouseEvent event(type, position, button, button, Qt::NoModifier); + QApplication::sendEvent(this, &event); +} + +bool WebPage::clickTest(QWebElement element, int absoluteX, int absoluteY) { + QPoint mousePos(absoluteX, absoluteY); + m_mousePosition = mousePos; + QWebHitTestResult res = mainFrame()->hitTestContent(mousePos); + return res.frame() == element.webFrame(); +} + bool WebPage::render(const QString &fileName, const QSize &minimumSize) { QFileInfo fileInfo(fileName); QDir dir; @@ -236,6 +250,10 @@ bool WebPage::render(const QString &fileName, const QSize &minimumSize) { this->setViewportSize(pageSize); this->mainFrame()->render(&p); + + QImage pointer = QImage(":/pointer.png"); + p.drawImage(m_mousePosition, pointer); + p.end(); this->setViewportSize(viewportSize); diff --git a/src/WebPage.h b/src/WebPage.h index d39f15a..db8e316 100644 --- a/src/WebPage.h +++ b/src/WebPage.h @@ -44,6 +44,8 @@ class WebPage : public QWebPage { QStringList pageHeaders(); QByteArray body(); QString contentType(); + void mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button); + bool clickTest(QWebElement element, int absoluteX, int absoluteY); public slots: bool shouldInterruptJavaScript(); @@ -88,6 +90,7 @@ class WebPage : public QWebPage { WebPageManager *m_manager; QString m_errorPageMessage; void setFrameProperties(QWebFrame *, QUrl &, NetworkReplyProxy *); + QPoint m_mousePosition; }; #endif //_WEBPAGE_H diff --git a/src/capybara.js b/src/capybara.js index 1a9b71c..55304c8 100644 --- a/src/capybara.js +++ b/src/capybara.js @@ -189,6 +189,7 @@ Capybara = { var node = this.nodes[index]; node.scrollIntoViewIfNeeded(); var pos = this.clickPosition(node); + CapybaraInvocation.hover(pos.relativeX, pos.relativeY); this.expectNodeAtPosition(node, pos); action(pos.absoluteX, pos.absoluteY); }, diff --git a/src/pointer.png b/src/pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..36d16d36553a5940189c1990e658285b3847f7c0 GIT binary patch literal 3573 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 literal 0 HcmV?d00001 diff --git a/src/webkit_server.qrc b/src/webkit_server.qrc index 88efd00..79eca4c 100644 --- a/src/webkit_server.qrc +++ b/src/webkit_server.qrc @@ -1,5 +1,6 @@ capybara.js + pointer.png