Update for Capybara 2.3.0

This adds support for the full Capybara 2.3.0 API. There are two known
incompatibilities:

* Selenium supports outerWidth and outerHeight, which we cannot, because we
  dont' have an actual OS window.
* Selenium raises errors after interacting with a closed window. We focus the
  next available window after closing.

This commit adds the following:

* Implement Driver#close_window
* Implement Driver#current_window_handle
* Implement Driver#maximize_window
* Implement Driver#open_new_window
* Implement Driver#no_such_window_error
* Implement Driver#resize_window_to
* Implement Driver#switch_to_window
* Implement Driver#window_size
* Implement Driver#go_back
* Implement Driver#go_forward
* Support change events when clearing a text input
* Support setting contentEditable elements
* Support window.close() in JavaScript
* Don't return text from hidden elements
* Skip Capybara specs which use outerWidth, outerHeight
* Don't use Qt object ownership to manage windows
This commit is contained in:
Joe Ferris 2014-06-18 18:00:34 -04:00 committed by Matthew Horan
parent 729c2cf364
commit e0172bfcd9
44 changed files with 567 additions and 113 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ webkit_server.pro.user
.ruby-version
.ruby-gemset
.idea
.qmake.stash

View File

@ -2,16 +2,17 @@ PATH
remote: .
specs:
capybara-webkit (1.1.1)
capybara (>= 2.0.2, < 2.2.0)
capybara (>= 2.0.2, < 2.4.0)
json
GEM
remote: https://rubygems.org/
specs:
addressable (2.3.6)
appraisal (0.4.0)
bundler
rake
capybara (2.1.0)
capybara (2.3.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@ -24,18 +25,15 @@ GEM
ffi (1.9.3-java)
ffi (1.9.3-x86-mingw32)
json (1.8.1)
json (1.8.1-java)
mime-types (2.0)
launchy (2.4.2)
addressable (~> 2.3)
mime-types (2.3)
mini_magick (3.2.1)
subexec (~> 0.0.4)
mini_portile (0.5.2)
mini_portile (0.6.0)
multi_json (1.8.4)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
nokogiri (1.6.0-java)
mini_portile (~> 0.5.0)
nokogiri (1.6.0-x86-mingw32)
mini_portile (~> 0.5.0)
nokogiri (1.6.2.1)
mini_portile (= 0.6.0)
rack (1.5.2)
rack-protection (1.3.2)
rack
@ -74,6 +72,7 @@ PLATFORMS
DEPENDENCIES
appraisal (~> 0.4.0)
capybara-webkit!
launchy
mini_magick
rake
rspec (~> 2.14.0)

View File

@ -19,7 +19,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 1.9.0"
s.add_runtime_dependency("capybara", ">= 2.0.2", "< 2.2.0")
s.add_runtime_dependency("capybara", ">= 2.0.2", "< 2.4.0")
s.add_runtime_dependency("json")
s.add_development_dependency("rspec", "~> 2.14.0")
@ -29,5 +29,6 @@ Gem::Specification.new do |s|
s.add_development_dependency("rake")
s.add_development_dependency("appraisal", "~> 0.4.0")
s.add_development_dependency("selenium-webdriver")
s.add_development_dependency("launchy")
end

View File

@ -102,6 +102,26 @@ module Capybara::Webkit
command("WindowFocus", selector)
end
def window_open
command("WindowOpen")
end
def window_close(selector)
command("WindowClose", selector)
end
def window_resize(handle, width, height)
command("WindowResize", handle, width.to_i, height.to_i)
end
def window_size(handle)
JSON.parse(command("WindowSize", handle))
end
def window_maximize(handle)
command("WindowMaximize", handle)
end
def get_window_handles
JSON.parse(command('GetWindowHandles'))
end
@ -195,14 +215,18 @@ module Capybara::Webkit
command("SetProxy")
end
def resize_window(width, height)
command("ResizeWindow", width.to_i, height.to_i)
end
def version
command("Version")
end
def go_back
command("GoBack")
end
def go_forward
command("GoForward")
end
private
def check

View File

@ -8,7 +8,7 @@ module Capybara::Webkit
SERVER_PATH = File.expand_path("../../../../bin/webkit_server", __FILE__)
WEBKIT_SERVER_START_TIMEOUT = 15
attr_reader :port
attr_reader :port, :pid
def initialize(options = {})
@socket = nil

View File

@ -89,7 +89,15 @@ module Capybara::Webkit
end
def resize_window(width, height)
browser.resize_window(width, height)
resize_window_to(current_window_handle, width, height)
end
def resize_window_to(handle, width, height)
browser.window_resize(handle, width, height)
end
def window_size(handle)
browser.window_size(handle)
end
def within_frame(selector)
@ -102,8 +110,8 @@ module Capybara::Webkit
end
def within_window(selector)
current_window = window_handle
browser.window_focus(selector)
current_window = current_window_handle
switch_to_window(selector)
begin
yield
ensure
@ -111,14 +119,30 @@ module Capybara::Webkit
end
end
def switch_to_window(selector)
browser.window_focus(selector)
end
def window_handles
browser.get_window_handles
end
def window_handle
def current_window_handle
browser.get_window_handle
end
def open_new_window
browser.window_open
end
def close_window(selector)
browser.window_close(selector)
end
def maximize_window(selector)
browser.window_maximize(selector)
end
def accept_js_confirms!
browser.accept_js_confirms
end
@ -143,6 +167,14 @@ module Capybara::Webkit
end
end
def go_back
browser.go_back
end
def go_forward
browser.go_forward
end
def wait?
true
end
@ -174,6 +206,10 @@ module Capybara::Webkit
[Capybara::Webkit::ClickFailed]
end
def no_such_window_error
Capybara::Webkit::NoSuchWindowError
end
def version
[
"Capybara: #{Capybara::VERSION}",

View File

@ -14,6 +14,9 @@ module Capybara::Webkit
class TimeoutError < Timeout::Error
end
class NoSuchWindowError < StandardError
end
class JsonError
def initialize(response)
error = JSON.parse response

View File

@ -45,4 +45,26 @@ describe Capybara::Webkit::Driver, "#resize_window(width, height)" do
driver.visit("#{AppRunner.app_host}/")
driver.html.should include(DEFAULT_DIMENTIONS)
end
it "resizes windows by handle" do
driver.visit("#{AppRunner.app_host}/")
driver.open_new_window
driver.visit("#{AppRunner.app_host}/")
driver.resize_window_to(driver.window_handles.first, 800, 600)
driver.resize_window_to(driver.window_handles.last, 400, 300)
driver.window_size(driver.window_handles.first).should eq [800, 600]
driver.window_size(driver.window_handles.last).should eq [400, 300]
end
it "maximizes a window" do
driver.visit("#{AppRunner.app_host}/")
driver.resize_window(400, 300)
driver.maximize_window(driver.current_window_handle)
width, height = *driver.window_size(driver.current_window_handle)
width.should be > 400
height.should be > 300
end
end

View File

@ -341,6 +341,9 @@ describe Capybara::Webkit::Driver do
<div id="hidden-text">
Some of this text is <em style="display:none">hidden!</em>
</div>
<div id="hidden-ancestor" style="display: none">
<div>Hello</div>
</div>
<input type="text" disabled="disabled"/>
<input id="checktest" type="checkbox" checked="checked"/>
<script type="text/javascript">
@ -353,6 +356,12 @@ describe Capybara::Webkit::Driver do
before { visit("/") }
it "doesn't return text if the ancestor is hidden" do
visit("/")
driver.find_css("#hidden-ancestor div").first.text.should eq ''
end
it "handles anchor tags" do
visit("#test")
driver.find_xpath("//*[contains(., 'hello')]").should_not be_empty
@ -1081,7 +1090,7 @@ describe Capybara::Webkit::Driver do
<input class="watch" type="password"/>
<input class="watch" type="search"/>
<input class="watch" type="tel"/>
<input class="watch" type="text"/>
<input class="watch" type="text" value="original"/>
<input class="watch" type="url"/>
<textarea class="watch"></textarea>
<input class="watch" type="checkbox"/>
@ -1117,7 +1126,7 @@ describe Capybara::Webkit::Driver do
before { visit("/") }
let(:newtext) { 'newvalue' }
let(:newtext) { '12345' }
let(:keyevents) do
(%w{focus} +
@ -1125,13 +1134,22 @@ describe Capybara::Webkit::Driver do
).flatten
end
let(:textevents) { keyevents + %w(change blur) }
%w(email number password search tel text url).each do | field_type |
it "triggers text input events on inputs of type #{field_type}" do
driver.find_xpath("//input[@type='#{field_type}']").first.set(newtext)
driver.find_xpath("//li").map(&:visible_text).should eq keyevents
driver.find_xpath("//body").first.click
driver.find_xpath("//li").map(&:visible_text).should eq textevents
end
end
it "triggers events for cleared inputs" do
driver.find_xpath("//input[@type='text']").first.set('')
driver.find_xpath("//body").first.click
driver.find_xpath("//li").map(&:visible_text).should include('change')
end
it "triggers textarea input events" do
driver.find_xpath("//textarea").first.set(newtext)
driver.find_xpath("//li").map(&:visible_text).should eq keyevents
@ -1925,6 +1943,42 @@ describe Capybara::Webkit::Driver do
end
end
it "can switch to another window" do
visit("/new_window")
driver.switch_to_window(driver.window_handles.last)
driver.find_xpath("//p").first.visible_text.should eq "finished"
end
it "knows the current window handle" do
visit("/new_window")
driver.within_window(driver.window_handles.last) do
driver.current_window_handle.should eq driver.window_handles.last
end
end
it "can close the current window" do
visit("/new_window")
original_handle = driver.current_window_handle
driver.switch_to_window(driver.window_handles.last)
driver.close_window(driver.current_window_handle)
driver.current_window_handle.should eq(original_handle)
end
it "can close an unfocused window" do
visit("/new_window")
driver.close_window(driver.window_handles.last)
driver.window_handles.size.should eq(1)
end
it "can close the last window" do
visit("/new_window")
handles = driver.window_handles
handles.each { |handle| driver.close_window(handle) }
driver.html.should be_empty
handles.should_not include(driver.current_window_handle)
end
it "waits for the new window to load" do
visit("/new_window?sleep=1")
driver.within_window(driver.window_handles.last) do
@ -1969,7 +2023,7 @@ describe Capybara::Webkit::Driver do
it "raises an error if the window is not found" do
expect { driver.within_window('myWindowDoesNotExist') }.
to raise_error(Capybara::Webkit::InvalidResponseError)
to raise_error(Capybara::Webkit::NoSuchWindowError)
end
it "has a number of window handles equal to the number of open windows" do
@ -1978,12 +2032,35 @@ describe Capybara::Webkit::Driver do
driver.window_handles.size.should eq 2
end
it "removes windows when closed via JavaScript" do
visit("/new_window")
driver.execute_script('console.log(window.document.title); window.close()')
sleep 2
driver.window_handles.size.should eq 1
end
it "closes new windows on reset" do
visit("/new_window")
last_handle = driver.window_handles.last
driver.reset!
driver.window_handles.should_not include(last_handle)
end
it "leaves the old window focused when opening a new window" do
visit("/new_window")
current_window = driver.current_window_handle
driver.open_new_window
driver.current_window_handle.should eq current_window
driver.window_handles.size.should eq 3
end
it "opens blank windows" do
visit("/new_window")
driver.open_new_window
driver.switch_to_window(driver.window_handles.last)
driver.html.should be_empty
end
end
it "preserves cookies across windows" do
@ -2386,6 +2463,34 @@ describe Capybara::Webkit::Driver do
end
end
context "history" do
let(:driver) do
driver_for_app do
get "/:param" do |param|
<<-HTML
<html>
<body>
<p>#{param}</p>
<a href="/navigated">Navigate</a>
</body>
</html>
HTML
end
end
end
it "can navigate in history" do
visit("/first")
driver.find_xpath("//p").first.text.should eq('first')
driver.find_xpath("//a").first.click
driver.find_xpath("//p").first.text.should eq('navigated')
driver.go_back
driver.find_xpath("//p").first.text.should eq('first')
driver.go_forward
driver.find_xpath("//p").first.text.should eq('navigated')
end
end
def driver_url(driver, path)
URI.parse(driver.current_url).merge(path).to_s
end

View File

@ -10,6 +10,8 @@ describe Capybara::Webkit, 'compatibility with selenium' do
<label for="one">One</label><input type="text" name="one" id="one" />
<label for="two">Two</label><input type="text" name="two" id="two" />
<label for="three">Three</label><input type="text" name="three" id="three" readonly="readonly" />
<label for="textarea">Textarea</label>
<textarea name="textarea" id="textarea"></textarea>
<input type="submit" value="Submit" id="submit" />
</form>
<script type="text/javascript">
@ -35,6 +37,7 @@ describe Capybara::Webkit, 'compatibility with selenium' do
fill_in "One", :with => "a new value"
fill_in "Two", :with => "other value"
fill_in "Three", :with => "readonly value"
fill_in "Textarea", :with => "last value"
click_button "Submit"
end
end

View File

@ -10,8 +10,8 @@ $LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
require 'capybara/webkit'
connection = Capybara::Webkit::Connection.new(:socket_class => TCPSocket)
$webkit_browser = Capybara::Webkit::Browser.new(connection)
$webkit_connection = Capybara::Webkit::Connection.new(:socket_class => TCPSocket)
$webkit_browser = Capybara::Webkit::Browser.new($webkit_connection)
if ENV['DEBUG']
$webkit_browser.enable_logging
@ -24,9 +24,27 @@ Capybara.register_driver :reusable_webkit do |app|
end
RSpec.configure do |c|
Capybara::SpecHelper.configure(c)
c.filter_run_excluding :skip_on_windows => !(RbConfig::CONFIG['host_os'] =~ /mingw32/).nil?
c.filter_run_excluding :skip_on_jruby => !defined?(::JRUBY_VERSION).nil?
Capybara::SpecHelper.configure(c)
# We can't support outerWidth and outerHeight without a visible window.
# We focus the next window instead of failing when closing windows.
c.filter_run_excluding :full_description =>
/Capybara::Session webkit Capybara::Window #(size|resize_to|maximize|close.*no_such_window_error)/
# Capybara's integration tests expect "capybara/" in the default path
c.around :requires => :screenshot do |example|
old_path = Capybara.save_and_open_page_path
Capybara.save_and_open_page_path = File.join(PROJECT_ROOT, 'tmp', 'capybara')
begin
example.run
ensure
Capybara.save_and_open_page_path = old_path
end
end
end
def with_env_vars(vars)

View File

@ -20,7 +20,7 @@
#include "CurrentUrl.h"
#include "SetTimeout.h"
#include "GetTimeout.h"
#include "ResizeWindow.h"
#include "WindowResize.h"
#include "IgnoreSslErrors.h"
#include "SetSkipImageLoading.h"
#include "WindowFocus.h"
@ -40,6 +40,12 @@
#include "Version.h"
#include "Title.h"
#include "FindCss.h"
#include "WindowClose.h"
#include "WindowOpen.h"
#include "WindowSize.h"
#include "WindowMaximize.h"
#include "GoBack.h"
#include "GoForward.h"
CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
m_manager = manager;

12
src/GoBack.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "GoBack.h"
#include "SocketCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
GoBack::GoBack(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void GoBack::start() {
page()->triggerAction(QWebPage::Back);
finish(true);
}

10
src/GoBack.h Normal file
View File

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

12
src/GoForward.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "GoForward.h"
#include "SocketCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
GoForward::GoForward(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void GoForward::start() {
page()->triggerAction(QWebPage::Forward);
finish(true);
}

10
src/GoForward.h Normal file
View File

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

View File

@ -6,8 +6,6 @@ Reset::Reset(WebPageManager *manager, QStringList &arguments, QObject *parent) :
}
void Reset::start() {
page()->triggerAction(QWebPage::Stop);
manager()->reset();
finish(true);

View File

@ -1,17 +0,0 @@
#include "ResizeWindow.h"
#include "WebPage.h"
#include "WebPageManager.h"
ResizeWindow::ResizeWindow(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void ResizeWindow::start() {
int width = arguments()[0].toInt();
int height = arguments()[1].toInt();
QSize size(width, height);
page()->setViewportSize(size);
finish(true);
}

View File

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

View File

@ -19,3 +19,9 @@ WebPageManager *SocketCommand::manager() const {
return m_manager;
}
QString SocketCommand::toString() const {
QString result;
QTextStream(&result) << metaObject()->className() << QString("(") << m_arguments.join(", ") << QString(")");
return result;
}

View File

@ -14,6 +14,7 @@ class SocketCommand : public Command {
public:
SocketCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
virtual QString toString() const;
protected:
WebPage *page() const;

View File

@ -11,6 +11,8 @@
#include <QWebSettings>
#include <QUuid>
#include <QApplication>
#include <QWebView>
#include <QMainWindow>
WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
m_loading = false;
@ -33,14 +35,23 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
this, SLOT(frameCreated(QWebFrame *)));
connect(this, SIGNAL(unsupportedContent(QNetworkReply*)),
this, SLOT(handleUnsupportedContent(QNetworkReply*)));
resetWindowSize();
connect(this, SIGNAL(windowCloseRequested()), this, SLOT(remove()));
settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
settings()->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
createWindow();
}
void WebPage::resetWindowSize() {
this->setViewportSize(QSize(1680, 1050));
this->settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
void WebPage::createWindow() {
QSize size(1680, 1050);
setViewportSize(size);
}
void WebPage::resize(int width, int height) {
QSize size(width, height);
setViewportSize(size);
}
void WebPage::resetLocalStorage() {
@ -334,7 +345,7 @@ bool WebPage::supportsExtension(Extension extension) const {
QWebPage *WebPage::createWindow(WebWindowType type) {
Q_UNUSED(type);
return m_manager->createPage(this);
return m_manager->createPage();
}
QString WebPage::uuid() {
@ -361,6 +372,10 @@ void WebPage::setFocus() {
m_manager->setCurrentPage(this);
}
void WebPage::remove() {
m_manager->removePage(this);
}
void WebPage::setConfirmAction(QString action) {
m_confirm = (action == "Yes");
}

View File

@ -10,6 +10,7 @@
class WebPageManager;
class InvocationResult;
class NetworkReplyProxy;
class QWebView;
class WebPage : public QWebPage {
Q_OBJECT
@ -33,7 +34,7 @@ class WebPage : public QWebPage {
QVariantList alertMessages();
QVariantList confirmMessages();
QVariantList promptMessages();
void resetWindowSize();
void createWindow();
void resetLocalStorage();
QWebPage *createWindow(WebWindowType type);
QString uuid();
@ -46,6 +47,7 @@ class WebPage : public QWebPage {
QString contentType();
void mouseEvent(QEvent::Type type, const QPoint &position, Qt::MouseButton button);
bool clickTest(QWebElement element, int absoluteX, int absoluteY);
void resize(int, int);
public slots:
bool shouldInterruptJavaScript();
@ -57,6 +59,7 @@ class WebPage : public QWebPage {
void handleSslErrorsForReply(QNetworkReply *reply, const QList<QSslError> &);
void handleUnsupportedContent(QNetworkReply *reply);
void replyFinished(QUrl &, QNetworkReply *);
void remove();
signals:
void pageFinished(bool);

View File

@ -12,7 +12,7 @@ WebPageManager::WebPageManager(QObject *parent) : QObject(parent) {
m_timeout = -1;
m_networkAccessManager = new NetworkAccessManager(this);
m_networkAccessManager->setCookieJar(m_cookieJar);
createPage(this)->setFocus();
createPage()->setFocus();
}
NetworkAccessManager *WebPageManager::networkAccessManager() {
@ -35,8 +35,8 @@ WebPage *WebPageManager::currentPage() const {
return m_currentPage;
}
WebPage *WebPageManager::createPage(QObject *parent) {
WebPage *page = new WebPage(this, parent);
WebPage *WebPageManager::createPage() {
WebPage *page = new WebPage(this);
connect(page, SIGNAL(loadStarted()),
this, SLOT(emitLoadStarted()));
connect(page, SIGNAL(pageFinished(bool)),
@ -47,6 +47,15 @@ WebPage *WebPageManager::createPage(QObject *parent) {
return page;
}
void WebPageManager::removePage(WebPage *page) {
m_pages.removeOne(page);
page->deleteLater();
if (m_pages.isEmpty())
createPage()->setFocus();
else if (page == m_currentPage)
m_pages.first()->setFocus();
}
void WebPageManager::emitLoadStarted() {
if (m_started.empty()) {
logger() << "Load started";
@ -110,10 +119,12 @@ void WebPageManager::reset() {
m_timeout = -1;
m_cookieJar->clearCookies();
m_networkAccessManager->reset();
m_pages.first()->resetLocalStorage();
m_pages.first()->deleteLater();
m_pages.clear();
createPage(this)->setFocus();
m_currentPage->resetLocalStorage();
while (!m_pages.isEmpty()) {
WebPage *page = m_pages.takeFirst();
delete page;
}
createPage()->setFocus();
}
NetworkCookieJar *WebPageManager::cookieJar() {

View File

@ -20,7 +20,8 @@ class WebPageManager : public QObject {
QList<WebPage *> pages() const;
void setCurrentPage(WebPage *);
WebPage *currentPage() const;
WebPage *createPage(QObject *parent);
WebPage *createPage();
void removePage(WebPage *);
void setIgnoreSslErrors(bool);
bool ignoreSslErrors();
void setTimeout(int);

10
src/WindowClose.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "WindowClose.h"
#include "WebPage.h"
WindowClose::WindowClose(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
}
void WindowClose::windowFound(WebPage *page) {
page->remove();
finish(true);
}

12
src/WindowClose.h Normal file
View File

@ -0,0 +1,12 @@
#include "WindowCommand.h"
class WindowClose : public WindowCommand {
Q_OBJECT
public:
WindowClose(WebPageManager *, QStringList &arguments, QObject *parent = 0);
protected:
virtual void windowFound(WebPage *);
};

27
src/WindowCommand.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "WindowCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
#include "ErrorMessage.h"
WindowCommand::WindowCommand(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void WindowCommand::start() {
findWindow(arguments()[0]);
}
void WindowCommand::findWindow(QString selector) {
foreach(WebPage *page, manager()->pages()) {
if (page->matchesWindowSelector(selector)) {
windowFound(page);
return;
}
}
windowNotFound();
}
void WindowCommand::windowNotFound() {
finish(false,
new ErrorMessage("NoSuchWindowError", "Unable to locate window."));
}

21
src/WindowCommand.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef WINDOW_COMMAND_H
#define WINDOW_COMMAND_H
#include "SocketCommand.h"
class WindowCommand : public SocketCommand {
Q_OBJECT
public:
WindowCommand(WebPageManager *, QStringList &arguments, QObject *parent = 0);
virtual void start();
protected:
virtual void windowFound(WebPage *) = 0;
private:
void findWindow(QString);
void windowNotFound();
};
#endif

View File

@ -1,33 +1,10 @@
#include "WindowFocus.h"
#include "SocketCommand.h"
#include "WebPage.h"
#include "CommandFactory.h"
#include "WebPageManager.h"
#include "ErrorMessage.h"
WindowFocus::WindowFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
WindowFocus::WindowFocus(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
}
void WindowFocus::start() {
focusWindow(arguments()[0]);
}
void WindowFocus::windowNotFound() {
finish(false, new ErrorMessage("Unable to locate window."));
}
void WindowFocus::success(WebPage *page) {
void WindowFocus::windowFound(WebPage *page) {
page->setFocus();
finish(true);
}
void WindowFocus::focusWindow(QString selector) {
foreach(WebPage *page, manager()->pages()) {
if (page->matchesWindowSelector(selector)) {
success(page);
return;
}
}
windowNotFound();
}

View File

@ -1,15 +1,12 @@
#include "SocketCommand.h"
#include "WindowCommand.h"
class WindowFocus : public SocketCommand {
class WindowFocus : public WindowCommand {
Q_OBJECT
public:
WindowFocus(WebPageManager *, QStringList &arguments, QObject *parent = 0);
virtual void start();
private:
void success(WebPage *);
void windowNotFound();
void focusWindow(QString);
protected:
virtual void windowFound(WebPage *);
};

13
src/WindowMaximize.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "WindowMaximize.h"
#include "WebPage.h"
#include "WebPageManager.h"
WindowMaximize::WindowMaximize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
}
void WindowMaximize::windowFound(WebPage *page) {
QDesktopWidget *desktop = QApplication::desktop();
QRect area = desktop->availableGeometry();
page->resize(area.width(), area.height());
finish(true);
}

12
src/WindowMaximize.h Normal file
View File

@ -0,0 +1,12 @@
#include "WindowCommand.h"
class WindowMaximize : public WindowCommand {
Q_OBJECT
public:
WindowMaximize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
protected:
virtual void windowFound(WebPage *);
};

12
src/WindowOpen.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "WindowOpen.h"
#include "SocketCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
WindowOpen::WindowOpen(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}
void WindowOpen::start() {
manager()->createPage();
finish(true);
}

10
src/WindowOpen.h Normal file
View File

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

16
src/WindowResize.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "WindowResize.h"
#include "WebPage.h"
#include "WebPageManager.h"
WindowResize::WindowResize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
}
void WindowResize::windowFound(WebPage *page) {
int width = arguments()[1].toInt();
int height = arguments()[2].toInt();
page->resize(width, height);
finish(true);
}

12
src/WindowResize.h Normal file
View File

@ -0,0 +1,12 @@
#include "WindowCommand.h"
class WindowResize : public WindowCommand {
Q_OBJECT
public:
WindowResize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
protected:
virtual void windowFound(WebPage *);
};

17
src/WindowSize.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "WindowSize.h"
#include "WebPage.h"
#include "WebPageManager.h"
#include "JsonSerializer.h"
WindowSize::WindowSize(WebPageManager *manager, QStringList &arguments, QObject *parent) : WindowCommand(manager, arguments, parent) {
}
void WindowSize::windowFound(WebPage *page) {
QSize size = page->viewportSize();
QVariantList elements;
elements << size.width();
elements << size.height();
JsonSerializer serializer;
QByteArray json = serializer.serialize(elements);
finish(true, json);
}

12
src/WindowSize.h Normal file
View File

@ -0,0 +1,12 @@
#include "WindowCommand.h"
class WindowSize : public WindowCommand {
Q_OBJECT
public:
WindowSize(WebPageManager *, QStringList &arguments, QObject *parent = 0);
protected:
virtual void windowFound(WebPage *);
};

View File

@ -66,7 +66,9 @@ Capybara = {
text: function (index) {
var node = this.getNode(index);
var type = (node.type || node.tagName).toLowerCase();
if (type == "textarea") {
if (!this.isNodeVisible(node)) {
return '';
} else if (type == "textarea") {
return node.innerHTML;
} else {
return node.innerText || node.textContent;
@ -286,20 +288,34 @@ Capybara = {
CapybaraInvocation.keypress(value[strindex]);
}
if (value == '')
this.trigger(index, "change");
} else if (type === "checkbox" || type === "radio") {
if (node.checked != (value === "true")) {
this.leftClick(index);
}
} else if (type === "file") {
this.attachedFiles = Array.prototype.slice.call(arguments, 1);
this.leftClick(index);
} else if (this.isContentEditable(node)) {
var content = document.createTextNode(value);
node.innerHTML = '';
node.appendChild(content);
} else {
node.value = value;
}
},
isContentEditable: function(node) {
if (node.contentEditable == 'true') {
return true;
} else if (node.contentEditable == 'false') {
return false;
} else if (node.contentEditable == 'inherit') {
return this.isContentEditable(node.parentNode);
}
},
focus: function(index) {
this.getNode(index).focus();
},

View File

@ -21,7 +21,7 @@ CHECK_COMMAND(GetCookies)
CHECK_COMMAND(SetProxy)
CHECK_COMMAND(ConsoleMessages)
CHECK_COMMAND(CurrentUrl)
CHECK_COMMAND(ResizeWindow)
CHECK_COMMAND(WindowResize)
CHECK_COMMAND(IgnoreSslErrors)
CHECK_COMMAND(SetSkipImageLoading)
CHECK_COMMAND(WindowFocus)
@ -42,3 +42,9 @@ CHECK_COMMAND(SetUrlBlacklist)
CHECK_COMMAND(Title)
CHECK_COMMAND(Version)
CHECK_COMMAND(FindCss)
CHECK_COMMAND(WindowClose)
CHECK_COMMAND(WindowOpen)
CHECK_COMMAND(WindowSize)
CHECK_COMMAND(WindowMaximize)
CHECK_COMMAND(GoBack)
CHECK_COMMAND(GoForward)

View File

@ -7,6 +7,13 @@ PRECOMPILED_DIR = $${BUILD_DIR}
OBJECTS_DIR = $${BUILD_DIR}
MOC_DIR = $${BUILD_DIR}
HEADERS = \
GoForward.h \
GoBack.h \
WindowMaximize.h \
WindowSize.h \
WindowCommand.h \
WindowOpen.h \
WindowClose.h \
Version.h \
EnableLogging.h \
Authenticate.h \
@ -18,7 +25,7 @@ HEADERS = \
JavascriptConfirmMessages.h \
JavascriptPromptMessages.h \
IgnoreSslErrors.h \
ResizeWindow.h \
WindowResize.h \
CurrentUrl.h \
ConsoleMessages.h \
WebPage.h \
@ -71,6 +78,13 @@ HEADERS = \
IgnoreDebugOutput.h
SOURCES = \
GoForward.cpp \
GoBack.cpp \
WindowMaximize.cpp \
WindowSize.cpp \
WindowCommand.cpp \
WindowOpen.cpp \
WindowClose.cpp \
Version.cpp \
EnableLogging.cpp \
Authenticate.cpp \
@ -82,7 +96,7 @@ SOURCES = \
JavascriptConfirmMessages.cpp \
JavascriptPromptMessages.cpp \
IgnoreSslErrors.cpp \
ResizeWindow.cpp \
WindowResize.cpp \
CurrentUrl.cpp \
ConsoleMessages.cpp \
main.cpp \

View File

@ -1,5 +1,7 @@
#include "NAME.h"
#include "SocketCommand.h"
#include "WebPage.h"
#include "WebPageManager.h"
NAME::NAME(WebPageManager *manager, QStringList &arguments, QObject *parent) : SocketCommand(manager, arguments, parent) {
}

View File

@ -1,6 +1,4 @@
#include "Command.h"
class WebPage;
#include "SocketCommand.h"
class NAME : public SocketCommand {
Q_OBJECT