browsercuke 0.1.0

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,3 @@
1
+ jssh-3.5.x-Darwin-param.xpi
2
+ Manifest.txt
3
+ pkg
data/README.md ADDED
@@ -0,0 +1,227 @@
1
+ = BrowserCuke
2
+
3
+ http://github.com/sminnee/browsercuke
4
+
5
+ == DESCRIPTION:
6
+
7
+ BrowserCuke is a layer of browser-based testing on top of Cucumber. It provides an intuitive way of
8
+ writing business-readable tests for your web applications, that use real web browsers to test. Your
9
+ test scripts can now be a way of communicating with your client how you have tested the application
10
+ and how it should work.
11
+
12
+ For more information about Cucumber, see [the cucumber website](http://cukes.info).
13
+
14
+ BrowserCuke currently uses Watir to perform the browser automation, although it may be extended in
15
+ the future to use something like WebRat to test applications sans-JavaScript.
16
+
17
+ == FEATURES
18
+
19
+ * Tests Safari and Firefox
20
+ * Looks for page elements like a human would: e.g. ignores hidden elements, and you can use form element labels as identifiers.
21
+ * Automatically wait for ajax requests to complete.
22
+
23
+ It currently has the following limitations:
24
+
25
+ * No known Windows and IE support
26
+ * No ability to test with JavaScript
27
+ * Ajax auto-wait only works with Prototype and jQuery generated Ajax requests.
28
+ * Testing of iframes and multiple windows is untested.
29
+ * No drag & drop tests.
30
+
31
+ Roadmap:
32
+
33
+ * Make it work in Windows/OSX/Linux and Firefox/Safari/IE
34
+ * Add JS-disabled testing using WebRat
35
+ * Ensure that you can test iframes and multiple windows intuitively
36
+ * Add tests for drag & drop
37
+ * Add tests for colour and image changes
38
+ * Get the FireWatir and SafariWatir monkey patches submitted to those upstream projects.
39
+
40
+ == INSTALL:
41
+
42
+ BrowserCuke is packaged as a gem, so the easiest way of installing BrowserCuke is by running this
43
+ command:
44
+
45
+ $ gem install browsercuke
46
+
47
+ After you have installed the gem, you should run this command to set up your browsers properly:
48
+
49
+ $ browsercuke-setup
50
+
51
+ == SYNOPSIS:
52
+
53
+ Run something like this to execute a test freature.
54
+
55
+ browsercuke http://localhost/yoursite create-page.feature
56
+
57
+ === Using BrowserCuke to run tests from another project
58
+
59
+ BrowserCuke doesn't come bundled with any actual tests, so usually you have to write tests as part
60
+ of your project.
61
+
62
+
63
+ This example runs a copy of BrowserCuke installed in ~/browsercuke to execute the features
64
+ contained in a Sapphire project. `sake SapphireURL/baseurl` return the URL of the current Sapphire
65
+ project.
66
+
67
+ browsercuke firefox `sake SapphireInfo/baseurl` */tests/cuke/*.feature
68
+
69
+ Because BrowserCuke was written in order to support testing of Sapphire projects, we have a special
70
+ script for this particular example. This will probably go away as BrowserCuke matures
71
+
72
+ sapphirecuke firefox
73
+
74
+ === Rules
75
+
76
+ Please see [the cucumber website](http://cukes.info) for information about the exact cucumber
77
+ syntax.
78
+
79
+ Actions
80
+ -------
81
+
82
+ ### When I click the "(identifier)" button
83
+
84
+ ### When I click the "(identifier)" checkbox
85
+
86
+ ### When I click the "(identifier)" image
87
+
88
+ ### When I click the "(identifier)" link
89
+
90
+ ### When I click the "(identifier)" radio button
91
+
92
+ ### When I visit (url)
93
+
94
+ ### When I select "(value)" from "(dropdown identfier)"
95
+
96
+ ### When I put "(value)" in the "(field identifier)" field / When I set "(field)" to "(value)"
97
+
98
+ Assertions
99
+ ----------
100
+
101
+ ### Then I see "(text)"
102
+
103
+ ### Then I don't see "(text)"
104
+
105
+ ### Then I am at (url) / Then I am sent to (url)
106
+
107
+ ### Then the url (url) does not exist
108
+
109
+ ### Then "(value)" is selected in "(dropdown identfier)"
110
+
111
+ ### Then the "(identifier)" field is "(value)" / Then the "(identifier)" field becomes "(value)"
112
+
113
+ ### Then the "(identifier)" field is blank
114
+
115
+
116
+
117
+ Waiting
118
+ -------
119
+
120
+ ### I wait for "(text)"
121
+
122
+ ### I wait for HTML ("html")
123
+
124
+ Misc
125
+ ----
126
+
127
+ ### I cancel pop-ups
128
+
129
+ ### I confirm pop-ups
130
+
131
+ ### I put "(text)" in the pop-up
132
+
133
+ -----
134
+
135
+ Recommended amendment - we should alter the rules to operate this way:
136
+
137
+ Actions
138
+ -------
139
+
140
+ ### When I click "(button/image/link/checkbox/radiobutton)"
141
+
142
+ ### When I visit "(url)"
143
+
144
+ ### When I set "(dropdown/textfield)" to "(value)"
145
+
146
+ Assertions
147
+ ----------
148
+
149
+ ### Then I see "(text)"
150
+
151
+ ### Then I don't see "(text)"
152
+
153
+ ### Then I am at (url)
154
+
155
+ ### Then the url (url) doesn't exist
156
+
157
+ ### Then "(field/dropdown)" is "(value)"
158
+
159
+ ### Then "(checkbox/radio)" is selected
160
+
161
+ ### Then "(checkbox/radio)" isn't selected
162
+
163
+ ### Then "(identifier)" is blank
164
+
165
+
166
+ Waiting
167
+ -------
168
+
169
+ ### I wait for "(text)"
170
+
171
+ ### I wait for HTML ("html")
172
+
173
+ Misc
174
+ ----
175
+
176
+ ### I cancel pop-ups
177
+
178
+ ### I confirm pop-ups
179
+
180
+ ### I put "(text)" in pop-ups
181
+
182
+ == REQUIREMENTS:
183
+
184
+ Currently browsercuke works with the following browsers.
185
+
186
+ * Firefox 3.5
187
+ * Safari
188
+
189
+ It has been tested on OS X only; Windows and IE support coming soon!
190
+
191
+ == DEVELOPERS:
192
+
193
+ After checking out the source, run:
194
+
195
+ $ rake newb
196
+
197
+ This task will install any missing dependencies, run the tests/specs,
198
+ and generate the RDoc.
199
+
200
+ == LICENSE:
201
+
202
+ Browsercuke is licensed under the BSD license
203
+
204
+ Copyright (c) 2009, Sam Minnée
205
+ All rights reserved.
206
+
207
+ Redistribution and use in source and binary forms, with or without
208
+ modification, are permitted provided that the following conditions are met:
209
+ * Redistributions of source code must retain the above copyright
210
+ notice, this list of conditions and the following disclaimer.
211
+ * Redistributions in binary form must reproduce the above copyright
212
+ notice, this list of conditions and the following disclaimer in the
213
+ documentation and/or other materials provided with the distribution.
214
+ * Neither the name of the <organization> nor the
215
+ names of its contributors may be used to endorse or promote products
216
+ derived from this software without specific prior written permission.
217
+
218
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
219
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
220
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
221
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
222
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
223
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
224
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
225
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
226
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
227
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/bin/browsercuke ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ if [ "$2" = "" ]; then
3
+ echo "Usage: $0 (browser) (url) (feature-file) (other cucumber args)
4
+
5
+ Run the given feature file using the URL has the test site, on the given browser.
6
+
7
+ Supported browsers:
8
+ firefox
9
+ safari
10
+
11
+ "
12
+ exit 1
13
+ fi
14
+
15
+ basedir=`dirname $0`
16
+
17
+ export BROWSERSALAD_BROWSER=$1
18
+ export BROWSERSALAD_URL=$2
19
+
20
+
21
+ # Special case for Sapphire applications - collect all the step_definitions from the modules
22
+ if [ -d sapphire/tests/cuke ]; then
23
+ for file in `ls -1d */tests/cuke/step_definitions`; do
24
+ extras="$extras -r $file"
25
+ done
26
+ fi
27
+
28
+ echo "cucumber -r $basedir/support -r $basedir/step_definitions $extras $3 $4 $5 $6 $7 $8 $9"
29
+ cucumber -r $basedir/support -r $basedir/step_definitions $extras $3 $4 $5 $6 $7 $8 $9
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+ echo "Browsercuke OS X installer
3
+ ===========================
4
+ "
5
+
6
+ ffbin=/Applications/Firefox.app/Contents/MacOS/firefox-bin
7
+
8
+
9
+ if [ ! -f jssh-3.5.x-Darwin-param.xpi ]; then
10
+ echo "> Downloading Firefox 3.5 JSSH plugin"
11
+ wget http://wiki.openqa.org/download/attachments/13893658/jssh-3.5.x-Darwin-param.xpi
12
+ fi
13
+
14
+ echo "> Installing Firefox 3.5 JSSH plugin"
15
+ open ./jssh-3.5.x-Darwin-param.xpi
16
+
17
+ echo "> Please go to System Prefences -> Universal Access and check the box labelled 'Enable access for assistive devices'
18
+
19
+ When you're done, press enter to continue."
20
+
21
+ read -s -n1
22
+
23
+ echo "
24
+ Done!
25
+
26
+ You should be able to run one of these commands now:
27
+
28
+ browsercuke firefox http://localhost/yourtestsite create-page.feature
29
+ browsercuke safari http://localhost/yourtestsite create-page.feature
30
+
31
+ "
data/bin/sapphirecuke ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ if [ "$1" = "" ]; then
3
+ echo "Usage: $0 (browser)
4
+ "
5
+ exit 1
6
+ fi
7
+
8
+ if [ ! -f sapphire/main.php ]; then
9
+ echo "Please run $0 from within your sapphire project root.
10
+ I couldn't find ./sapphire/main.php.
11
+ "
12
+ exit 2
13
+ fi
14
+
15
+ dirname=`dirname $0`
16
+ $dirname/browsercuke $1 `./sapphire/sake SapphireInfo/baseurl` */tests/cuke/*.feature
@@ -0,0 +1,38 @@
1
+ Web Application Testing Kit
2
+ ===========================
3
+
4
+ These step definitions are intended to provide a generic, intuitive and powerful mechanism for
5
+ building web application tests in Cucumber. They have been originally developed for the
6
+ SilverStripe CMS but should be able to be used anywhere you find Ajax and HTML.
7
+
8
+
9
+ To do
10
+ -----
11
+
12
+ ### Soon
13
+
14
+ * [x] Wait for ajax
15
+ * [x] Use <label for="X"> tags to find fields by label
16
+ * [x] Exclude hidden fields from the search; find the first visible tab
17
+ * [ ] Make label matchers case-insensitive
18
+ * [ ] Drag & drop testing (incl. SiteTree)
19
+ * [ ] ComplexTableField, pop-up, iframe testing
20
+ * [ ] Content-identification testing
21
+ * [ ] Multi-window testing (incl. checking the front-end site)
22
+  * [ ] TinyMCE testing
23
+
24
+ ### Later
25
+
26
+ * Allow detection of an element's colour - The "New Page" link is green.
27
+
28
+ Origin
29
+ ------
30
+
31
+ These step definitions came from [Watircuke](http://github.com/richdownie/watircuke),
32
+ by Rich Downie. They have been modified quite a bit.
33
+
34
+ No license was provided with them, so we've included this credit and our thanks. :-)
35
+
36
+ Sam Minnée
37
+ SilverStripe
38
+
@@ -0,0 +1,83 @@
1
+ =begin
2
+ Ajax Handling
3
+ This pair of methods lets us wait for ajax actions to complete before proceeding.
4
+
5
+ To use:
6
+ * Call ajax_before_action prior to the action - e.g. button/link click
7
+ * Call ajax_after_action after the action
8
+
9
+ What happens:
10
+ * If the action didn't trigger an ajax call, then it won't wait
11
+ * If the action did trigger an ajax call, then it will wait until after the success handler is
12
+ completed
13
+
14
+ Because the handler does nothing if there wasn't an ajax call, it's safe to be wrapped around all
15
+ button/link clicks.
16
+ =end
17
+
18
+ def ajax_before_action(browser)
19
+
20
+ # Warning: don't use // comments in the JS, only /* */, because newlines are removed
21
+
22
+ js = <<-eos
23
+ window.__ajaxStatus = function() { return 'no ajax'; };
24
+
25
+ if(typeof window.__ajaxPatch == 'undefined') {
26
+ window.__ajaxPatch = 1;
27
+
28
+ var patchedList = "";
29
+
30
+ /* Monkey-patch Prototype */
31
+ if(typeof window.Ajax!='undefined' && typeof window.Ajax.Request!='undefined') {
32
+ window.Ajax.Request.prototype.initialize = function(url, options) {
33
+ this.transport = window.Ajax.getTransport();
34
+
35
+ var __activeTransport = this.transport;
36
+ window.__ajaxStatus = function() {
37
+ return (__activeTransport.readyState == 4) ? 'success' : 'waiting';
38
+ };
39
+
40
+ this.setOptions(options);
41
+ this.request(url);
42
+ };
43
+ patchedList += " prototype";
44
+ }
45
+
46
+ /* Monkey-patch jQuery */
47
+ if(typeof window.jQuery!='undefined') {
48
+ var _orig_ajax = window.jQuery.ajax;
49
+
50
+ window.jQuery(window).ajaxStop(function() {
51
+ window.__ajaxStatus = function() { return 'success'; };
52
+ });
53
+
54
+ window.jQuery.ajax = function(s) {
55
+ window.__ajaxStatus = function() { return 'waiting'; };
56
+ _orig_ajax(s);
57
+ };
58
+ patchedList += " jquery";
59
+ }
60
+ return "patched" + patchedList;
61
+ } else {
62
+ return "already patched: " + window.__ajaxPatch;
63
+ }
64
+ eos
65
+
66
+ browser.evaluate_script(js)
67
+
68
+ #js = js.gsub(/[\n\r]/, "; ")
69
+ #browser.js_eval(js)
70
+
71
+ # For debugging
72
+ #puts "Patching:"
73
+ #puts browser.js_eval(js)
74
+ end
75
+
76
+ def ajax_after_action(browser)
77
+ Watir::Waiter::wait_until {
78
+ status = browser.evaluate_script("return window.__ajaxStatus ? window.__ajaxStatus() : 'no ajax'")
79
+ # For debugging
80
+ # puts "Waiting for ajax: #{status}"
81
+ status != "waiting"
82
+ }
83
+ end
@@ -0,0 +1,33 @@
1
+ # Try running with the button ":id", ":name", ":value", ":text", ":index" or ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /click the "(.*)" button/i do |type|
6
+ btn = getButton(@browser, type)
7
+
8
+ if(btn) then
9
+ ajax_before_action @browser
10
+ btn.click
11
+ ajax_after_action @browser
12
+ else
13
+ fail("could not find the '#{type}'button on #{@browser.url}")
14
+ end
15
+ end
16
+
17
+ def getButton(browser, type)
18
+ button = browser.button(:id, type)
19
+ button = browser.button(:value, type) unless button.exists?
20
+
21
+ # :text used for Firefox suport
22
+ if browser.instance_of?(FireWatir::Firefox)
23
+ button = browser.button(:text, type) unless button.exists?
24
+ end
25
+ # :xpath used for Safari suport
26
+ button = browser.button(:xpath, "//button[.='#{type}']") unless button.exists?
27
+
28
+ button = browser.button(:index, type) unless button.exists?
29
+ button = browser.button(:class, type) unless button.exists?
30
+
31
+ button = nil unless button.exists?
32
+ return button
33
+ end
@@ -0,0 +1,26 @@
1
+ # Try running with the checkbox ":id", ":name", ":value", ":text", ":index" or ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /click the "(.*)" checkbox/i do |type|
6
+ if @browser.checkbox(:id, type).exists? then
7
+ @browser.checkbox(:id, type).click
8
+ elsif
9
+ @browser.checkbox(:name, type).exists? then
10
+ @browser.checkbox(:name, type).click
11
+ elsif
12
+ @browser.checkbox(:value, type).exists? then
13
+ @browser.checkbox(:value, type).click
14
+ elsif
15
+ @browser.checkbox(:text, type).exists? then
16
+ @browser.checkbox(:text, type).click
17
+ elsif
18
+ @browser.checkbox(:index, type).exists? then
19
+ @browser.checkbox(:index, type).click
20
+ elsif
21
+ @browser.checkbox(:class, type).exists? then
22
+ @browser.checkbox(:class, type).click
23
+ else
24
+ fail("could not find what you asked for")
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ Given /I see "(.*)"/ do |text|
2
+ describe "Matcher" do
3
+ it "should have text" do
4
+ @browser.should have_text(text)
5
+ end
6
+ end
7
+ end
8
+
9
+ Given /I don't see "(.*)"/ do |text|
10
+ describe "Matcher" do
11
+ it "shouldn't have text" do
12
+ @browser.should_not have_text(text)
13
+ end
14
+ end
15
+ end
16
+
17
+ Given /I wait for "(.*)"/ do |text|
18
+ Watir::Waiter::wait_until { @browser.text.include? text}
19
+ end
20
+
21
+ Given /I wait for html "(.*)"/ do |text|
22
+ Watir::Waiter::wait_until { @browser.html.include? text}
23
+ end
24
+
25
+ # Wait for time
26
+ Given /Wait ([0-9]+)s$/i do |seconds|
27
+ sleep seconds.to_i
28
+ end
29
+
@@ -0,0 +1,26 @@
1
+ # Try running with the image ":src", ":id", ":name", ":index", ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /click the "(.*)" image/i do |type|
6
+ if @browser.image(:src, type).exists? then
7
+ @browser.image(:src, type).click
8
+ elsif
9
+ @browser.image(:id, type).exists? then
10
+ @browser.image(:id, type).click
11
+ elsif
12
+ @browser.image(:name, type).exists? then
13
+ @browser.image(:name, type).click
14
+ elsif
15
+ @browser.image(:text, type).exists? then
16
+ @browser.image(:text, type).click
17
+ elsif
18
+ @browser.image(:index, type).exists? then
19
+ @browser.image(:index, type).click
20
+ elsif
21
+ @browser.image(:class, type).exists? then
22
+ @browser.image(:class, type).click
23
+ else
24
+ fail("could not find what you asked for")
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # Try running with the link ":id", ":text", ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /click the "(.*)" link/i do |type|
6
+ if link = getLink(@browser, type) then
7
+ ajax_before_action @browser
8
+ link.click
9
+ ajax_after_action @browser
10
+ else
11
+ fail("could not find the '#{type}' link on #{@browser.url}")
12
+ end
13
+ end
14
+
15
+ def getLink(browser, match)
16
+ link = browser.link(:id, match)
17
+ if not link.exists? then link = browser.link(:text, match) end
18
+ if not link.exists? then link = browser.link(:class, match) end
19
+ # Try the URL with both the baseURL prefix and without it
20
+ if not link.exists? then link = browser.link(:url, @baseURL + match) end
21
+ if not link.exists? then link = browser.link(:url, match) end
22
+ if not link.exists? then link = browser.link(:xpath, match) end
23
+ if not link.exists? then link = nil end
24
+
25
+ return link
26
+ end
@@ -0,0 +1,17 @@
1
+ Given /visit (.*)/o do |url|
2
+ @browser.goto(@baseURL + url)
3
+ end
4
+
5
+ Given /I am at (.*)/i do |url|
6
+ @browser.url.should =~ /^#{@baseURL}#{url}/
7
+ end
8
+
9
+ Given /I am sent to (.*)/i do |url|
10
+ Given "I am at #{url}"
11
+ end
12
+
13
+ Given /url (.*) does not exist/ do |url|
14
+ Given "I visit #{url}"
15
+ # Brittle - needs to check that actual 404 status, but that's hard
16
+ And "I see \"The requested page couldn't be found.\""
17
+ end
@@ -0,0 +1,32 @@
1
+ Given /cancel pop-ups/i do
2
+ script = "window.confirm=window.alert=window.prompt=function(){ return false; };"
3
+
4
+ # Special case for FireWatir
5
+ if @browser.respond_to?('evaluate_script_alternate')
6
+ @browser.evaluate_script_alternate(script)
7
+ else
8
+ @browser.evaluate_script(script)
9
+ end
10
+ end
11
+
12
+ Given /confirm pop-ups/i do
13
+ script ="window.confirm=window.alert=function(){ return true; }; window.prompt=function(){ return false; }";
14
+
15
+ # Special case for FireWatir
16
+ if @browser.respond_to?('evaluate_script_alternate')
17
+ @browser.evaluate_script_alternate(script)
18
+ else
19
+ @browser.evaluate_script(script)
20
+ end
21
+ end
22
+
23
+ Given /put "(.*)" in the pop-up/i do | value |
24
+ script = "window.confirm=window.alert=function(){ return true; }; window.prompt=function(){ return \"#{value}\"; }"
25
+
26
+ # Special case for FireWatir
27
+ if @browser.respond_to?('evaluate_script_alternate')
28
+ @browser.evaluate_script_alternate(script)
29
+ else
30
+ @browser.evaluate_script(script)
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ # Try running with the radio ":id", ":name", ":value", ":text", ":index" or ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /click the "(.*)" radio button/i do |type|
6
+ if @browser.radio(:id, type).exists? then
7
+ @browser.radio(:id, type).click
8
+ elsif
9
+ @browser.radio(:name, type).exists? then
10
+ @browser.radio(:name, type).click
11
+ elsif
12
+ @browser.radio(:value, type).exists? then
13
+ @browser.radio(:value, type).click
14
+ elsif
15
+ @browser.radio(:text, type).exists? then
16
+ @browser.radio(:text, type).click
17
+ elsif
18
+ @browser.radio(:index, type).exists? then
19
+ @browser.radio(:index, type).click
20
+ elsif
21
+ @browser.radio(:class, type).exists? then
22
+ @browser.radio(:class, type).click
23
+ else
24
+ fail("could not find what you asked for")
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ # Try running with the select-list ":id", ":name", ":value", ":text", ":index" or ":class" element attribute.
2
+ # Does not matter what you select!
3
+ # The proper watir code will be executed regardless.
4
+
5
+ Given /select "(.*)" from "(.*)"/i do |text, type|
6
+ if @browser.select_list(:id, type).exists? then
7
+ @browser.select_list(:id, type).select(text)
8
+
9
+ elsif @browser.select_list(:name, type).exists? then
10
+ @browser.select_list(:name, type).select(text)
11
+
12
+ elsif @browser.select_list(:value, type).exists? then
13
+ @browser.select_list(:value, type).select(text)
14
+
15
+ elsif @browser.select_list(:text, type).exists? then
16
+ @browser.select_list(:text, type).select(text)
17
+
18
+ elsif @browser.select_list(:index, type).exists? then
19
+ @browser.select_list(:index, type).select(text)
20
+
21
+ elsif @browser.select_list(:class, /(^|\s)#{type}(\s|$)/).exists? then
22
+ @browser.select_list(:class, /(^|\s)#{type}(\s|$)/).set(text)
23
+ else
24
+ fail("could not find what you asked for")
25
+ end
26
+ end
27
+
28
+ Given /"(.*)" is selected in "(.*)"/i do |text, field|
29
+ pending
30
+ end
31
+
@@ -0,0 +1,71 @@
1
+ Given /put "(.*)" in the "(.*)" field/i do |text,type|
2
+ field = getTextField(@browser, type)
3
+ if field then
4
+ field.set(text)
5
+ else
6
+ fail("could not find the '#{type}' field to write '#{text}' into on #{@browser.url}")
7
+ end
8
+ end
9
+
10
+
11
+ Given /The "(.*)" field is "(.*)"/i do |name, value|
12
+ field = getTextField(@browser, name)
13
+
14
+ if field then
15
+ field.value.should == value
16
+ else
17
+ fail("could not find the '#{name}' field on #{@browser.url}")
18
+ end
19
+ end
20
+
21
+ # Synonyms
22
+ Given /set "(.*)" to "(.*)"/i do |type,text|
23
+ Given "I put \"#{text}\" in the \"#{type}\" field"
24
+ end
25
+
26
+ Given /The "(.*)" field is blank/i do |field|
27
+ Given "The \"#{field}\" field is \"\""
28
+ end
29
+
30
+ Given /The value of "(.*)" becomes "(.*)"/i do |field, value|
31
+ Given "The \"#{field}\" field is \"#{value}\""
32
+ end
33
+
34
+ # Try running with the text_field ":id", ":name", ":value", ":index" or ":class" element attribute.
35
+ # Does not matter what you select!
36
+ # The proper watir code will be executed regardless.
37
+
38
+ def getTextField(browser, type)
39
+ # Build an array of all potential fields
40
+ fields = []
41
+ # By ID
42
+ browser.text_fields().each { | field |
43
+ if field.id == type then fields.push field end
44
+ }
45
+ # By Name
46
+ browser.text_fields().each { | field |
47
+ if field.respond_to?('htmlname') then
48
+ if field.htmlname == type then fields.push field end
49
+ else
50
+ if field.name == type then fields.push field end
51
+ end
52
+ }
53
+ # By the associated <label>
54
+ browser.elements_by_xpath("//label[.='#{type}']").each { | label |
55
+ if label.for then
56
+ browser.text_fields().each { | field |
57
+ if field.id == label.for then fields.push field end
58
+ }
59
+ end
60
+ }
61
+ # By value
62
+ #fields += browser.text_fields(:value, type)
63
+ # By CSS Class
64
+ #fields += browser.text_fields(:class, type)
65
+
66
+ # Return the first visible one
67
+ fields.each { | field |
68
+ if field.visible? then return field end
69
+ }
70
+ return nil
71
+ end
data/support/env.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'spec'
2
+ VERSION = "0.1.0"
3
+
4
+
5
+ $killFF = false
6
+
7
+ if ENV['BROWSERSALAD_BROWSER'] and ENV['BROWSERSALAD_BROWSER'].downcase == 'safari'
8
+ require 'safariwatir'
9
+ Browser = Watir::Safari
10
+ else
11
+ case RUBY_PLATFORM
12
+ when /darwin/
13
+ require 'firewatir'
14
+
15
+ class FireWatir::Firefox
16
+ # Modified implementation of Firewatir start-up to give it up to 30 seconds to get things sorted
17
+ def initialize(options = {})
18
+ if(options.kind_of?(Integer))
19
+ options = {:waitTime => options}
20
+ end
21
+
22
+ # check for jssh not running, firefox may be open but not with -jssh
23
+ # if its not open at all, regardless of the :suppress_launch_process option start it
24
+ # error if running without jssh, we don't want to kill their current window (mac only)
25
+ jssh_down = false
26
+ begin
27
+ set_defaults()
28
+ rescue Watir::Exception::UnableToStartJSShException
29
+ jssh_down = true
30
+ end
31
+
32
+ if current_os == :macosx && !%x{ps x | grep firefox-bin | grep -v grep}.empty?
33
+ raise "Firefox is running without -jssh" if jssh_down
34
+ open_window unless options[:suppress_launch_process]
35
+ elsif not options[:suppress_launch_process]
36
+ launch_browser(options)
37
+ end
38
+
39
+ set_defaults(300)
40
+ get_window_number()
41
+ set_browser_document()
42
+ end
43
+
44
+ def set_defaults(no_of_tries = 3)
45
+ no_of_tries_so_far = 0
46
+ # JSSH listens on port 9997. Create a new socket to connect to port 9997.
47
+ begin
48
+ $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
49
+ $jssh_socket.sync = true
50
+ read_socket()
51
+ rescue
52
+ no_of_tries_so_far += 1
53
+ sleep 0.1
54
+ retry if no_of_tries_so_far < no_of_tries
55
+ raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
56
+ end
57
+ @error_checkers = []
58
+ end
59
+ end
60
+
61
+ Watir::Browser.default = 'firefox'
62
+ Browser = Watir::Browser
63
+ $killFF = true
64
+ when /win32|mingw/
65
+ require 'watir'
66
+ Browser = Watir::IE
67
+ when /java/
68
+ require 'celerity'
69
+ Browser = Celerity::Browser
70
+ else
71
+ raise "This platform is not supported (#{PLATFORM})"
72
+ end
73
+ end
74
+
75
+ # Set up
76
+ $browser = Browser.new
77
+
78
+ if ENV['BROWSERSALAD_URL'] then
79
+ $baseURL = ENV['BROWSERSALAD_URL']
80
+ if not $baseURL.match(/\/$/) then
81
+ $baseURL += '/'
82
+ end
83
+ else
84
+ $baseURL = "http://demo.silverstripe.com/"
85
+ end
86
+
87
+ # Make it go fast - IE only
88
+ # $browser.speed = :zippy
89
+
90
+ # Before each scenario, load in the globals
91
+ Before do
92
+ @browser = $browser
93
+ @baseURL = $baseURL
94
+ end
95
+
96
+ at_exit do
97
+ # Let us see the aftermath for 10 seconds
98
+ sleep 10
99
+
100
+ # Kill database
101
+ $browser.goto $baseURL + 'dev/tests/endsession'
102
+
103
+ if $killFF then `killall -9 firefox-bin` end
104
+ end
@@ -0,0 +1,46 @@
1
+ require 'firewatir'
2
+
3
+ class Element
4
+ # Returns true if the current elements returns true
5
+ # Feature missing from FireWatir
6
+ def visible?
7
+ js = "(function(el) {
8
+ var w = getWindows()[#{$browser.window_index}].content;
9
+ while(el && el != w.document) {
10
+ var s = w.getComputedStyle(el, null);
11
+ if(s.getPropertyValue('display') == 'none' || s.getPropertyValue('visibility') == 'hidden') return false;
12
+ el = el.parentNode;
13
+ };
14
+ return true;
15
+ })(#{element_object});"
16
+ return js_eval(js) == 'true'
17
+ end
18
+ end
19
+
20
+ class FireWatir::Firefox
21
+ def evaluate_script(script)
22
+ # Warning: don't use // comments in the JS, only /* */, because newlines are removed
23
+
24
+ outerScript = <<-eos
25
+ (function(window) {
26
+ #{script};
27
+ })(getWindows()[#{@window_index}].content);
28
+ eos
29
+
30
+ return self.js_eval(outerScript)
31
+ end
32
+
33
+ def evaluate_script_alternate(script)
34
+ # Warning: don't use // comments in the JS, only /* */, because newlines are removed
35
+
36
+ outerScript = <<-eos
37
+ var _old_window = window;
38
+ var window = getWindows()[#{@window_index}].content;
39
+ #{script};
40
+ window = _old_window;
41
+ delete _old_window;
42
+ eos
43
+
44
+ return self.js_eval(outerScript)
45
+ end
46
+ end
@@ -0,0 +1,167 @@
1
+ =begin
2
+
3
+ This is a collection of modifications to SafariWatir to get it to work more consistently with the
4
+ Watir API
5
+
6
+ =end
7
+
8
+ require 'safariwatir'
9
+
10
+ module Watir
11
+ module Container
12
+ # Implement missing API method - all text fields
13
+ def text_fields()
14
+ return @scripter.get_all_text_fields
15
+ end
16
+
17
+ # Implement missing API method - all elements matching an xpath
18
+ def elements_by_xpath(xpath)
19
+ return @scripter.get_all_by_xpath(xpath)
20
+ end
21
+
22
+ class HtmlElement
23
+ # Implement missing API methods - id, for, and name on all HTML elements
24
+ def id
25
+ return attr('id') || ''
26
+ end
27
+ def for
28
+ return attr('for') || ''
29
+ end
30
+ # This htmlname rather than name because name seems to have a special meaning in some versions of Ruby.
31
+ def htmlname
32
+ return @scripter.get_attribute('name', self) || ''
33
+ end
34
+ def value
35
+ return @scripter.get_value_for(self) || ''
36
+ end
37
+
38
+ # Implement missing API method - is the element visible?
39
+ def visible?
40
+ return @scripter.eval_on_element("return (function(el) {
41
+ while(el && el != document) {
42
+ var s = window.getComputedStyle(el, null);
43
+ if(s.getPropertyValue('display') == 'none' || s.getPropertyValue('visibility') == 'hidden') return false;
44
+ el = el.parentNode;
45
+ };
46
+ return true;
47
+ })(element);", self)
48
+ end
49
+ end
50
+
51
+ class TextField
52
+ # Altered API method for consistency - char-by-char setting of the value mucked with onchange
53
+ # handlers
54
+ def set(value)
55
+ @scripter.focus(self)
56
+ @scripter.highlight(self) do
57
+ clear_text_input
58
+ append_text_input(value.to_s)
59
+ end
60
+ @scripter.blur(self)
61
+ end
62
+ end
63
+ end
64
+
65
+ class Safari
66
+ # Added missing API method - evaluate an arbitrary script
67
+ def evaluate_script(script)
68
+ return @scripter.evaluate_script(script)
69
+ end
70
+ end
71
+
72
+ class AppleScripter
73
+ # Internal handler for Safari class to access
74
+ def evaluate_script(script)
75
+ return execute(script)
76
+ end
77
+
78
+ # Internal handler for Safari class to access
79
+ def get_all_text_fields()
80
+ ids = get_ids_from_js("document.getElementsByTagName('input')", "item.type == 'text' || item.type == 'password'") + get_ids_from_js("document.getElementsByTagName('textarea')")
81
+ return ids.map { |id|
82
+ Container::TextField.new(self, :id, id)
83
+ }
84
+ end
85
+
86
+ # Internal handler for Safari class to access
87
+ def get_all_by_xpath(xpath)
88
+ xpath = xpath.gsub("'","\'")
89
+ ids = get_ids_from_js("document.evaluate(\"#{xpath}\", document.documentElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)",
90
+ nil,
91
+ "items.snapshotLength",
92
+ "items.snapshotItem(i)")
93
+ return ids.map { |id|
94
+ Container::HtmlElement.new(self, :id, id)
95
+ }
96
+ end
97
+
98
+ # Return the IDs of the elements returned by the given JS expression
99
+ # js: The JavaScript that returns a list of items
100
+ # filter: The JavaScript to evaluate on each item (variable name will be 'item') to filter the
101
+ # list. This is optional
102
+ # getLength: Expression to get the length of the JS var 'items'
103
+ # getItem: Expression to get the 'i'th of the JS var 'items'
104
+ def get_ids_from_js(js, filter = nil, getLength = "items.length", getItem = "items[i]")
105
+ if filter == nil
106
+ filter = "true"
107
+ end
108
+
109
+ fullJS = <<-eos
110
+ return (function() {
111
+ var items = #{js};
112
+ var i, item;
113
+ var output = [];
114
+ for(i=0;i<#{getLength};i++) {
115
+ item = #{getItem};
116
+ if(#{filter}) {
117
+ // Adding a random ID is a little clumsy but will do, for now
118
+ if(!item.id) item.id = 'safariwatir-' + parseInt(Math.random()*1000000000);
119
+ output.push(item.id);
120
+ }
121
+ }
122
+ return output;
123
+ })()
124
+ eos
125
+
126
+ return eval_js(fullJS)
127
+ end
128
+
129
+ # Internal handler for HtmlElement class - Evaluate JS on a particular element
130
+ def eval_on_element(js, element)
131
+ execute(element.operate { js }, element)
132
+ end
133
+
134
+ # Altered API methdo - fix bugs in match by ID and Value
135
+ def handle_match(element, how = nil)
136
+ how = {:text => "text", :url => "href", :id => 'id', :value => 'value', :xpath => 'xpath'}[element.how] unless how
137
+ case element.what
138
+ when Regexp
139
+ %|#{how}.match(/#{element.what.source}/#{element.what.casefold? ? "i":nil})|
140
+ when String
141
+ %|#{how} == '#{element.what}'|
142
+ else
143
+ raise RuntimeError, "Unable to locate #{element.element_name} with #{element.how}"
144
+ end
145
+ end
146
+
147
+ # Altered internal API method - need to wait for the URL displayed to actually change, so that
148
+ # we can handle slow-loading pages better
149
+ def navigate_to(url, &extra_action)
150
+ page_load(extra_action) do
151
+ currentURL = @document.URL.get
152
+ @document.URL.set(url)
153
+
154
+ if currentURL != url
155
+ 60.times do |tries|
156
+ if @document.URL.get != currentURL
157
+ break
158
+ else
159
+ sleep 0.5
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ end
167
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: browsercuke
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - "Sam Minn\xC3\xA9e"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-07 00:00:00 +13:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: cucumber
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.4.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: firewatir>= 1.6.5
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: safariwatir>= 0.3.7
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rb-appscript>= 0.5.3
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: hoe
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 2.4.0
64
+ version:
65
+ description: |-
66
+ BrowserCuke is a layer of browser-based testing on top of Cucumber. It provides an intuitive way of
67
+ writing business-readable tests for your web applications, that use real web browsers to test. Your
68
+ test scripts can now be a way of communicating with your client how you have tested the application
69
+ and how it should work.
70
+
71
+ For more information about Cucumber, see [the cucumber website](http://cukes.info).
72
+
73
+ BrowserCuke currently uses Watir to perform the browser automation, although it may be extended in
74
+ the future to use something like WebRat to test applications sans-JavaScript.
75
+ email:
76
+ - sam@silverstripe.com
77
+ executables:
78
+ - browsercuke
79
+ - browsercuke-setup
80
+ - sapphirecuke
81
+ extensions: []
82
+
83
+ extra_rdoc_files: []
84
+
85
+ files:
86
+ - .gitignore
87
+ - README.md
88
+ - bin/browsercuke
89
+ - bin/browsercuke-setup
90
+ - bin/sapphirecuke
91
+ - step_definitions/browser/README.md
92
+ - step_definitions/browser/ajax.rb
93
+ - step_definitions/browser/buttons.rb
94
+ - step_definitions/browser/checkboxes.rb
95
+ - step_definitions/browser/expectations.rb
96
+ - step_definitions/browser/images.rb
97
+ - step_definitions/browser/links.rb
98
+ - step_definitions/browser/pages.rb
99
+ - step_definitions/browser/popups.rb
100
+ - step_definitions/browser/radio_buttons.rb
101
+ - step_definitions/browser/select_lists.rb
102
+ - step_definitions/browser/text_fields.rb
103
+ - support/env.rb
104
+ - support/firewatir-mods.rb
105
+ - support/safariwatir-mods.rb
106
+ has_rdoc: true
107
+ homepage: http://github.com/sminnee/browsercuke
108
+ licenses: []
109
+
110
+ post_install_message:
111
+ rdoc_options:
112
+ - --main
113
+ - README.md
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ version:
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: "0"
127
+ version:
128
+ requirements: []
129
+
130
+ rubyforge_project: browsercuke
131
+ rubygems_version: 1.3.5
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: BrowserCuke is a layer of browser-based testing on top of Cucumber
135
+ test_files: []
136
+