Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

support `within_frame` capybara method #25

Closed
wants to merge 2 commits into from

3 participants

@agibralter

I added within_frame support to capybara-webkit. The method takes either a Fixnum or a String and sets the focus on a frame located by index or name/id respectively.

@agibralter agibralter Working on frame handling. In terms of the QT code, the non-frame-rel…
…ated

specs pass with these changes (e.g. mainFrame() => currentFrame() and the new
injectJavascriptHelpers() code in WebPage.cpp).

It seems like the current JS+xpath implementation dives in to iframes already.
Is this desired behavior? I wonder if that works with x-domain iframes? I
doubt it...

Also, this design assumes that we only step one frame down at a time...

Lastly, I'm really not sure how QWebKit decides which frame is currentFrame().
For now, I'm hoping to be able to use the QWebFrame setFocus() method. This
may be a dead end though. We may have to have WebPage manually keep track of
the "current" frame.
e677195
@agibralter

Hmm I think I'll use setFocus()... and in order to jump down and up multiple levels, we'll have to loop through all childFrames() and childFrames() of those frames and so on. We'll also have to keep track of how many levels down we go so that frame_focus_parent (maybe to be renamed frame_focus_original) knows how many levels to jump back up.

@jferris
Admin

Thanks; I pulled this in. I refactored the FrameFocus class a bit; here are the changes if you're interested: 3615624

@jferris jferris closed this
@agibralter

Awesome, thank you! Sorry for my n00b c++ code... it's been like 6 years since I've touched C.

@rmontgomery429

Is this supposed to yield the current page context to the caller? It looks like setFocus() in Qt only focuses the keyboard on that element.

http://harmattan-dev.nokia.com/docs/library/html/qtwebkit/qwebframe.html#setFocus

Mostly I'm wondering if the page.find inside the within_frame supposed be scoped to the iframe?

within_frame id do
  page.find(".within-frame")
end
@agibralter

yeah I think page.find should work within that frame. That's what the tests in this commit test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 19, 2011
  1. @agibralter

    Working on frame handling. In terms of the QT code, the non-frame-rel…

    agibralter authored
    …ated
    
    specs pass with these changes (e.g. mainFrame() => currentFrame() and the new
    injectJavascriptHelpers() code in WebPage.cpp).
    
    It seems like the current JS+xpath implementation dives in to iframes already.
    Is this desired behavior? I wonder if that works with x-domain iframes? I
    doubt it...
    
    Also, this design assumes that we only step one frame down at a time...
    
    Lastly, I'm really not sure how QWebKit decides which frame is currentFrame().
    For now, I'm hoping to be able to use the QWebFrame setFocus() method. This
    may be a dead end though. We may have to have WebPage manually keep track of
    the "current" frame.
Commits on Apr 20, 2011
  1. @agibralter
This page is out of date. Refresh to see the latest.
View
9 lib/capybara/driver/webkit.rb
@@ -52,8 +52,13 @@ def status_code
raise Capybara::NotSupportedByDriverError
end
- def within_frame(frame_id)
- raise Capybara::NotSupportedByDriverError
+ def within_frame(frame_id_or_index)
+ browser.frame_focus(frame_id_or_index)
+ begin
+ yield
+ ensure
+ browser.frame_focus
+ end
end
def within_window(handle)
View
10 lib/capybara/driver/webkit/browser.rb
@@ -30,6 +30,16 @@ def url
command("Url")
end
+ def frame_focus(frame_id_or_index=nil)
+ if frame_id_or_index.is_a? Fixnum
+ command("FrameFocus", "", frame_id_or_index.to_s)
+ elsif frame_id_or_index
+ command("FrameFocus", frame_id_or_index)
+ else
+ command("FrameFocus")
+ end
+ end
+
def command(name, *args)
@socket.puts name
@socket.puts args.size
View
98 spec/driver_spec.rb
@@ -6,6 +6,104 @@
before { subject.visit("/hello/world?success=true") }
after { subject.reset! }
+ context "iframe app" do
+ before(:all) do
+ @app = lambda do |env|
+ params = ::Rack::Utils.parse_query(env['QUERY_STRING'])
+ if params["iframe"] == "true"
+ # We are in an iframe request.
+ p_id = "farewell"
+ msg = "goodbye"
+ iframe = nil
+ else
+ # We are not in an iframe request and need to make an iframe!
+ p_id = "greeting"
+ msg = "hello"
+ iframe = "<iframe id=\"f\" src=\"/?iframe=true\"></iframe>"
+ end
+ body = <<-HTML
+ <html>
+ <head>
+ <style type="text/css">
+ #display_none { display: none }
+ </style>
+ </head>
+ <body>
+ #{iframe}
+ <script type="text/javascript">
+ document.write("<p id='#{p_id}'>#{msg}</p>");
+ </script>
+ </body>
+ </html>
+ HTML
+ [200,
+ { 'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s },
+ [body]]
+ end
+ end
+
+ it "finds frames by index" do
+ subject.within_frame(0) do
+ subject.find("//*[contains(., 'goodbye')]").should_not be_empty
+ end
+ end
+
+ it "finds frames by id" do
+ subject.within_frame("f") do
+ subject.find("//*[contains(., 'goodbye')]").should_not be_empty
+ end
+ end
+
+ it "raises error for missing frame by index" do
+ expect { subject.within_frame(1) { } }.
+ to raise_error(Capybara::Driver::Webkit::WebkitError)
+ end
+
+ it "raise_error for missing frame by id" do
+ expect { subject.within_frame("foo") { } }.
+ to raise_error(Capybara::Driver::Webkit::WebkitError)
+ end
+
+ it "returns an attribute's value" do
+ subject.within_frame("f") do
+ subject.find("//p").first["id"].should == "farewell"
+ end
+ end
+
+ it "returns a node's text" do
+ subject.within_frame("f") do
+ subject.find("//p").first.text.should == "goodbye"
+ end
+ end
+
+ it "returns the current URL" do
+ subject.within_frame("f") do
+ port = subject.instance_variable_get("@rack_server").port
+ subject.current_url.should == "http://127.0.0.1:#{port}/?iframe=true"
+ end
+ end
+
+ it "returns the source code for the page" do
+ subject.within_frame("f") do
+ subject.source.should =~ %r{<html>.*farewell.*}m
+ end
+ end
+
+ it "evaluates Javascript" do
+ subject.within_frame("f") do
+ result = subject.evaluate_script(%<document.getElementById('farewell').innerText>)
+ result.should == "goodbye"
+ end
+ end
+
+ it "executes Javascript" do
+ subject.within_frame("f") do
+ subject.execute_script(%<document.getElementById('farewell').innerHTML = 'yo'>)
+ subject.find("//p[contains(., 'yo')]").should_not be_empty
+ end
+ end
+ end
+
context "hello app" do
before(:all) do
@app = lambda do |env|
View
1  src/Connection.cpp
@@ -9,6 +9,7 @@
#include "Source.h"
#include "Evaluate.h"
#include "Execute.h"
+#include "FrameFocus.h"
#include <QTcpSocket>
#include <iostream>
View
2  src/Evaluate.cpp
@@ -7,7 +7,7 @@ Evaluate::Evaluate(WebPage *page, QObject *parent) : Command(page, parent) {
}
void Evaluate::start(QStringList &arguments) {
- QVariant result = page()->mainFrame()->evaluateJavaScript(arguments[0]);
+ QVariant result = page()->currentFrame()->evaluateJavaScript(arguments[0]);
addVariant(result);
emit finished(true, m_buffer);
}
View
2  src/Execute.cpp
@@ -6,7 +6,7 @@ Execute::Execute(WebPage *page, QObject *parent) : Command(page, parent) {
void Execute::start(QStringList &arguments) {
QString script = arguments[0] + QString("; 'success'");
- QVariant result = page()->mainFrame()->evaluateJavaScript(script);
+ QVariant result = page()->currentFrame()->evaluateJavaScript(script);
QString response;
if (result.isValid()) {
emit finished(true, response);
View
49 src/FrameFocus.cpp
@@ -0,0 +1,49 @@
+#include "FrameFocus.h"
+#include "Command.h"
+#include "WebPage.h"
+
+FrameFocus::FrameFocus(WebPage *page, QObject *parent) : Command(page, parent) {
+}
+
+void FrameFocus::start(QStringList &arguments) {
+ int index;
+ bool ok;
+ bool found = false;
+ QString response;
+ QList<QWebFrame *> child_frames = page()->currentFrame()->childFrames();
+ if (arguments.length() > 0) {
+ if (arguments.length() > 1) {
+ // Find frame by index.
+ index = arguments[1].toInt(&ok);
+ if (ok && 0 <= index && index < child_frames.length()) {
+ child_frames[index]->setFocus();
+ found = true;
+ }
+ } else {
+ // Find frame by id.
+ for (int i = 0; i < child_frames.length(); i++) {
+ if (child_frames[i]->frameName().compare(arguments[0]) == 0) {
+ child_frames[i]->setFocus();
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found) {
+ emit finished(true, response);
+ } else {
+ response = "Unable to locate frame. ";
+ emit finished(false, response);
+ }
+ } else {
+ // Set focus on parent.
+ if (page()->currentFrame()->parentFrame() == 0) {
+ response = "Already at parent frame.";
+ emit finished(false, response);
+ } else {
+ page()->currentFrame()->parentFrame()->setFocus();
+ emit finished(true, response);
+ }
+ }
+}
+
View
12 src/FrameFocus.h
@@ -0,0 +1,12 @@
+#include "Command.h"
+
+class WebPage;
+
+class FrameFocus : public Command {
+ Q_OBJECT
+
+ public:
+ FrameFocus(WebPage *page, QObject *parent = 0);
+ virtual void start(QStringList &arguments);
+};
+
View
2  src/Reset.cpp
@@ -8,7 +8,7 @@ void Reset::start(QStringList &arguments) {
Q_UNUSED(arguments);
page()->triggerAction(QWebPage::Stop);
- page()->mainFrame()->setHtml("<html><body></body></html>");
+ page()->currentFrame()->setHtml("<html><body></body></html>");
page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
QString response = "";
emit finished(true, response);
View
2  src/Source.cpp
@@ -7,7 +7,7 @@ Source::Source(WebPage *page, QObject *parent) : Command(page, parent) {
void Source::start(QStringList &arguments) {
Q_UNUSED(arguments)
- QString response = page()->mainFrame()->toHtml();
+ QString response = page()->currentFrame()->toHtml();
emit finished(true, response);
}
View
2  src/Url.cpp
@@ -7,7 +7,7 @@ Url::Url(WebPage *page, QObject *parent) : Command(page, parent) {
void Url::start(QStringList &argments) {
Q_UNUSED(argments);
- QUrl humanUrl = page()->mainFrame()->url();
+ QUrl humanUrl = page()->currentFrame()->url();
QByteArray encodedBytes = humanUrl.toEncoded();
QString response = QString(encodedBytes);
View
2  src/Visit.cpp
@@ -7,7 +7,7 @@ Visit::Visit(WebPage *page, QObject *parent) : Command(page, parent) {
}
void Visit::start(QStringList &arguments) {
- page()->mainFrame()->setUrl(QUrl(arguments[0]));
+ page()->currentFrame()->setUrl(QUrl(arguments[0]));
}
void Visit::loadFinished(bool success) {
View
18 src/WebPage.cpp
@@ -4,8 +4,6 @@
#include <iostream>
WebPage::WebPage(QObject *parent) : QWebPage(parent) {
- connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
- this, SLOT(injectJavascriptHelpers()));
QResource javascript(":/capybara.js");
char * javascriptString = new char[javascript.size() + 1];
strcpy(javascriptString, (const char *)javascript.data());
@@ -14,10 +12,18 @@ WebPage::WebPage(QObject *parent) : QWebPage(parent) {
m_loading = false;
connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted()));
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
+ connect(this, SIGNAL(frameCreated(QWebFrame *)),
+ this, SLOT(frameCreated(QWebFrame *)));
+}
+
+void WebPage::frameCreated(QWebFrame * frame) {
+ connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
+ this, SLOT(injectJavascriptHelpers()));
}
void WebPage::injectJavascriptHelpers() {
- mainFrame()->evaluateJavaScript(m_capybaraJavascript);
+ QWebFrame* frame = qobject_cast<QWebFrame *>(QObject::sender());
+ frame->evaluateJavaScript(m_capybaraJavascript);
}
bool WebPage::shouldInterruptJavaScript() {
@@ -28,9 +34,9 @@ QVariant WebPage::invokeCapybaraFunction(const char *name, QStringList &argument
QString qname(name);
QString objectName("CapybaraInvocation");
JavascriptInvocation invocation(qname, arguments);
- mainFrame()->addToJavaScriptWindowObject(objectName, &invocation);
+ currentFrame()->addToJavaScriptWindowObject(objectName, &invocation);
QString javascript = QString("Capybara.invoke()");
- return mainFrame()->evaluateJavaScript(javascript);
+ return currentFrame()->evaluateJavaScript(javascript);
}
QVariant WebPage::invokeCapybaraFunction(QString &name, QStringList &arguments) {
@@ -76,6 +82,6 @@ bool WebPage::isLoading() const {
}
QString WebPage::failureString() {
- return QString("Unable to load URL: ") + mainFrame()->url().toString();
+ return QString("Unable to load URL: ") + currentFrame()->url().toString();
}
View
1  src/WebPage.h
@@ -15,6 +15,7 @@ class WebPage : public QWebPage {
void loadStarted();
void loadFinished(bool);
bool isLoading() const;
+ void frameCreated(QWebFrame *);
protected:
virtual void javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID);
View
1  src/find_command.h
@@ -11,3 +11,4 @@ CHECK_COMMAND(Url)
CHECK_COMMAND(Source)
CHECK_COMMAND(Evaluate)
CHECK_COMMAND(Execute)
+CHECK_COMMAND(FrameFocus)
View
4 src/webkit_server.pro
@@ -1,8 +1,8 @@
TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
-HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h
-SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp
+HEADERS = WebPage.h Server.h Connection.h Command.h Visit.h Find.h Reset.h Node.h JavascriptInvocation.h Url.h Source.h Evaluate.h Execute.h FrameFocus.h
+SOURCES = main.cpp WebPage.cpp Server.cpp Connection.cpp Command.cpp Visit.cpp Find.cpp Reset.cpp Node.cpp JavascriptInvocation.cpp Url.cpp Source.cpp Evaluate.cpp Execute.cpp FrameFocus.cpp
RESOURCES = webkit_server.qrc
QT += network webkit
CONFIG += console
Something went wrong with that request. Please try again.