capybara-rc 0.5.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/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.2.3
5
+ before_install: gem install bundler -v 1.10.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ * 0.5.1
2
+
3
+ * Add homepage to gemspec (rubygems.org won't let you publish without it)
4
+
5
+ # 0.5.0
6
+
7
+ * Initial version
data/DIFFERENCES.md ADDED
@@ -0,0 +1,34 @@
1
+ # Behavior differences between Selenium-RC and Capybara
2
+
3
+ * Selenium-RC can locate elements which are not visible. Capybara only returns
4
+ visible elements. Among other things, this means that `is_element_present` and
5
+ `is_visible` behave differently in this adapter than in Selenium-RC.
6
+ * Capybara always waits for conditions to become true, so
7
+ `wait_for_page_to_load` is a no-op. Capybara does not expose a way to do what
8
+ Selenium-RC's `wait_for_page_to_load` does (wait until the next time a page
9
+ is loaded for any reason). The Capybara way to do this is to specify a
10
+ condition that will be satisfied once the new page is loaded. Capybara will
11
+ wait for that to become true. See also `Capybara.use_wait_time` if Capybara's
12
+ default wait time is too short for the page load you are waiting on.
13
+ * The Capybara-backed implementation of `is_visible` waits for an element to
14
+ show up even if what you're trying to test is that the element is _not_
15
+ present. The adapter provides a method `capybara_has_no_locator?` which allows
16
+ you to hook into Capybara's negated matchers and avoid this delay.
17
+ * Support for `mouse_out` is not available, but a workaround is calling
18
+ `mouse_over` on another element.
19
+ * Methods which take JavaScript snippets as arguments (such as `get_eval`,
20
+ `run_script`, and `wait_for_condition`) behave differently. Because Capybara
21
+ uses Selenium-Webdriver and not RC, the JavaScript environment is different:
22
+ the execution context is the active window, not the `selenium` object and the
23
+ Selenium-RC API is not present (`browserbot`, etc.). Details for specific
24
+ adapted methods:
25
+ * `get_eval` is translated to Capybara's `execute_script`. While `get_eval`
26
+ returns the value of the last expression automatically, `execute_script`
27
+ only returns a value if the script snippet has an explicit return.
28
+ * `run_script` is also translated to Capybara's `execute_script`. Unlike
29
+ Selenium-RC, this will not necessarily be implemented by creating a script
30
+ tag with the provided JavaScript source.
31
+ * `wait_for_condition`'s condition is evaluated using `evaluate_script`. The
32
+ script must only contain a single expression.
33
+ * Capybara-RC does not support switching to a window based on a handle
34
+ referenced as a local variable in the current page.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capybara-rc.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Collaborative Drug Discovery.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Capybara::Rc
2
+
3
+ This gem provides an adapter that allows Selenium-RC-based code to execute using
4
+ [Capybara](http://jnicklas.github.io/capybara/) instead. This is useful because:
5
+
6
+ * Selenium-RC has been deprecated for a while
7
+ * Capybara allows your tests to run against different backends (not just selenium)
8
+ * Porting an existing test suite to a new library is dangerous — you can wind up
9
+ modifying your tests in such a way that they still pass but are no longer
10
+ actually testing what you want.
11
+
12
+ ### Caveats
13
+
14
+ * This is not a full adapter yet — it only adapts the methods that we are using
15
+ at CDD.
16
+ * Some things that were possible with Selenium-RC are not possible in Capybara.
17
+ See DIFFERENCES.md for a list of some of these.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'capybara-rc'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install capybara-rc
34
+
35
+ ## Usage
36
+
37
+ * Configure Capybara as is appropriate for your application
38
+ * Replace your Selenium-RC initialization with something like this:
39
+
40
+ ```ruby
41
+ require 'capybara/rc'
42
+ # If @browser was your Selenium-RC browser before …
43
+ @browser = Capybara::Rc::Adapter.new(Capybara.current_session)
44
+ ```
45
+
46
+ ### Additions
47
+
48
+ The ideal with this adapter is that you'd be able to drop it in and run your
49
+ existing test suite with no changes. The reality is that you probably use some
50
+ features of Selenium-RC that can't be adapted to Capybara (`get_eval` and the
51
+ Selenium-RC in-browser JavaScript API are likely sources of this).
52
+
53
+ In order to limit the changes you need to make to your test suite, the `Adapter`
54
+ class provides some lower-level translation methods:
55
+
56
+ * `capybara_find_by_locator(locator)`: returns a Capybara element corresponding
57
+ to the given Selenium-RC locator. You can use the Capybara API to do further
58
+ querying/testing on the returned element.
59
+ * `capybara_has_no_locator?(locator)`: allows you to bypass Capybara's wait
60
+ when you are trying to test that something isn't present. Maps from a
61
+ Selenium-RC locator to the appropriate Capybara `has_no_*` method
62
+ (`has_no_css`, `has_no_xpath`, etc.).
63
+ * `capybara_has_locator?(locator)`: The positive version of the previous method.
64
+ Similarly maps to the appropriate Capybara `has_*` method.
65
+
66
+ ## Development
67
+
68
+ ### Project infrastructure
69
+
70
+ * [Source on GitHub](https://github.com/cdd/capybara-rc)
71
+ * [![Build Status](https://travis-ci.org/cdd/capybara-rc.svg?branch=master)](https://travis-ci.org/cdd/capybara-rc)
72
+ [Continuous Integration on Travis-CI](https://travis-ci.org/cdd/capybara-rc)
73
+
74
+ ## Contributing
75
+
76
+ Bug reports and pull requests are welcome on GitHub at https://github.com/cdd/capybara-rc.
77
+
78
+
79
+ ## License
80
+
81
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'capybara/rc/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "capybara-rc"
8
+ spec.version = Capybara::Rc::VERSION
9
+ spec.authors = ["Rhett Sutphin"]
10
+ spec.email = ["rhett@collaborativedrug.com"]
11
+
12
+ spec.summary = %q{Provides the Selenium-RC API on top of Capybara}
13
+ spec.description = %q{
14
+ Provides a wrapper for a Capybara session which exposes the ancient Selenium-RC API.
15
+ Allows gradually porting a legacy Selenium suite to newer technologies.
16
+ }
17
+ spec.homepage = "https://github.com/cdd/capybara-rc"
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.10"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", '~> 3.3'
28
+ spec.add_development_dependency "capybara"
29
+ spec.add_development_dependency "mime-types", "2.99" # 3.0+ requires Ruby 2.0+, which we currently do not support
30
+ end
@@ -0,0 +1,7 @@
1
+ require "capybara/rc/version"
2
+ require "capybara/rc/adapter"
3
+
4
+ module Capybara
5
+ module Rc
6
+ end
7
+ end
@@ -0,0 +1,159 @@
1
+ require 'timeout'
2
+
3
+ module Capybara
4
+ module Rc
5
+ ##
6
+ # Contributes Selenium-RC reader methods to the adapter.
7
+ #
8
+ # Expects a `session` accessor to be provided where it is mixed in.
9
+ module Accessors
10
+ WAIT_FOR_CONDITION_DELAY_SECONDS = 0.2
11
+
12
+ def is_checked(locator)
13
+ element = capybara_find_by_locator(locator)
14
+ if capybara_element_is_radio_or_checkbox?(element)
15
+ element.checked?
16
+ else
17
+ fail "#{locator.inspect} is not a checkable element"
18
+ end
19
+ end
20
+
21
+ ##
22
+ # Note that Capybara does not allow access to invisible elements, so this
23
+ # is equivalent to `is_visible`.
24
+ #
25
+ # @return [Boolean]
26
+ def is_element_present(locator)
27
+ capybara_has_locator?(locator)
28
+ end
29
+
30
+ ##
31
+ # Returns true if the locator matches an element. Capybara does not make
32
+ # invisible elements available at all, so there is no distinction between
33
+ # an invisible element and one that is not present. (This method returns
34
+ # false for both.)
35
+ #
36
+ # @return [Boolean]
37
+ def is_visible(locator)
38
+ capybara_has_locator?(locator)
39
+ end
40
+
41
+ def is_text_present(pattern)
42
+ page_text = session.text
43
+ parsed = parse_selenium_rc_string_pattern(pattern)
44
+ case parsed[:type]
45
+ when :glob
46
+ page_text.include?(parsed[:string])
47
+ else
48
+ fail "Don't know how to search a string using a #{parsed[:type].inspect} Selenium pattern"
49
+ end
50
+ end
51
+
52
+ def is_editable(locator)
53
+ !capybara_find_by_locator(locator).disabled?
54
+ end
55
+
56
+ def get_title
57
+ session.title
58
+ end
59
+
60
+ def get_html_source
61
+ session.source
62
+ end
63
+
64
+ def get_location
65
+ session.current_url
66
+ end
67
+
68
+ def get_text(locator)
69
+ capybara_find_by_locator(locator).text
70
+ end
71
+
72
+ def get_value(locator)
73
+ c_element = capybara_find_by_locator(locator)
74
+ if (capybara_element_is_radio_or_checkbox?(c_element))
75
+ c_element.checked? ? 'on' : 'off'
76
+ else
77
+ c_element.value.strip
78
+ end
79
+ end
80
+
81
+ def get_attribute(attribute_locator)
82
+ match = (attribute_locator.match(/\A(.*)@(\w+)\Z/))
83
+ if match
84
+ element_locator = match[1]
85
+ attribute_name = match[2]
86
+ capybara_find_by_locator(element_locator)[attribute_name]
87
+ else
88
+ fail "Unable to parse attribute_locator #{attribute_locator.inspect}"
89
+ end
90
+ end
91
+
92
+ def get_select_options(locator)
93
+ capybara_find_select_by_locator(locator).
94
+ all('option').map { |opt_elt| opt_elt.text.strip }
95
+ end
96
+
97
+ def get_selected_label(locator)
98
+ capybara_get_selected_option_elements_by_locator(locator).first.text
99
+ end
100
+
101
+ def get_selected_value(locator)
102
+ capybara_get_selected_option_elements_by_locator(locator).first.value
103
+ end
104
+
105
+ def get_xpath_count(xpath)
106
+ session.all(:xpath, xpath).size
107
+ end
108
+
109
+ def get_table(table_cell_address)
110
+ address_parts = table_cell_address.split(".")
111
+ locator = address_parts[0..-3].join(".")
112
+ row = address_parts[-2].to_i
113
+ column = address_parts[-1].to_i
114
+
115
+ table = capybara_find_by_locator(locator)
116
+ row = table.all("tr")[row]
117
+ cell = (row.all('th').to_a + row.all('td').to_a)[column]
118
+
119
+ if cell
120
+ cell.text
121
+ else
122
+ fail Capybara::ElementNotFound
123
+ end
124
+ end
125
+
126
+ def wait_for_condition(script, timeout_milliseconds)
127
+ begin
128
+ Timeout.timeout(timeout_milliseconds / 1000.0) do
129
+ until session.evaluate_script(script)
130
+ sleep(WAIT_FOR_CONDITION_DELAY_SECONDS)
131
+ end
132
+ end
133
+ rescue Timeout::Error
134
+ fail "Condition did not become true within #{timeout_milliseconds}ms"
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def capybara_element_is_radio_or_checkbox?(element)
141
+ element.tag_name == 'input' && %w(checkbox radio).include?(element['type'])
142
+ end
143
+
144
+ def capybara_find_select_by_locator(locator)
145
+ element = capybara_find_by_locator(locator)
146
+ if element.tag_name == 'select'
147
+ element
148
+ else
149
+ fail "#{locator.inspect} does not point to a select element"
150
+ end
151
+ end
152
+
153
+ def capybara_get_selected_option_elements_by_locator(locator)
154
+ capybara_find_select_by_locator(locator).all('option').
155
+ select { |opt_elt| opt_elt.selected? }
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,103 @@
1
+ require 'capybara/rc/selenium_rc_locators'
2
+ require 'capybara/rc/extensions'
3
+
4
+ module Capybara
5
+ module Rc
6
+ ##
7
+ # Contributes Selenium-RC action methods to the adapter.
8
+ #
9
+ # Expects a `session` accessor to be provided where it is mixed in.
10
+ module Actions
11
+ include SeleniumRcLocators
12
+ include Extensions
13
+
14
+ def open(url)
15
+ session.visit(url)
16
+ end
17
+
18
+ def get_eval(js)
19
+ session.execute_script(js)
20
+ end
21
+
22
+ def run_script(js)
23
+ session.execute_script(js); nil
24
+ end
25
+
26
+ def click(locator)
27
+ capybara_find_by_locator(locator).click
28
+ end
29
+
30
+ def check(locator)
31
+ capybara_find_by_locator(locator).set(true)
32
+ end
33
+
34
+ def uncheck(locator)
35
+ capybara_find_by_locator(locator).set(false)
36
+ end
37
+
38
+ def select(select_locator, option_locator)
39
+ select_element = capybara_find_by_locator(select_locator)
40
+ parsed = parse_selenium_rc_select_option_locator(option_locator)
41
+ if parsed[:type] == :text
42
+ select_element.select(parsed[:string])
43
+ return
44
+ end
45
+ options = select_element.all('option')
46
+ option_to_select = nil
47
+ case parsed[:type]
48
+ when :value
49
+ option_to_select = options.detect { |o| o['value'] == parsed[:string] }
50
+ else
51
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium option locator with Capybara"
52
+ end
53
+ if option_to_select
54
+ option_to_select.select_option
55
+ else
56
+ fail Capybara::ElementNotFound, "No option matching #{option_locator.inspect} for select element #{select_locator}"
57
+ end
58
+ end
59
+
60
+ def type(locator, value)
61
+ element = capybara_find_by_locator(locator)
62
+ element.set(value)
63
+ end
64
+
65
+ def type_keys(locator, value)
66
+ element = capybara_find_by_locator(locator)
67
+ element.send_keys(value)
68
+ end
69
+
70
+ def drag_and_drop_to_object(draggable_locator, target_locator)
71
+ draggable = capybara_find_by_locator(draggable_locator)
72
+ target = capybara_find_by_locator(target_locator)
73
+
74
+ draggable.drag_to(target)
75
+ end
76
+
77
+ def drag_and_drop(draggable_locator, move_by)
78
+ draggable = capybara_find_by_locator(draggable_locator).native
79
+ delta_x, delta_y = move_by.split(",").map { |int| Kernel.Integer(int) }
80
+
81
+ session.driver.browser.action.drag_and_drop_by(draggable, delta_x, delta_y).perform
82
+ end
83
+
84
+ def attach_file(locator, file_name)
85
+ capybara_element = capybara_find_by_locator(locator)
86
+ session.attach_file(capybara_element[:id] || capybara_element[:name], file_name)
87
+ end
88
+
89
+ def go_back
90
+ session.execute_script("window.history.back()")
91
+ end
92
+
93
+ def refresh
94
+ session.execute_script("window.location.reload()")
95
+ end
96
+
97
+ def mouse_over(locator)
98
+ element = capybara_find_by_locator(locator)
99
+ element.hover
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,28 @@
1
+ require 'capybara/rc/accessors'
2
+ require 'capybara/rc/actions'
3
+ require 'capybara/rc/extensions'
4
+ require 'capybara/rc/modals'
5
+ require 'capybara/rc/windows'
6
+
7
+ module Capybara
8
+ module Rc
9
+ class Adapter
10
+ include Accessors
11
+ include Actions
12
+ include Extensions
13
+ include Modals
14
+ include Windows
15
+
16
+ attr_reader :session
17
+
18
+ def initialize(session)
19
+ super
20
+ @session = session
21
+ end
22
+
23
+ def wait_for_page_to_load(timeout)
24
+ # Capybara always waits, so nothing to do here.
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,143 @@
1
+ require 'capybara/rc/selenium_rc_locators'
2
+ require 'xpath'
3
+
4
+ module Capybara
5
+ module Rc
6
+ ##
7
+ # Contributes methods to the adapter that are not part of Selenium-RC, but
8
+ # which are useful in a test suite in transition.
9
+ #
10
+ # Expects a `session` accessor to be provided where it is mixed in.
11
+ module Extensions
12
+ include SeleniumRcLocators
13
+
14
+ ##
15
+ # Parses the locator and finds the corresponding Capybara element.
16
+ # Raises Capybara::ElementNotFound when the element can't be found, or
17
+ # a StandardError if the locator is not supported.
18
+ #
19
+ # @return [Capybara::Node::Element]
20
+ def capybara_find_by_locator(locator)
21
+ parsed = parse_selenium_rc_locator(locator)
22
+
23
+ css_selector = locator_to_css_selector(parsed)
24
+ return session.find(css_selector) if css_selector
25
+
26
+ xpath_expression = locator_to_xpath_expression(parsed)
27
+ return session.find(:xpath, xpath_expression) if xpath_expression
28
+
29
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
30
+ end
31
+
32
+ ##
33
+ # Parses the locator and queries the session to see if it is present.
34
+ #
35
+ # @return [Boolean]
36
+ def capybara_has_locator?(locator)
37
+ parsed = parse_selenium_rc_locator(locator)
38
+
39
+ css_selector = locator_to_css_selector(parsed)
40
+ return session.has_css?(css_selector) if css_selector
41
+
42
+ xpath_expression = locator_to_xpath_expression(parsed)
43
+ return session.has_xpath?(xpath_expression) if xpath_expression
44
+
45
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
46
+ end
47
+
48
+ ##
49
+ # Parses the locator and queries the session to see if it is _not_ present.
50
+ # This saves time over `!adapter.is_visible(locator)` because Capybara
51
+ # [pauses]( https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends)
52
+ # to wait for an element to show up after you find it. If you want to
53
+ # check that it isn't there, this pause is a waste of time. As a side
54
+ # effect of this Capybara behavior, this method will also wait for an
55
+ # element to disappear.
56
+ def capybara_has_no_locator?(locator)
57
+ parsed = parse_selenium_rc_locator(locator)
58
+
59
+ css_selector = locator_to_css_selector(parsed)
60
+ return session.has_no_css?(css_selector) if css_selector
61
+
62
+ xpath_expression = locator_to_xpath_expression(parsed)
63
+ return session.has_no_xpath?(xpath_expression) if xpath_expression
64
+
65
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
66
+ end
67
+
68
+ private
69
+
70
+ ##
71
+ # @return [String, nil] A CSS selector for the given Selenium-RC locator,
72
+ # or nil if the locator is of a type which cannot be converted into a
73
+ # CSS selector.
74
+ def locator_to_css_selector(parsedLocator)
75
+ case parsedLocator[:type]
76
+ when :class
77
+ ".#{parsedLocator[:string]}"
78
+ when :css
79
+ parsedLocator[:string]
80
+ when :id
81
+ "##{parsedLocator[:string]}"
82
+ when :identifier
83
+ possibilities = [
84
+ ("##{parsedLocator[:string]}" if parsedLocator[:string] =~ /\A[a-z][a-z0-9_:\-\.]*\Z/i),
85
+ "[name=#{parsedLocator[:string].inspect}]"
86
+ ]
87
+ possibilities.compact.join(', ')
88
+ when :name
89
+ "[name=#{parsedLocator[:string].inspect}]"
90
+ else
91
+ nil
92
+ end
93
+ end
94
+
95
+ ##
96
+ # @return [String, nil] An XPath selector for the given Selenium-RC
97
+ # locator, or nil if the locator is of a type which cannot be converted
98
+ # into an XPath selector. N.b. — intended to be used after the CSS
99
+ # version, so it doesn't attempt to translate locator types handled
100
+ # there.
101
+ def locator_to_xpath_expression(parsed_locator)
102
+ case parsed_locator[:type]
103
+ when :xpath
104
+ parsed_locator[:string]
105
+ when :link
106
+ link_pattern = parse_selenium_rc_string_pattern(parsed_locator[:string])
107
+ link_pattern_to_xpath_expression(link_pattern)
108
+ else
109
+ nil
110
+ end
111
+ end
112
+
113
+ def link_pattern_to_xpath_expression(parsed_pattern)
114
+ case parsed_pattern[:type]
115
+ when :glob
116
+ link_glob_to_xpath_expression(parsed_pattern[:string])
117
+ else
118
+ fail "Don't know how to find a link using a #{parsed[:type].inspect} Selenium pattern"
119
+ end
120
+ end
121
+
122
+ def link_glob_to_xpath_expression(glob)
123
+ pos = 0
124
+ asterisk_locations = glob.split('*', glob.size).map { |part| pos += part.size }
125
+ asterisk_locations.pop # the last index is the remainder after all the asterisks have been accounted for
126
+
127
+ glob_without_asterisks = glob.gsub('*', '')
128
+ content_expression =
129
+ if asterisk_locations.empty?
130
+ XPath.string.n.equals(glob_without_asterisks)
131
+ elsif asterisk_locations == [glob_without_asterisks.size]
132
+ XPath.string.n.starts_with(glob_without_asterisks)
133
+ elsif asterisk_locations == [0, glob_without_asterisks.size]
134
+ XPath.string.n.is(glob_without_asterisks)
135
+ else
136
+ fail "Don't know how to translate the glob #{glob.inspect} into an XPath link-matching expression"
137
+ end
138
+
139
+ XPath.generate { |x| x.descendant(:a)[x.attr(:href)][content_expression] }.to_s
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,27 @@
1
+ module Capybara
2
+ module Rc
3
+ ##
4
+ # Contributes Selenium-RC methods related to modal dialogs to the adapter.
5
+ #
6
+ # Expects a `session` accessor to be provided where it is mixed in.
7
+ module Modals
8
+ def initialize(*)
9
+ choose_ok_on_next_confirmation
10
+ end
11
+
12
+ def choose_ok_on_next_confirmation
13
+ @next_confirmation = :accept_confirm
14
+ end
15
+
16
+ def choose_cancel_on_next_confirmation
17
+ @next_confirmation = :dismiss_confirm
18
+ end
19
+
20
+ def get_confirmation
21
+ result = session.send(@next_confirmation)
22
+ choose_ok_on_next_confirmation
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ module Capybara
2
+ module Rc
3
+ module SeleniumRcLocators
4
+ ##
5
+ # Follows the logic & heuristics in
6
+ # https://github.com/SeleniumHQ/selenium/blob/master/javascript/selenium-atoms/se_locators.js
7
+ # function core.locators.parseLocator_
8
+ def parse_selenium_rc_locator(locator)
9
+ match = locator.match(/\A([A-Za-z]+)=.+\Z/)
10
+ if match
11
+ type = match[1]
12
+ string = locator.split('=', 2)[1]
13
+ {
14
+ type: type.to_sym,
15
+ string: string
16
+ }
17
+ elsif locator =~ %r{^//}
18
+ { type: :xpath, string: locator }
19
+ elsif locator =~ /^document\./
20
+ { type: :dom, string: locator }
21
+ else
22
+ { type: :identifier, string: locator }
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Follows the logic & heuristics in
28
+ # https://github.com/SeleniumHQ/selenium/blob/master/javascript/selenium-atoms/select.js
29
+ # function core.select.option.getOptionLocator_
30
+ def parse_selenium_rc_select_option_locator(option_locator)
31
+ match = option_locator.match(/\A([A-Za-z]+)=.*\Z/)
32
+ if match
33
+ type = match[1]
34
+ type = "text" if type == "label"
35
+ string = option_locator.split('=', 2)[1]
36
+ {
37
+ type: type.to_sym,
38
+ string: string
39
+ }
40
+ else
41
+ { type: :text, string: option_locator }
42
+ end
43
+ end
44
+
45
+ def parse_selenium_rc_window_locator(window_locator)
46
+ if window_locator == nil || window_locator == "null"
47
+ return { type: :main_window, string: nil }
48
+ end
49
+ match = window_locator.match(/\A([A-Za-z]+)=.*\Z/)
50
+ if match
51
+ type = match[1]
52
+ string = window_locator.split('=', 2)[1]
53
+ { type: type.to_sym, string: string }
54
+ else
55
+ { type: :window_id, string: window_locator }
56
+ end
57
+ end
58
+
59
+ def parse_selenium_rc_string_pattern(pattern)
60
+ match = pattern.match(/\A([A-Za-z]+):.+\Z/)
61
+ if match
62
+ type = match[1]
63
+ string = pattern.split(':', 2)[1]
64
+ {
65
+ type: type.to_sym,
66
+ string: string
67
+ }
68
+ else
69
+ { type: :glob, string: pattern }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module Capybara
2
+ module Rc
3
+ VERSION = "0.5.1"
4
+ end
5
+ end
@@ -0,0 +1,110 @@
1
+ module Capybara
2
+ module Rc
3
+ ##
4
+ # Contributes Selenium-RC methods related to windows to the adapter.
5
+ #
6
+ # Expects a `session` accessor to be provided where it is mixed in.
7
+ # Also requires that the adapter class's initialize method call super.
8
+ module Windows
9
+ MAIN_APP_WINDOW_NAME = "selenium_main_app_window"
10
+
11
+ def initialize(session)
12
+ super
13
+ if session.respond_to?(:current_window)
14
+ # The result from Capybara.string does not have a current window
15
+ @main_window = session.current_window
16
+ session.execute_script("window.name = #{MAIN_APP_WINDOW_NAME.inspect}")
17
+ end
18
+ end
19
+
20
+ def open_window(url, window_id)
21
+ session.execute_script("window.open(#{url.inspect}, #{window_id.inspect})")
22
+ end
23
+
24
+ def select_window(window_locator)
25
+ parsed = parse_selenium_rc_window_locator(window_locator)
26
+
27
+ window =
28
+ case parsed[:type]
29
+ when :main_window
30
+ @main_window
31
+ when :name
32
+ find_window_by_name(parsed[:string])
33
+ when :title
34
+ find_window_by_title(parsed[:string])
35
+ when :window_id
36
+ find_window_by_name_or_title(parsed[:string])
37
+ else
38
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium window locator with Capybara"
39
+ end
40
+
41
+ unless window
42
+ fail "Could not find a window matching #{window_locator.inspect}"
43
+ end
44
+ session.switch_to_window(window)
45
+ end
46
+
47
+ def close
48
+ window = session.current_window
49
+ window.close
50
+ end
51
+
52
+ def window_maximize
53
+ session.current_window.maximize
54
+ end
55
+
56
+ def get_all_window_names
57
+ all_window_names = []
58
+ in_each_window do |window, page|
59
+ all_window_names << page.execute_script("return window.name;");
60
+ end
61
+ all_window_names
62
+ end
63
+
64
+ private
65
+
66
+ def find_window_by_name(name)
67
+ in_each_window do |window, page|
68
+ this_name = page.execute_script("return window.name;");
69
+ break window if name == this_name
70
+ end
71
+ end
72
+
73
+ def find_window_by_title(title)
74
+ in_each_window do |window, page|
75
+ break window if title == page.title
76
+ end
77
+ end
78
+
79
+ def find_window_by_name_or_title(id)
80
+ # Check for name matches & return immediately if found. At the same
81
+ # time collect titles so that we can do that check if necessary without
82
+ # flipping through the windows again.
83
+ titles = {}
84
+ by_name = in_each_window do |window, page|
85
+ name = page.execute_script("return window.name;");
86
+ break window if name == id
87
+ titles[page.title] = window
88
+ end
89
+ return by_name if by_name
90
+
91
+ # If the other checks didn't find anything, return the window matching
92
+ # by title or nil if there isn't one.
93
+ titles[id]
94
+ end
95
+
96
+ def in_each_window(&block)
97
+ starting_window = session.current_window
98
+ begin
99
+ session.windows.each do |window|
100
+ session.switch_to_window(window)
101
+ yield window, session
102
+ end
103
+ nil
104
+ ensure
105
+ session.switch_to_window(starting_window)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capybara-rc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rhett Sutphin
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2015-12-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.10'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.10'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: capybara
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: mime-types
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: '2.99'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: '2.99'
94
+ description: ! "\n Provides a wrapper for a Capybara session which exposes the
95
+ ancient Selenium-RC API.\n Allows gradually porting a legacy Selenium suite to
96
+ newer technologies.\n "
97
+ email:
98
+ - rhett@collaborativedrug.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - .rspec
105
+ - .travis.yml
106
+ - CHANGELOG.md
107
+ - DIFFERENCES.md
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/rspec
113
+ - capybara-rc.gemspec
114
+ - lib/capybara/rc.rb
115
+ - lib/capybara/rc/accessors.rb
116
+ - lib/capybara/rc/actions.rb
117
+ - lib/capybara/rc/adapter.rb
118
+ - lib/capybara/rc/extensions.rb
119
+ - lib/capybara/rc/modals.rb
120
+ - lib/capybara/rc/selenium_rc_locators.rb
121
+ - lib/capybara/rc/version.rb
122
+ - lib/capybara/rc/windows.rb
123
+ homepage: https://github.com/cdd/capybara-rc
124
+ licenses:
125
+ - MIT
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
137
+ - 0
138
+ hash: -3019246126874219789
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ segments:
146
+ - 0
147
+ hash: -3019246126874219789
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 1.8.25
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: Provides the Selenium-RC API on top of Capybara
154
+ test_files: []