browsercuke 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+