Control JavaScript confirmation dialogs from Ruby

Adds #alert_messages, #confirm_messages, #prompt_messages,
 #accept_js_confirms!, #dismiss_js_confirms, #accept_js_prompts!,
 #dismiss_js_prompts, and #js_prompt_input= .
This commit is contained in:
Dan Ivovich 2012-07-10 15:47:17 +02:00 committed by Joe Ferris
parent a4eb401da4
commit e677cb9c8e
23 changed files with 540 additions and 6 deletions

View File

@ -77,6 +77,20 @@ capybara-webkit supports a few methods that are not part of the standard capybar
page.driver.error_messages
=> {:source=>"http://example.com", :line_number=>1, :message=>"SyntaxError: Parse error"}
**alert_messages, confirm_messages, prompt_messages**: returns arrays of Javascript dialog messages for each dialog type
# In Javascript:
alert("HI");
confirm("Ok?");
prompt("Number?", "42");
# In Ruby:
page.driver.alert_messages
=> ["Hi"]
page.driver.confirm_messages
=> ["Ok?"]
page.driver.prompt_messages
=> ["Number?"]
**resize_window**: change the viewport size to the given width and height
page.driver.resize_window(500, 300)
@ -92,6 +106,69 @@ capybara-webkit supports a few methods that are not part of the standard capybar
page.driver.cookies["alpha"]
=> "abc"
**accept_js_confirms!**: accept any Javascript confirm that is triggered by the page's Javascript
# In Javascript:
if (confirm("Ok?"))
console.log("Hi");
else
console.log("Bye");
# In Ruby:
page.driver.accept_js_confirms!
visit "/"
page.driver.console_messages.first[:message]
=> "Hi"
**dismiss_js_confirms!**: dismiss any Javascript confirm that is triggered by the page's Javascript
# In Javascript:
if (confirm("Ok?"))
console.log("Hi");
else
console.log("Bye");
# In Ruby:
page.driver.dismiss_js_confirms!
visit "/"
page.driver.console_messages.first[:message]
=> "Bye"
**accept_js_prompts!**: accept any Javascript prompt that is triggered by the page's Javascript
# In Javascript:
var a = prompt("Number?", "0")
console.log(a);
# In Ruby:
page.driver.accept_js_prompts!
visit "/"
page.driver.console_messages.first[:message]
=> "0"
**dismiss_js_prompts!**: dismiss any Javascript prompt that is triggered by the page's Javascript
# In Javascript:
var a = prompt("Number?", "0")
if (a != null)
console.log(a);
else
console.log("you said no"));
# In Ruby:
page.driver.dismiss_js_prompts!
visit "/"
page.driver.console_messages.first[:message]
=> "you said no"
**js_prompt_input=(value)**: set the text to use if a Javascript prompt is encountered and accepted
# In Javascript:
var a = prompt("Number?", "0")
console.log(a);
# In Ruby:
page.driver.js_prompt_input = "42"
page.driver.accept_js_prompts!
visit "/"
page.driver.console_messages.first[:message]
=> "42"
Contributing
------------

View File

@ -55,6 +55,18 @@ module Capybara::Webkit
end
end
def alert_messages
command("JavascriptAlertMessages").split("\n")
end
def confirm_messages
command("JavascriptConfirmMessages").split("\n")
end
def prompt_messages
command("JavascriptPromptMessages").split("\n")
end
def response_headers
Hash[command("Headers").split("\n").map { |header| header.split(": ") }]
end
@ -105,6 +117,30 @@ module Capybara::Webkit
alias_method :window_handle, :get_window_handle
def accept_js_confirms
command("SetConfirmAction", "Yes")
end
def reject_js_confirms
command("SetConfirmAction", "No")
end
def accept_js_prompts
command("SetPromptAction", "Yes")
end
def reject_js_prompts
command("SetPromptAction", "No")
end
def set_prompt_text_to(string)
command("SetPromptText", string)
end
def clear_prompt_text
command("ClearPromptText")
end
def command(name, *args)
@connection.puts name
@connection.puts args.size

View File

@ -68,6 +68,18 @@ module Capybara::Webkit
browser.error_messages
end
def alert_messages
browser.alert_messages
end
def confirm_messages
browser.confirm_messages
end
def prompt_messages
browser.prompt_messages
end
def response_headers
browser.response_headers
end
@ -107,6 +119,30 @@ module Capybara::Webkit
browser.get_window_handle
end
def accept_js_confirms!
browser.accept_js_confirms
end
def dismiss_js_confirms!
browser.reject_js_confirms
end
def accept_js_prompts!
browser.accept_js_prompts
end
def dismiss_js_prompts!
browser.reject_js_prompts
end
def js_prompt_input=(value)
if value.nil?
browser.clear_prompt_text
else
browser.set_prompt_text_to(value)
end
end
def wait?
true
end

View File

@ -403,6 +403,180 @@ describe Capybara::Webkit::Driver do
driver.error_messages.length.should eq 1
end
it "empties the array when reset" do
driver.reset!
driver.console_messages.should be_empty
end
end
context "javascript dialog interaction" do
context "on an alert app" do
let(:driver) do
driver_for_html(<<-HTML)
<html>
<head>
</head>
<body>
<script type="text/javascript">
alert("Alert Text Goes Here");
</script>
</body>
</html>
HTML
end
before { driver.visit("/") }
it "should let me read my alert messages" do
driver.alert_messages.first.should == "Alert Text Goes Here"
end
it "empties the array when reset" do
driver.reset!
driver.alert_messages.should be_empty
end
end
context "on a confirm app" do
let(:driver) do
driver_for_html(<<-HTML)
<html>
<head>
</head>
<body>
<script type="text/javascript">
function test_dialog() {
if(confirm("Yes?"))
console.log("hello");
else
console.log("goodbye");
}
</script>
<input type="button" onclick="test_dialog()" name="test"/>
</body>
</html>
HTML
end
before { driver.visit("/") }
it "should default to accept the confirm" do
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello"
end
it "can dismiss the confirm" do
driver.dismiss_js_confirms!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "goodbye"
end
it "can accept the confirm explicitly" do
driver.dismiss_js_confirms!
driver.accept_js_confirms!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello"
end
it "should collect the javsacript confirm dialog contents" do
driver.find("//input").first.click
driver.confirm_messages.first.should == "Yes?"
end
it "empties the array when reset" do
driver.find("//input").first.click
driver.reset!
driver.confirm_messages.should be_empty
end
it "resets to the default of accepting confirms" do
driver.dismiss_js_confirms!
driver.reset!
driver.visit("/")
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello"
end
end
context "on a prompt app" do
let(:driver) do
driver_for_html(<<-HTML)
<html>
<head>
</head>
<body>
<script type="text/javascript">
function test_dialog() {
var response = prompt("Your name?", "John Smith");
if(response != null)
console.log("hello " + response);
else
console.log("goodbye");
}
</script>
<input type="button" onclick="test_dialog()" name="test"/>
</body>
</html>
HTML
end
before { driver.visit("/") }
it "should default to dismiss the prompt" do
driver.find("//input").first.click
driver.console_messages.first[:message].should == "goodbye"
end
it "can accept the prompt without providing text" do
driver.accept_js_prompts!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello John Smith"
end
it "can accept the prompt with input" do
driver.js_prompt_input = "Capy"
driver.accept_js_prompts!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello Capy"
end
it "can return to dismiss the prompt after accepting prompts" do
driver.accept_js_prompts!
driver.dismiss_js_prompts!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "goodbye"
end
it "should let me remove the prompt input text" do
driver.js_prompt_input = "Capy"
driver.accept_js_prompts!
driver.find("//input").first.click
driver.console_messages.first[:message].should == "hello Capy"
driver.js_prompt_input = nil
driver.find("//input").first.click
driver.console_messages.last[:message].should == "hello John Smith"
end
it "should collect the javsacript prompt dialog contents" do
driver.find("//input").first.click
driver.prompt_messages.first.should == "Your name?"
end
it "empties the array when reset" do
driver.find("//input").first.click
driver.reset!
driver.prompt_messages.should be_empty
end
it "returns the prompt action to dismiss on reset" do
driver.accept_js_prompts!
driver.reset!
driver.visit("/")
driver.find("//input").first.click
driver.console_messages.first[:message].should == "goodbye"
end
end
end
context "form app" do

11
src/ClearPromptText.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "ClearPromptText.h"
#include "WebPage.h"
#include "WebPageManager.h"
ClearPromptText::ClearPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void ClearPromptText::start()
{
page()->setPromptText(QString());
emit finished(new Response(true));
}

9
src/ClearPromptText.h Normal file
View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class ClearPromptText : public SocketCommand {
Q_OBJECT;
public:
ClearPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

View File

@ -31,6 +31,13 @@
#include "WebPageManager.h"
#include "Authenticate.h"
#include "EnableLogging.h"
#include "SetConfirmAction.h"
#include "SetPromptAction.h"
#include "SetPromptText.h"
#include "ClearPromptText.h"
#include "JavascriptAlertMessages.h"
#include "JavascriptConfirmMessages.h"
#include "JavascriptPromptMessages.h"
CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
m_manager = manager;

View File

@ -0,0 +1,10 @@
#include "JavascriptAlertMessages.h"
#include "WebPage.h"
#include "WebPageManager.h"
JavascriptAlertMessages::JavascriptAlertMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void JavascriptAlertMessages::start()
{
emit finished(new Response(true, page()->alertMessages()));
}

View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class JavascriptAlertMessages : public SocketCommand {
Q_OBJECT
public:
JavascriptAlertMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

View File

@ -0,0 +1,10 @@
#include "JavascriptConfirmMessages.h"
#include "WebPage.h"
#include "WebPageManager.h"
JavascriptConfirmMessages::JavascriptConfirmMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void JavascriptConfirmMessages::start()
{
emit finished(new Response(true, page()->confirmMessages()));
}

View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class JavascriptConfirmMessages : public SocketCommand {
Q_OBJECT
public:
JavascriptConfirmMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

View File

@ -0,0 +1,10 @@
#include "JavascriptPromptMessages.h"
#include "WebPage.h"
#include "WebPageManager.h"
JavascriptPromptMessages::JavascriptPromptMessages(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void JavascriptPromptMessages::start()
{
emit finished(new Response(true, page()->promptMessages()));
}

View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class JavascriptPromptMessages : public SocketCommand {
Q_OBJECT
public:
JavascriptPromptMessages(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

11
src/SetConfirmAction.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "SetConfirmAction.h"
#include "WebPage.h"
#include "WebPageManager.h"
SetConfirmAction::SetConfirmAction(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void SetConfirmAction::start()
{
page()->setConfirmAction(arguments()[0]);
emit finished(new Response(true));
}

9
src/SetConfirmAction.h Normal file
View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class SetConfirmAction : public SocketCommand {
Q_OBJECT;
public:
SetConfirmAction(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

11
src/SetPromptAction.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "SetPromptAction.h"
#include "WebPage.h"
#include "WebPageManager.h"
SetPromptAction::SetPromptAction(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void SetPromptAction::start()
{
page()->setPromptAction(arguments()[0]);
emit finished(new Response(true));
}

9
src/SetPromptAction.h Normal file
View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class SetPromptAction : public SocketCommand {
Q_OBJECT;
public:
SetPromptAction(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

11
src/SetPromptText.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "SetPromptText.h"
#include "WebPage.h"
#include "WebPageManager.h"
SetPromptText::SetPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {}
void SetPromptText::start()
{
page()->setPromptText(arguments()[0]);
emit finished(new Response(true));
}

9
src/SetPromptText.h Normal file
View File

@ -0,0 +1,9 @@
#include "SocketCommand.h"
class SetPromptText : public SocketCommand {
Q_OBJECT;
public:
SetPromptText(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

View File

@ -20,6 +20,9 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
loadJavascript();
setUserStylesheet();
m_confirm = true;
m_prompt = false;
m_prompt_text = QString();
this->setCustomNetworkAccessManager();
connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
@ -99,6 +102,18 @@ QString WebPage::consoleMessages() {
return m_consoleMessages.join("\n");
}
QString WebPage::alertMessages() {
return m_alertMessages.join("\n");
}
QString WebPage::confirmMessages() {
return m_confirmMessages.join("\n");
}
QString WebPage::promptMessages() {
return m_promptMessages.join("\n");
}
void WebPage::setUserAgent(QString userAgent) {
m_userAgent = userAgent;
}
@ -140,21 +155,27 @@ void WebPage::javaScriptConsoleMessage(const QString &message, int lineNumber, c
void WebPage::javaScriptAlert(QWebFrame *frame, const QString &message) {
Q_UNUSED(frame);
m_alertMessages.append(message);
std::cout << "ALERT: " << qPrintable(message) << std::endl;
}
bool WebPage::javaScriptConfirm(QWebFrame *frame, const QString &message) {
Q_UNUSED(frame);
Q_UNUSED(message);
return true;
m_confirmMessages.append(message);
return m_confirm;
}
bool WebPage::javaScriptPrompt(QWebFrame *frame, const QString &message, const QString &defaultValue, QString *result) {
Q_UNUSED(frame)
Q_UNUSED(message)
Q_UNUSED(defaultValue)
Q_UNUSED(result)
return false;
m_promptMessages.append(message);
if (m_prompt) {
if (m_prompt_text.isNull()) {
*result = defaultValue;
} else {
*result = m_prompt_text;
}
}
return m_prompt;
}
void WebPage::loadStarted() {
@ -297,3 +318,16 @@ bool WebPage::matchesWindowSelector(QString selector) {
void WebPage::setFocus() {
m_manager->setCurrentPage(this);
}
void WebPage::setConfirmAction(QString action) {
m_confirm = (action == "Yes");
}
void WebPage::setPromptAction(QString action) {
m_prompt = (action == "Yes");
}
void WebPage::setPromptText(QString text) {
m_prompt_text = text;
}

View File

@ -14,12 +14,18 @@ class WebPage : public QWebPage {
QString failureString();
QString userAgentForUrl(const QUrl &url ) const;
void setUserAgent(QString userAgent);
void setConfirmAction(QString action);
void setPromptAction(QString action);
void setPromptText(QString action);
int getLastStatus();
void setCustomNetworkAccessManager();
bool render(const QString &fileName);
virtual bool extension (Extension extension, const ExtensionOption *option=0, ExtensionReturn *output=0);
void setSkipImageLoading(bool skip);
QString consoleMessages();
QString alertMessages();
QString confirmMessages();
QString promptMessages();
void resetWindowSize();
QWebPage *createWindow(WebWindowType type);
QString uuid();
@ -63,7 +69,13 @@ class WebPage : public QWebPage {
void setUserStylesheet();
int m_lastStatus;
QString m_pageHeaders;
bool m_confirm;
bool m_prompt;
QStringList m_consoleMessages;
QStringList m_alertMessages;
QStringList m_confirmMessages;
QString m_prompt_text;
QStringList m_promptMessages;
QString m_uuid;
WebPageManager *m_manager;
QString m_errorPageMessage;

View File

@ -32,3 +32,10 @@ CHECK_COMMAND(GetWindowHandles)
CHECK_COMMAND(GetWindowHandle)
CHECK_COMMAND(Authenticate)
CHECK_COMMAND(EnableLogging)
CHECK_COMMAND(SetConfirmAction)
CHECK_COMMAND(SetPromptAction)
CHECK_COMMAND(SetPromptText)
CHECK_COMMAND(ClearPromptText)
CHECK_COMMAND(JavascriptAlertMessages)
CHECK_COMMAND(JavascriptConfirmMessages)
CHECK_COMMAND(JavascriptPromptMessages)

View File

@ -4,6 +4,13 @@ DESTDIR = .
HEADERS = \
EnableLogging.h \
Authenticate.h \
SetConfirmAction.h \
SetPromptAction.h \
SetPromptText.h \
ClearPromptText.h \
JavascriptAlertMessages.h \
JavascriptConfirmMessages.h \
JavascriptPromptMessages.h \
IgnoreSslErrors.h \
ResizeWindow.h \
CurrentUrl.h \
@ -50,6 +57,13 @@ HEADERS = \
SOURCES = \
EnableLogging.cpp \
Authenticate.cpp \
SetConfirmAction.cpp \
SetPromptAction.cpp \
SetPromptText.cpp \
ClearPromptText.cpp \
JavascriptAlertMessages.cpp \
JavascriptConfirmMessages.cpp \
JavascriptPromptMessages.cpp \
IgnoreSslErrors.cpp \
ResizeWindow.cpp \
CurrentUrl.cpp \