Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Improve capybara-webkit contribution documentation #994

Open
AlanFoster opened this issue Mar 29, 2017 · 0 comments
Open

Improve capybara-webkit contribution documentation #994

AlanFoster opened this issue Mar 29, 2017 · 0 comments

Comments

@AlanFoster
Copy link

AlanFoster commented Mar 29, 2017

I would love to understand more about how capybara-webkit works and provide contributions to capybara-webkit. However the contributor's guide does not provide a high-level view of how Capybara Webkit works. In particular how capybara-webkit is initialised from the context of Capybara, and how a developer without intimate knowledge of the QT ecosystem could begin to contribute / look at relevant reading resources.

To try to help with this initial documentation, I have investigated the source code and this is what I believe to be true.


Capybara-Webkit

There are two important aspects to Capybara-Webkit. Firstly the Capybara-webkit DSL implementation which allows for a developer to call methods such as page.findCss('#foo'), and a running C++ Webkit-server which the Ruby Capybara-webkit DSL will react out to.

Webkit Server

When Capybara-webkit is installed, it creates a custom webkit binary which directly interacts with a browser. This binary hosts a server which listens for commands sent to it, in this case via capyara-webkit.

When capybara-webkit is bundled via bundle install, it creates a new webkit server binary as part of extconf.rb, which calls CapybaraWebkitBuilder.build_all. This CapybaraWebkitBuilder relies on qmake to create the webkit server binary. A top level file called webkit_server.pro is used as part of the qmake build, where .pro is a file extension used to represent project.

It is within webkit_server.pro that specifies the configuration of various various custom capybara C++ header files to be injected into the custom webkit-server build, as seen here here

Additionally within this webkit_server.pro file there is a RESOURCES = webkit_server.qrc line which injects two custom resources:

<RCC>
  <qresource prefix="/">
    <file>capybara.js</file>
    <file>pointer.png</file>
  </qresource>
</RCC>

It is the Capybara.js file which injects in a global JavaScript helper object called Capybara. This helper contains various function to find and interact with particular elements on the page, such as findXpath etc. For instance, when the capybara-webkit server is called, it will reach out to this custom JavaScript helper embedded on to the page to evaluate its scripts. This JavaScript helper will be injected on to the page when a new webpage is visited.

All of this is required to build a webkit server which is built under bin/webkit_server, which will later be run by the capybara-webkit Ruby process.

Capybara Webkit

Capybara Webkit is a driver for Capybara, and it implements Capybara's Domain Specific Language (DSL). When running an rspec project with feature tests; Capybara will request for the currently configured driver (i.e. capybara-webkit) to initialize. When this Capybara driver is initialized, the previously built webkit server bin/webkit_server will be started, and a connection from Ruby will be opened to this new server.

As a high level example of how the interaction of capybara-webkit works:

  1. A Capybara DSL method such as page.find_css('#foo') is called within an RSpec test
  2. The Capybara-webkit library needs to reach out to the running webkit server
  3. Capybara webkit communicates with the webkit server with 'command' calls. For instance invoke("findXpathWithin", xpath), which will result in a command being sent to the previously opened webkit-server connection, as shown here
  4. The webkit-server receives and parses this sent string command into a C++ command object, which contains the logic of how to handle this particular request. The full list can be seen here:
CHECK_COMMAND(Visit)
CHECK_COMMAND(FindXpath)
CHECK_COMMAND(Reset)

...
  1. In this case the FindCss.cpp command object will be matched, and the particular implementation will invoke page()->invokeCapybaraFunction(...).
  2. invokeCapybaraFunction is a function call which will attempt to make the request to call findCss within the browser. It does this in two steps. First, it first injects a global CapybaraInvocation object on the current QWebFrame, which contains the request object information. With this helper object injected on to the web page, it will call the previously embedded JavaScript helper Capybara.invoke. The implementation of invoke will look at injected CapybaraInvocation object and decide what function to call:
  invoke: function () {
    try {
      if (CapybaraInvocation.functionName == "leftClick") {
        return this["verifiedClickPosition"].apply(this, CapybaraInvocation.arguments);
      } else {
        return this[CapybaraInvocation.functionName].apply(this, CapybaraInvocation.arguments);
      }
    } catch (e) {
      CapybaraInvocation.error = e;
    }
  },

Useful Resources

QT - The C++ library used to create applications with widgets. Capybara-webkit uses this and qmake to create a custom qtwebkit binary
QT_WebKit - Describes how to integrate QT Webkit in a .pro file, as used within webkit_server.pro
qmake project files - Describes the overall structure of qmake .pro files


I am unsure if the above knowledge is correct; And I would appreciate additional insight into this 👍
If it seems appropriate, I can go ahead and create a PR to add this into the codebase too.

At a high-level I would like to also add additional information into debugging race conditions/other issues with capybara webkit.

For instance, how a developer should approach a problem such as:

     1.1) Failure/Error: visit '/dashboard'

          EOFError:
            end of file reached
          # <internal:prelude>:76:in `__read_nonblock'
          # <internal:prelude>:76:in `read_nonblock'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/connection.rb:41:in `read'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/connection.rb:32:in `gets'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/browser.rb:299:in `check'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/browser.rb:211:in `command'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/browser.rb:19:in `visit'
          # /usr/local/bundle/gems/capybara-webkit-1.12.0/lib/capybara/webkit/driver.rb:50:in `visit'
          # /usr/local/bundle/gems/capybara-2.12.1/lib/capybara/session.rb:252:in `visit'
          # /usr/local/bundle/gems/capybara-2.12.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
          # 

Currently I am unsure how to document the debugging issues, due to my unfamiliarity with the codebase, but I would definitely appreciate thoughts on this 👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants