selenium-webdriver 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
- data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
- data/chrome/src/extension/background.html +9 -0
- data/chrome/src/extension/background.js +933 -0
- data/chrome/src/extension/content_script.js +1286 -0
- data/chrome/src/extension/manifest-nonwin.json +15 -0
- data/chrome/src/extension/manifest-win.json +16 -0
- data/chrome/src/extension/toolstrip.html +28 -0
- data/chrome/src/extension/utils.js +196 -0
- data/chrome/src/rb/lib/selenium/webdriver/chrome.rb +8 -0
- data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +324 -0
- data/chrome/src/rb/lib/selenium/webdriver/chrome/command_executor.rb +70 -0
- data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +119 -0
- data/common/src/js/abstractcommandprocessor.js +161 -0
- data/common/src/js/asserts.js +296 -0
- data/common/src/js/by.js +147 -0
- data/common/src/js/command.js +274 -0
- data/common/src/js/context.js +58 -0
- data/common/src/js/extension/README +2 -0
- data/common/src/js/extension/dommessenger.js +152 -0
- data/common/src/js/factory.js +55 -0
- data/common/src/js/future.js +118 -0
- data/common/src/js/key.js +117 -0
- data/common/src/js/localcommandprocessor.js +181 -0
- data/common/src/js/logging.js +249 -0
- data/common/src/js/testrunner.js +605 -0
- data/common/src/js/timing.js +89 -0
- data/common/src/js/wait.js +199 -0
- data/common/src/js/webdriver.js +853 -0
- data/common/src/js/webelement.js +683 -0
- data/common/src/rb/lib/selenium-webdriver.rb +1 -0
- data/common/src/rb/lib/selenium/webdriver.rb +52 -0
- data/common/src/rb/lib/selenium/webdriver/bridge_helper.rb +88 -0
- data/common/src/rb/lib/selenium/webdriver/child_process.rb +85 -0
- data/common/src/rb/lib/selenium/webdriver/core_ext/dir.rb +41 -0
- data/common/src/rb/lib/selenium/webdriver/driver.rb +128 -0
- data/common/src/rb/lib/selenium/webdriver/element.rb +126 -0
- data/common/src/rb/lib/selenium/webdriver/error.rb +68 -0
- data/common/src/rb/lib/selenium/webdriver/find.rb +69 -0
- data/common/src/rb/lib/selenium/webdriver/navigation.rb +23 -0
- data/common/src/rb/lib/selenium/webdriver/options.rb +50 -0
- data/common/src/rb/lib/selenium/webdriver/platform.rb +82 -0
- data/common/src/rb/lib/selenium/webdriver/target_locator.rb +23 -0
- data/firefox/prebuilt/nsICommandProcessor.xpt +0 -0
- data/firefox/prebuilt/nsINativeEvents.xpt +0 -0
- data/firefox/prebuilt/nsIResponseHandler.xpt +0 -0
- data/firefox/src/extension/chrome.manifest +3 -0
- data/firefox/src/extension/components/context.js +37 -0
- data/firefox/src/extension/components/driver-component.js +127 -0
- data/firefox/src/extension/components/firefoxDriver.js +706 -0
- data/firefox/src/extension/components/json2.js +273 -0
- data/firefox/src/extension/components/keytest.html +554 -0
- data/firefox/src/extension/components/nsCommandProcessor.js +586 -0
- data/firefox/src/extension/components/screenshooter.js +70 -0
- data/firefox/src/extension/components/socketListener.js +185 -0
- data/firefox/src/extension/components/utils.js +1200 -0
- data/firefox/src/extension/components/webLoadingListener.js +57 -0
- data/firefox/src/extension/components/webdriverserver.js +101 -0
- data/firefox/src/extension/components/wrappedElement.js +609 -0
- data/firefox/src/extension/content/fxdriver.xul +30 -0
- data/firefox/src/extension/content/server.js +95 -0
- data/firefox/src/extension/idl/nsICommandProcessor.idl +38 -0
- data/firefox/src/extension/idl/nsIResponseHandler.idl +34 -0
- data/firefox/src/extension/install.rdf +29 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +21 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +86 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +426 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/extension_connection.rb +82 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/launcher.rb +132 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +174 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/profiles_ini.rb +60 -0
- data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +23 -0
- data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
- data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
- data/jobbie/src/rb/lib/selenium/webdriver/ie.rb +14 -0
- data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +552 -0
- data/jobbie/src/rb/lib/selenium/webdriver/ie/lib.rb +94 -0
- data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +147 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote.rb +16 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +374 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/capabilities.rb +105 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/commands.rb +53 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/default_http_client.rb +71 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/response.rb +43 -0
- data/remote/client/src/rb/lib/selenium/webdriver/remote/server_error.rb +32 -0
- metadata +182 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
Copyright 2007-2009 WebDriver committers
|
|
5
|
+
Copyright 2007-2009 Google Inc.
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
18
|
+
-->
|
|
19
|
+
|
|
20
|
+
<overlay id="fxdriver-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
21
|
+
<script src="dommessenger.js" />
|
|
22
|
+
<script src="server.js" />
|
|
23
|
+
<statusbar id="status-bar">
|
|
24
|
+
<statusbarpanel>
|
|
25
|
+
<hbox>
|
|
26
|
+
<label id="fxdriver-label" value="WebDriver" tooltiptext="This Firefox can be remote controlled with WebDriver." />
|
|
27
|
+
</hbox>
|
|
28
|
+
</statusbarpanel>
|
|
29
|
+
</statusbar>
|
|
30
|
+
</overlay>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2007-2009 WebDriver committers
|
|
3
|
+
Copyright 2007-2009 Google Inc.
|
|
4
|
+
Portions copyright 2007 ThoughtWorks, Inc
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
you may not use this file except in compliance with the License.
|
|
8
|
+
You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
See the License for the specific language governing permissions and
|
|
16
|
+
limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var driver = false;
|
|
20
|
+
var domMessenger = null;
|
|
21
|
+
|
|
22
|
+
// This will configure a FirefoxDriver and DomMessenger for each
|
|
23
|
+
// _browser window_ (not chrome window). Multiple tabs in the same window will
|
|
24
|
+
// share a FirefoxDriver and DomMessenger instance.
|
|
25
|
+
window.addEventListener("load", function(e) {
|
|
26
|
+
handle = Components.classes["@googlecode.com/webdriver/fxdriver;1"].createInstance(Components.interfaces.nsISupports);
|
|
27
|
+
var server = handle.wrappedJSObject;
|
|
28
|
+
|
|
29
|
+
if (!domMessenger) {
|
|
30
|
+
var appcontent = document.getElementById('appcontent');
|
|
31
|
+
if (appcontent) {
|
|
32
|
+
try {
|
|
33
|
+
var commandProcessor = Components.
|
|
34
|
+
classes['@googlecode.com/webdriver/command-processor;1'].
|
|
35
|
+
getService(Components.interfaces.nsICommandProcessor);
|
|
36
|
+
domMessenger = new DomMessenger(commandProcessor);
|
|
37
|
+
appcontent.addEventListener('DOMContentLoaded',
|
|
38
|
+
function(e) {
|
|
39
|
+
domMessenger.onPageLoad(e);
|
|
40
|
+
}, true);
|
|
41
|
+
appcontent.addEventListener('pagehide',
|
|
42
|
+
function(e) {
|
|
43
|
+
domMessenger.onPageUnload(e);
|
|
44
|
+
}, true);
|
|
45
|
+
} catch (ex) {
|
|
46
|
+
// Not catching this can really mess things up and lead to inexplicable
|
|
47
|
+
// and hard to debug behavior.
|
|
48
|
+
Components.utils.reportError(ex);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!driver) {
|
|
54
|
+
driver = server.newDriver(window);
|
|
55
|
+
} else {
|
|
56
|
+
if (window.content)
|
|
57
|
+
var frames = window.content.frames;
|
|
58
|
+
|
|
59
|
+
// If we are already focused on a frame, try and stay focused
|
|
60
|
+
if (driver.context.frameId !== undefined && frames) {
|
|
61
|
+
if (frames && frames.length > driver.context.frameId) {
|
|
62
|
+
// do nothing
|
|
63
|
+
} else {
|
|
64
|
+
if (frames && frames.length && "FRAME" == frames[0].frameElement.tagName) {
|
|
65
|
+
if (!frames[driver.context.frameId]) {
|
|
66
|
+
driver.context.frameId = 0;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
driver.context.frameId = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// Other use a sensible default
|
|
75
|
+
if (frames && frames.length && "FRAME" == frames[0].frameElement.tagName) {
|
|
76
|
+
if (!frames[driver.context.frameId]) {
|
|
77
|
+
driver.context.frameId = 0;
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
driver.context.frameId = undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
server.startListening();
|
|
86
|
+
|
|
87
|
+
}, true);
|
|
88
|
+
|
|
89
|
+
//window.addEventListener("focus", function(e) {
|
|
90
|
+
// var active = e.originalTarget;
|
|
91
|
+
// var doc = gBrowser.selectedBrowser.contentDocument;
|
|
92
|
+
// if (active.ownerDocument == doc) {
|
|
93
|
+
// driver.activeElement = active;
|
|
94
|
+
// }
|
|
95
|
+
//}, true);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2007-2009 WebDriver committers
|
|
3
|
+
Copyright 2007-2009 Google Inc.
|
|
4
|
+
Portions copyright 2007 ThoughtWorks, Inc
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
you may not use this file except in compliance with the License.
|
|
8
|
+
You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
See the License for the specific language governing permissions and
|
|
16
|
+
limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#include "nsISupports.idl"
|
|
20
|
+
#include "nsIResponseHandler.idl"
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Defines a single method for executing commands specified as a JSON string.
|
|
24
|
+
*/
|
|
25
|
+
[scriptable, uuid(4427729b-441e-47c3-8380-df0350cac636)]
|
|
26
|
+
interface nsICommandProcessor : nsISupports {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Executes the given command specified in a JSON string. When the command
|
|
30
|
+
* is fully processed, the response will be sent to the given
|
|
31
|
+
* {@code nsIResponseHandler} callback as a JSON string.
|
|
32
|
+
*
|
|
33
|
+
* @param jsonCommandString The command to execute, specified in JSON format.
|
|
34
|
+
* @param responseHandler The callback to send the command response to.
|
|
35
|
+
*/
|
|
36
|
+
void execute(in AUTF8String jsonCommandString,
|
|
37
|
+
in nsIResponseHandler responseHandler);
|
|
38
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2007-2009 WebDriver committers
|
|
3
|
+
Copyright 2007-2009 Google Inc.
|
|
4
|
+
Portions copyright 2007 ThoughtWorks, Inc
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
you may not use this file except in compliance with the License.
|
|
8
|
+
You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
See the License for the specific language governing permissions and
|
|
16
|
+
limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#include "nsISupports.idl"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Defines a callback used to process the response to a command sent to the
|
|
23
|
+
* {@code nsICommandProcessor}.
|
|
24
|
+
*/
|
|
25
|
+
[scriptable, function, uuid(0539a68f-b4a8-4543-bf2a-031cef89aff1)]
|
|
26
|
+
interface nsIResponseHandler : nsISupports {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Handle a response from the {@code nsICommandProcessor}.
|
|
30
|
+
*
|
|
31
|
+
* @param jsonResponseString The command response as a string.
|
|
32
|
+
*/
|
|
33
|
+
void handleResponse(in AUTF8String jsonResponseString);
|
|
34
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
|
3
|
+
|
|
4
|
+
<Description about="urn:mozilla:install-manifest">
|
|
5
|
+
<em:id>fxdriver@googlecode.com</em:id>
|
|
6
|
+
<em:name>Firefox WebDriver</em:name>
|
|
7
|
+
<em:version>0.6.680</em:version>
|
|
8
|
+
<em:description>WebDriver implementation for Firefox</em:description>
|
|
9
|
+
<em:creator>Simon Stewart</em:creator>
|
|
10
|
+
|
|
11
|
+
<!-- Firefox -->
|
|
12
|
+
<em:targetApplication>
|
|
13
|
+
<Description>
|
|
14
|
+
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
|
15
|
+
<em:minVersion>2.0</em:minVersion>
|
|
16
|
+
<em:maxVersion>3.*</em:maxVersion>
|
|
17
|
+
</Description>
|
|
18
|
+
</em:targetApplication>
|
|
19
|
+
|
|
20
|
+
<!-- Platforms where we're not compiling the native events -->
|
|
21
|
+
<em:targetPlatform>Darwin</em:targetPlatform>
|
|
22
|
+
<em:targetPlatform>Linux</em:targetPlatform>
|
|
23
|
+
<!-- Platforms where we are -->
|
|
24
|
+
<em:targetPlatform>WINNT_x86-msvc</em:targetPlatform>
|
|
25
|
+
|
|
26
|
+
<!-- We're probably missing lots of platforms here -->
|
|
27
|
+
</Description>
|
|
28
|
+
|
|
29
|
+
</RDF>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "timeout"
|
|
2
|
+
require "socket"
|
|
3
|
+
|
|
4
|
+
require "selenium/webdriver/firefox/util"
|
|
5
|
+
require "selenium/webdriver/firefox/binary"
|
|
6
|
+
require "selenium/webdriver/firefox/profiles_ini"
|
|
7
|
+
require "selenium/webdriver/firefox/profile"
|
|
8
|
+
require "selenium/webdriver/firefox/extension_connection"
|
|
9
|
+
require "selenium/webdriver/firefox/launcher"
|
|
10
|
+
require "selenium/webdriver/firefox/bridge"
|
|
11
|
+
|
|
12
|
+
module Selenium
|
|
13
|
+
module WebDriver
|
|
14
|
+
module Firefox
|
|
15
|
+
|
|
16
|
+
DEFAULT_PROFILE_NAME = "WebDriver".freeze
|
|
17
|
+
DEFAULT_PORT = 7055
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Selenium
|
|
2
|
+
module WebDriver
|
|
3
|
+
module Firefox
|
|
4
|
+
class Binary
|
|
5
|
+
def initialize
|
|
6
|
+
ENV['MOZ_NO_REMOTE'] = '1' # able to launch multiple instances
|
|
7
|
+
check_binary_exists
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def create_base_profile(name)
|
|
11
|
+
execute("-CreateProfile", name)
|
|
12
|
+
Timeout.timeout(15, Error::TimeOutError) { wait }
|
|
13
|
+
|
|
14
|
+
unless $?.success?
|
|
15
|
+
raise Error::WebDriverError, "could not create base profile: (#{$?.exitstatus})"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def start_with(profile, *args)
|
|
20
|
+
ENV['XRE_PROFILE_PATH'] = profile.absolute_path
|
|
21
|
+
|
|
22
|
+
# TODO: native lib loading
|
|
23
|
+
# if Platform.os == :unix && (profile.enable_native_events || profile.always_load_no_focus_lib)
|
|
24
|
+
# modify_link_library_path profile
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
execute(*args)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def execute(*extra_args)
|
|
31
|
+
args = [path, "-no-remote", "--verbose"] + extra_args
|
|
32
|
+
@process = ChildProcess.new(*args).start
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def kill
|
|
36
|
+
@process.kill if @process
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def wait
|
|
40
|
+
@process.wait if @process
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def path
|
|
46
|
+
@path ||= case Platform.os
|
|
47
|
+
when :macosx
|
|
48
|
+
"/Applications/Firefox.app/Contents/MacOS/firefox-bin"
|
|
49
|
+
when :windows
|
|
50
|
+
windows_path
|
|
51
|
+
when :unix
|
|
52
|
+
"/usr/bin/firefox"
|
|
53
|
+
else
|
|
54
|
+
raise "Unknown platform: #{Platform.os}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_binary_exists
|
|
59
|
+
unless File.file?(path)
|
|
60
|
+
raise Error::WebDriverError, "Could not find Firefox binary. Make sure Firefox is installed (OS: #{Platform.os})"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def windows_path
|
|
65
|
+
windows_registry_path || (ENV['PROGRAMFILES'] || "\\Program Files") + "\\Mozilla Firefox\\firefox.exe"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def windows_registry_path
|
|
69
|
+
return if Platform.jruby?
|
|
70
|
+
require "win32/registry"
|
|
71
|
+
|
|
72
|
+
lm = Win32::Registry::HKEY_LOCAL_MACHINE
|
|
73
|
+
lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox") do |reg|
|
|
74
|
+
main = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
|
|
75
|
+
if entry = main.find {|key, type, data| key =~ /pathtoexe/i}
|
|
76
|
+
return entry.last
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
rescue Win32::Registry::Error
|
|
80
|
+
raise Error::WebDriverError, "Firefox not found in Windows registry, please make sure you have it installed."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end # Binary
|
|
84
|
+
end # Firefox
|
|
85
|
+
end # WebDriver
|
|
86
|
+
end # Selenium
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
module Selenium
|
|
2
|
+
module WebDriver
|
|
3
|
+
module Firefox
|
|
4
|
+
class Bridge
|
|
5
|
+
include BridgeHelper
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@binary = Binary.new
|
|
9
|
+
@launcher = Launcher.new(@binary).launch
|
|
10
|
+
@connection = @launcher.connection
|
|
11
|
+
@context = newSession
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def browser
|
|
15
|
+
:firefox
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def quit
|
|
19
|
+
@connection.quit
|
|
20
|
+
@binary.wait
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def getPageSource
|
|
25
|
+
execute :getPageSource
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def getWindowHandles
|
|
29
|
+
execute(:getWindowHandles).to_s.split(", ")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def getCurrentWindowHandle
|
|
33
|
+
execute :getCurrentWindowHandle
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get(url)
|
|
38
|
+
execute :get,
|
|
39
|
+
:parameters => [url]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def close
|
|
43
|
+
execute :close
|
|
44
|
+
# TODO: rescue ?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def goBack
|
|
48
|
+
execute :goBack
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def goForward
|
|
52
|
+
execute :goForward
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def getCurrentUrl
|
|
56
|
+
execute :getCurrentUrl
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def getTitle
|
|
60
|
+
execute :title
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def executeScript(string, *args)
|
|
64
|
+
typed_args = args.map { |e| wrap_script_argument(e) }
|
|
65
|
+
|
|
66
|
+
resp = raw_execute :executeScript, :parameters => [string, typed_args]
|
|
67
|
+
|
|
68
|
+
unwrap_script_argument resp
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
# Finders - TODO: should be shared with Chrome::Bridge
|
|
73
|
+
#
|
|
74
|
+
|
|
75
|
+
def findElementByClassName(parent, class_name)
|
|
76
|
+
find_element_by 'class name', class_name, parent
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def findElementsByClassName(parent, class_name)
|
|
80
|
+
find_elements_by 'class name', class_name, parent
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def findElementById(parent, id)
|
|
84
|
+
find_element_by 'id', id, parent
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def findElementsById(parent, id)
|
|
88
|
+
find_elements_by 'id', id, parent
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def findElementByLinkText(parent, link_text)
|
|
92
|
+
find_element_by 'link text', link_text, parent
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def findElementsByLinkText(parent, link_text)
|
|
96
|
+
find_elements_by 'link text', link_text, parent
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def findElementByPartialLinkText(parent, link_text)
|
|
100
|
+
find_element_by 'partial link text', link_text, parent
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def findElementsByPartialLinkText(parent, link_text)
|
|
104
|
+
find_elements_by 'partial link text', link_text, parent
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def findElementByName(parent, name)
|
|
108
|
+
find_element_by 'name', name, parent
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def findElementsByName(parent, name)
|
|
112
|
+
find_elements_by 'name', name, parent
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def findElementByTagName(parent, tag_name)
|
|
116
|
+
find_element_by 'tag name', tag_name, parent
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def findElementsByTagName(parent, tag_name)
|
|
120
|
+
find_elements_by 'tag name', tag_name, parent
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def findElementByXpath(parent, xpath)
|
|
124
|
+
find_element_by 'xpath', xpath, parent
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def findElementsByXpath(parent, xpath)
|
|
128
|
+
find_elements_by 'xpath', xpath, parent
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Element functions
|
|
133
|
+
#
|
|
134
|
+
|
|
135
|
+
def clickElement(element)
|
|
136
|
+
# execute :clickElement, :element_id => element
|
|
137
|
+
execute :click,
|
|
138
|
+
:element_id => element
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def getElementTagName(element)
|
|
142
|
+
# execute :getElementTagName, :element_id => element
|
|
143
|
+
execute :getTagName,
|
|
144
|
+
:element_id => element
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def getElementAttribute(element, name)
|
|
148
|
+
# execute :getElementAttribute, :element_id => element, :parameters => [name]
|
|
149
|
+
execute :getAttribute,
|
|
150
|
+
:element_id => element,
|
|
151
|
+
:parameters => [name]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def getElementValue(element)
|
|
155
|
+
# execute :getElementValue, :element_id => element
|
|
156
|
+
execute :getValue,
|
|
157
|
+
:element_id => element
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def getElementText(element)
|
|
161
|
+
# execute :getElementText, :element_id => element
|
|
162
|
+
execute :getText,
|
|
163
|
+
:element_id => element
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def getElementLocation(element)
|
|
167
|
+
# data = execute :getElementLocation, :element_id => element
|
|
168
|
+
data = execute :getLocation,
|
|
169
|
+
:element_id => element
|
|
170
|
+
|
|
171
|
+
Point.new(*data.split(",").map { |e| Integer(e.strip) })
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def getElementSize(element)
|
|
175
|
+
# execute :getElementSize, :element_id => element
|
|
176
|
+
execute :getSize,
|
|
177
|
+
:element_id => element
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def sendKeysToElement(element, string)
|
|
181
|
+
# execute :sendKeysToElement, :element_id => element, :parameters => [string.split(//u)]
|
|
182
|
+
execute :sendKeys,
|
|
183
|
+
:element_id => element,
|
|
184
|
+
:parameters => [string.to_s]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def clearElement(element)
|
|
188
|
+
# execute :clearElement, :element_id => element
|
|
189
|
+
execute :clear,
|
|
190
|
+
:element_id => element
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def isElementEnabled(element)
|
|
194
|
+
# execute :isElementEnabled, :element_id => element
|
|
195
|
+
!getElementAttribute(element, "disabled")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def isElementSelected(element)
|
|
199
|
+
# execute :isElementSelected, :element_id => element
|
|
200
|
+
execute :isSelected,
|
|
201
|
+
:element_id => element
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def isElementDisplayed(element)
|
|
205
|
+
# execute :isElementDisplayed, :element_id => element
|
|
206
|
+
execute :isDisplayed,
|
|
207
|
+
:element_id => element
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def submitElement(element)
|
|
211
|
+
# execute :submitElement, :element_id => element
|
|
212
|
+
execute :submit,
|
|
213
|
+
:element_id => element
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def toggleElement(element)
|
|
217
|
+
# execute :toggleElement, :element_id => element
|
|
218
|
+
execute :toggle,
|
|
219
|
+
:element_id => element
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def setElementSelected(element)
|
|
223
|
+
# execute :setElementSelected, :element_id => element
|
|
224
|
+
execute :setSelected,
|
|
225
|
+
:element_id => element
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def getElementValueOfCssProperty(element, prop)
|
|
229
|
+
# execute :getElementValueOfCssProperty, :element_id => element, :parameters => [prop]
|
|
230
|
+
execute :getValueOfCssProperty,
|
|
231
|
+
:element_id => element,
|
|
232
|
+
:parameters => [prop]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def hoverOverElement
|
|
236
|
+
execute :hover,
|
|
237
|
+
:element_id => element
|
|
238
|
+
# execute :hoverOverElement, :element_id => element
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def dragElement(element, rigth_by, down_by)
|
|
242
|
+
execute :dragElement,
|
|
243
|
+
:element_id => element,
|
|
244
|
+
:parameters => [rigth_by, down_by]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def setSpeed(speed)
|
|
249
|
+
pixel_speed = case speed
|
|
250
|
+
when "SLOW" then '1'
|
|
251
|
+
when "MEDIUM" then '10'
|
|
252
|
+
when "FAST" then '100'
|
|
253
|
+
else
|
|
254
|
+
raise ArgumentError, "unknown speed: #{speed.inspect}"
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
execute :setMouseSpeed,
|
|
258
|
+
:parameters => [pixel_speed]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def getSpeed
|
|
262
|
+
case execute(:getMouseSpeed)
|
|
263
|
+
when '1' then "SLOW"
|
|
264
|
+
when '10' then "MEDIUM"
|
|
265
|
+
when '100' then "FAST"
|
|
266
|
+
else
|
|
267
|
+
"FAST"
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def addCookie(cookie)
|
|
272
|
+
execute :addCookie,
|
|
273
|
+
:parameters => [cookie.to_json] # uhm, sending text instead of data
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def deleteCookie(name)
|
|
277
|
+
execute :deleteCookie,
|
|
278
|
+
:parameters => [{:name => name}.to_json] # ditto
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def getAllCookies
|
|
282
|
+
data = execute :getCookie
|
|
283
|
+
|
|
284
|
+
data.strip.split("\n").map do |c|
|
|
285
|
+
parse_cookie_string(c) unless c.strip.empty?
|
|
286
|
+
end.compact
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
#
|
|
290
|
+
# FF-specific?
|
|
291
|
+
#
|
|
292
|
+
|
|
293
|
+
def deleteAllCookies
|
|
294
|
+
execute :deleteAllCookies
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def getCookieNamed(name)
|
|
298
|
+
getCookies.find { |c| c['name'] == name }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def switchToFrame(name)
|
|
302
|
+
execute :switchToFrame,
|
|
303
|
+
:parameters => [name.to_s]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def switchToWindow(name)
|
|
307
|
+
@context = execute :switchToWindow,
|
|
308
|
+
:parameters => [name.to_s]
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def switchToActiveElement
|
|
312
|
+
Element.new self, element_id_from(execute(:switchToActiveElement))
|
|
313
|
+
end
|
|
314
|
+
alias_method :getActiveElement, :switchToActiveElement
|
|
315
|
+
|
|
316
|
+
private
|
|
317
|
+
|
|
318
|
+
def find_element_by(how, what, parent = nil)
|
|
319
|
+
if parent
|
|
320
|
+
id = execute :findChildElement,
|
|
321
|
+
:parameters => [{:id => parent, :using => how, :value => what}]
|
|
322
|
+
else
|
|
323
|
+
id = execute :findElement,
|
|
324
|
+
:parameters => [how, what]
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
Element.new self, element_id_from(id)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def find_elements_by(how, what, parent = nil)
|
|
331
|
+
if parent
|
|
332
|
+
id_string = execute :findChildElements,
|
|
333
|
+
:parameters => [{:id => parent, :using => how, :value => what}]
|
|
334
|
+
else
|
|
335
|
+
id_string = execute :findElements,
|
|
336
|
+
:parameters => [how, what]
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
id_string.split(",").map { |id| Element.new self, element_id_from(id) }
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def newSession
|
|
343
|
+
execute :newSession
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def execute(*args)
|
|
347
|
+
raw_execute(*args)['response']
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def raw_execute(command, opts = {})
|
|
351
|
+
request = {:commandName => command, :context => @context.to_s}
|
|
352
|
+
|
|
353
|
+
if eid = opts[:element_id]
|
|
354
|
+
request[:elementId] = eid
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
if params = opts[:parameters]
|
|
358
|
+
request[:parameters] = params
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
puts "--> #{request.inspect}" if $DEBUG
|
|
362
|
+
|
|
363
|
+
@connection.send_string request.to_json
|
|
364
|
+
resp = @connection.read_response
|
|
365
|
+
|
|
366
|
+
puts "<-- #{resp.inspect}" if $DEBUG
|
|
367
|
+
|
|
368
|
+
if resp['isError']
|
|
369
|
+
msg = resp['response']['message'] rescue nil
|
|
370
|
+
msg ||= resp['response'] || resp.inspect
|
|
371
|
+
raise Error::WebDriverError, msg
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
if ctx = resp['context']
|
|
375
|
+
@context = ctx
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
resp
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
#
|
|
382
|
+
# wrap/unwrap will be shared with Chrome (overrides BridgeHelper for now)
|
|
383
|
+
#
|
|
384
|
+
|
|
385
|
+
def wrap_script_argument(arg)
|
|
386
|
+
case arg
|
|
387
|
+
when Integer, Float
|
|
388
|
+
{ :type => "NUMBER", :value => arg }
|
|
389
|
+
when TrueClass, FalseClass, NilClass
|
|
390
|
+
{ :type => "BOOLEAN", :value => !!arg }
|
|
391
|
+
when Element
|
|
392
|
+
{ :type => "ELEMENT", :value => arg.ref }
|
|
393
|
+
when String
|
|
394
|
+
{ :type => "STRING", :value => arg.to_s }
|
|
395
|
+
when Array # Enumerable?
|
|
396
|
+
arg.map { |e| wrap_script_argument(e) }
|
|
397
|
+
else
|
|
398
|
+
raise TypeError, "Parameter is not of recognized type: #{arg.inspect}:#{arg.class}"
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def unwrap_script_argument(arg)
|
|
403
|
+
raise TypeError, "expected Hash" unless arg.kind_of? Hash
|
|
404
|
+
case arg["resultType"]
|
|
405
|
+
when "NULL"
|
|
406
|
+
nil
|
|
407
|
+
when "ELEMENT"
|
|
408
|
+
Element.new self, element_id_from(arg["response"])
|
|
409
|
+
when "ARRAY"
|
|
410
|
+
arg['response'].map { |e| unwrap_script_argument(e) }
|
|
411
|
+
# when "POINT"
|
|
412
|
+
# Point.new arg['x'], arg['y']
|
|
413
|
+
# when "DIMENSION"
|
|
414
|
+
# Dimension.new arg['width'], arg['height']
|
|
415
|
+
# when "COOKIE"
|
|
416
|
+
# {:name => arg['name'], :value => arg['value']}
|
|
417
|
+
else
|
|
418
|
+
arg["response"]
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
end # Bridge
|
|
424
|
+
end # Firefox
|
|
425
|
+
end # WebDriver
|
|
426
|
+
end # Selenium
|