honey-do 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,11 @@
1
+ Copyright (c) 2008 Tim Camper
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing,
8
+ software distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+
11
+ See the License for the specific language governing permissions and limitations under the License.
data/README ADDED
@@ -0,0 +1,120 @@
1
+ =Just Give it a List!
2
+
3
+ ====Collection-driven, commandless Selenium
4
+ HoneyDo is a small Ruby/Javascript library that supports collection-driven browser automation.
5
+ Currently implemented as a Selenium RC user extension, it provides a simple, consistent interface to any list of 1 or more HTML form elements.
6
+
7
+ Basically, you set one or many values, click one or many controls, or read the whole form with a single call, always using the same sweet syntax.
8
+
9
+ ==When would I use this?
10
+
11
+ ====When you have lots of testing to do on:
12
+ * many forms
13
+ * large forms
14
+ * complex forms
15
+ * dynamic forms
16
+ HoneyDo was born of the need to test a large, input-heavy app with over 350 Oracle tables at the backend and over 100 input screens in the front.
17
+ The business domain and UI workflows are complex, so the tools for making the functional tests needed to be as lightweight and flexible as possible.
18
+ Things like GUI maps and screen driver objects are heavyweight, rigid, and brittle: the amount of maintenance they require keeps you from getting your tests right.
19
+
20
+ ====When data matters, but widgets don't
21
+ In testing app functionality through the UI, it often doesn't make a difference if you _type_, _select_, _check_, or _click_: you just need to get values to the server.
22
+
23
+ ==Examples
24
+ ====With a SeleniumDriver . . .
25
+ browser = Selenium::SeleniumDriver.new('localhost', 44...
26
+ ====Click things
27
+ # submit your form
28
+ browser.set_form_values(:saveButton)
29
+
30
+ # toggle a bunch of checkboxes and submit
31
+ browser.set_form_values( [:include_documents, :calculate_fee, :require_guarantor_address, :fasb_13_ok, :120_day_rule, :UPDATE] )
32
+
33
+ ====Set things
34
+ browser.set_form_values(:user_name => "pprubens", :password => "hy!Bosch53xy")
35
+
36
+ browser.set_form_values(:state => "TN", :street_1 => "9113 Foo St.", :street_2 => "4D", :city => "Erlewine", :postal => 99999, :same_as_shipping => true)
37
+
38
+ ====Read things
39
+ default_values = browser.get_form_values("newProprietorshipForm")
40
+ assert_equal({:state => "AL",
41
+ :typeOfBusiness => "Please Select",
42
+ :taxjurisdiction => "Federal",
43
+ :corporation => "on",
44
+ :submitButton => "Save",
45
+ :cancel => "Cancel"}, default_values, "only default fields populated?")
46
+
47
+ visible_fields = browser.get_form_elements("newProprietorshipForm").split(',')
48
+ assert_equal(visible_fields.size - default_values.size, 17, "count of unset fields on initial page load")
49
+
50
+ ==How does it work?
51
+ The key to HoneyDo#set_form_values is that each HTML <tt>form</tt> element knows its own type. If we identify the element, the code knows what to do.
52
+
53
+ For each element in our input list:
54
+ ====1 - find the element
55
+ The <tt>form.elements</tt> collection is a JavaScript associative array, so we can use either its array index or its <tt>name</tt> attribute.
56
+ For a generic js example, these will all have the same effect if <tt>selectDealer</tt> is the 3rd element:
57
+ element = myForm.elements['selectDealer'];
58
+ element = myForm.elements[2];
59
+ element = myForm.elements['2'];
60
+
61
+ ====2 - get its type
62
+ element.type
63
+ ====3 - set the value (or call <tt>click()</tt>)
64
+ The +type+ could be:
65
+
66
+ settable: <tt>(text, textarea, password)</tt>:: The +value+ attribute is set
67
+ selectable: <tt>(select-one, select-mulitple)</tt>:: The +option+ index is found and the +selected+ attribute is set
68
+ clickable:<tt>(button, submit, reset, radio)</tt>:: <tt>click()</tt> is called.
69
+ checkbox: <tt>(checkbox)</tt>:: the +checked+ attribute is set or <tt>click()</tt> is called
70
+
71
+ When <tt>element.type</tt> has no value, then +element+ is itself a collection of other elements, i.e., a group of checkboxes or radio buttons with the same +name+.
72
+ In this case <tt>click()</tt> is called on the correct item in the goup.
73
+
74
+ ====4 - fire event handlers
75
+ Any or all of these are fired, if present:
76
+ onclick()
77
+ onchange()
78
+ onblur()
79
+
80
+ ====Ajax
81
+ Nothing explicit is done to wait for Ajax calls. On HoneyDo's home app, we find that in IE the built-in <tt>retry</tt> is enough to handle the occasional glitch.
82
+ In Firefox, multiple call mode using the <tt>:one_field_at_a_time</tt> option is required, but does in fact work.
83
+ We've tried the <i>prototype.js</i> <tt>Ajax.activeRequestCount</tt> trick, but couldn't figure out how get a reference to the right <tt>Ajax</tt> instance.
84
+
85
+ I make no guarantees about how this will work with _your_ Ajax calls, but I'd suggest trying <tt>:one_field_at_a_time => true</tt> for your Ajax pages and just leaving it alone otherwise.
86
+ If you need an explicit _wait_ and have the solution, JavaScript <tt>Form_.prototype.set_value()</tt> would be the place to call it from.
87
+
88
+ ==Installation[link:files/doc/INSTALLATION.html]
89
+ There's a manual step[link:files/doc/INSTALLATION.html].
90
+
91
+ ==The Implementation
92
+ Selenium user extensions that aren't implemented as methods on the <tt>Selenium</tt> JavaScript prototype object might be odd.
93
+ However, some of these methods require pre- or post-processing in Ruby, so I couldn't think of any other way to do it.
94
+
95
+ I'm always open to better solutions. I'm more interested in propagating the list-driven technique wherever it's applicable than in preserving any particular encoding of it.
96
+
97
+ Blessings upon whoever came up with <tt>SeleniumDriver.get_eval()</tt>!
98
+
99
+ ==The Tests[link:/classes/TestGeneralInputOptions.html] ...
100
+ ... are the *documentation*. They detail the features and limitations.
101
+ Run[link:/files/doc/running_the_tests_rdoc.html] them and read[link:/classes/TestGeneralInputOptions.html] them.
102
+
103
+ ====See running_the_tests[link:/files/doc/running_the_tests_rdoc.html]
104
+
105
+ ==Why be collection-driven?
106
+
107
+ ====More testing, less code
108
+ As a functional test automator and automation lead, I want my scripts, scripters, and self to do more of the testing work that matters,
109
+ in less time, with less code to distract from finding bugs.
110
+
111
+ At run time, scripts go faster[link:/classes/TestFastAndPretty.html] when all of the fields are set or read in one go, rather than with an individual Selenium call for each field.
112
+ At development and maintenance time, the code is streamlined considerably by removing the usually unnecessary layer of "action" commands. And the
113
+ scripters' work goes faster when there is only one API to learn, as opposed to many different ones for all of the different widget types.
114
+
115
+ ====User emulation is waste
116
+ Named actions (_click_, _select_, _toggle_, etc) that require us to identify the type of control being called and to specify the arguments to each command differently derive from
117
+ the assumption that functional test automation implies or requires end-user emulation. Emulating human users and testing app functionality are two
118
+ different programming problems, and while user emulation is certainly possible it rarely adds anything but clutter, confusion, and complexity to the
119
+ functional testing effort. Unless there is an explicit testing need to make assertions about the mechanical details of interacting with widgets in
120
+ type-specific ways, then we are better off without named actions.
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+ require 'rake/rdoctask'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require 'open-uri'
5
+
6
+ desc "run all tests"
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'functional_testcase' << 'test'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = false
11
+ end
12
+ task :test => [:selenium, :webrick]
13
+
14
+ desc "verify selenium is running"
15
+ task :selenium do
16
+ error = nil
17
+ begin
18
+ open("http://localhost:4444")
19
+ rescue Exception => ex
20
+ error = ex.message
21
+ end
22
+ raise RuntimeError.new("SELENIUM IS NOT RUNNING:\n#{error}") if error !~ /403 /
23
+ end
24
+
25
+ desc "verify webrick demo server is running"
26
+ task :webrick do
27
+ error = nil
28
+ begin
29
+ open("http://localhost:8000")
30
+ rescue Exception => ex
31
+ error = ex.message
32
+ end
33
+ raise RuntimeError.new("WEBRICK SERVER IS NOT RUNNING:\n#{error}") if error
34
+ end
35
+
36
+ RDOC_OPTS = ["--all" , "--quiet" , "--line-numbers" , "--inline-source",
37
+ "--main", "README",
38
+ "--title", "Honey-Do: Just give it a list!"]
39
+ XTRA_RDOC = %w{README COPYING js/user-extensions.js doc/INSTALLATION doc/running_the_tests.rdoc}
40
+
41
+ Rake::RDocTask.new do |rd|
42
+ rd.rdoc_dir = "doc/rdoc"
43
+ rd.rdoc_files.include("**/*.rb")
44
+ rd.rdoc_files.add(XTRA_RDOC)
45
+ rd.options = RDOC_OPTS
46
+ end
47
+
48
+ spec = Gem::Specification.new do |s|
49
+ s.name = 'honey-do'
50
+ s.version = '0.5.0'
51
+ s.rubyforge_project = s.name
52
+
53
+ s.platform = Gem::Platform::RUBY
54
+ s.has_rdoc = true
55
+ s.extra_rdoc_files = XTRA_RDOC + FileList["test/*.rb", "functional_testcase/*.rb", "demo/**/*.rb"].to_a
56
+ s.rdoc_options += RDOC_OPTS
57
+ s.summary = "collection driven, commandless form handling for Selenium"
58
+ s.description = s.summary
59
+ s.author = "Tim Camper"
60
+ s.email = 'twcamper@thoughtworks.com'
61
+ s.homepage = 'http://honey-do.rubyforge.org/'
62
+
63
+ s.add_dependency('Selenium', '>= 1.0.1')
64
+ s.required_ruby_version = '>= 1.8.2'
65
+
66
+ s.files = %w(COPYING README Rakefile) +
67
+ FileList["lib/*.rb", "test/*.rb", "demo/**/*", "functional_testcase/*.rb", "bin/*"].to_a
68
+
69
+ s.require_path = "lib"
70
+ end
71
+
72
+ Rake::GemPackageTask.new(spec) do |p|
73
+ p.need_zip = true
74
+ p.need_tar = true
75
+ p.gem_spec = spec
76
+ end
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ REM SAMPLE windows command line
3
+ REM your paths, Sel version and options may vary
4
+ java -jar ..\..\selenium-remote-control-0.9.2\server\selenium-server.jar -multiWindow -userExtensions ..\js\user-extensions.js
@@ -0,0 +1,3 @@
1
+ # SAMPLE command line
2
+ # your paths, Sel version and options may vary
3
+ java -jar ../../selenium-remote-control-0.9.2/server/selenium-server-ff3.jar -multiWindow -userExtensions ../js/user-extensions.js
@@ -0,0 +1,112 @@
1
+ <html>
2
+ <head>
3
+ <title>HoneyDo Demo Form: Just give it a list!</title>
4
+ <script language="JavaScript" src="everything.js"></script>
5
+ </head>
6
+ <body>
7
+ <!-- A one-of-everything HTML form:
8
+ after David Flanagan, Chapter 18 of "JavaScript: The Definitive Guide", O'Reilly, 5th edition
9
+ -->
10
+ <div align="center">
11
+ <form name="everything" method="POST" action="../display">
12
+ <table border="3" bgcolor="beige" cellspacing="1" cellpadding="4">
13
+ <tr>
14
+ <td colspan="4"><h4>All Possible Form Elements</h4></td>
15
+ </tr>
16
+ <tr>
17
+ <td>Username:<br>[1]<input type="text" name="username" size="15"></td>
18
+ <td>Password:<br>[2]<input type="password" name="password" size="15"></td>
19
+ <td rowspan="4">Input Events[3]<br>
20
+ <textarea name="event_reports_area" rows="20" cols="40"></textarea></td>
21
+ <td rowspan="4" align="center" valign="center">
22
+ [10]<input type="button" value="Clear" name="clearButton"><br>
23
+ [11]<input type="submit" value="Submit" name="submitButton"><br>
24
+ [12]<input type="reset" value="Reset" name="resetButton"><br>
25
+ [13]<input type="hidden" value="you can't see this unless you're looking at the source" name="justHiding"><br></td>
26
+ </tr>
27
+ <tr>
28
+ <td colspan="2">
29
+ Filename: [4]<input type="file" name="file" size="15"> </td>
30
+ </tr>
31
+ <tr>
32
+ <td><p>My Computer Peripherals:</p>
33
+ [5]<input type="checkbox" name="extras" value="burner">DVD Writer<br>
34
+ [5]<input type="checkbox" name="extras" value="printer">Printer<br>
35
+ [5]<input type="checkbox" name="extras" value="card">Card Reader<br>
36
+ <p>My Utilities:</p>
37
+ [6]<input type="checkbox" name="disk manager">Disk Manager<br>
38
+ [6]<input type="checkbox" name="hackzall">Firewall Drill Pro<br>
39
+ [6]<input type="checkbox" name="a-clock.exe">AtomSynch<br>
40
+ [6]<input type="checkbox" name="version control client">Sottoversione 1.4.3<br></td>
41
+ <td><p>My Web Browser:</p>
42
+ [7]<input type="radio" name="browser" value="ff">Firefox<br>
43
+ [7]<input type="radio" name="browser" value="ie">Internet Explorer<br>
44
+ [7]<input type="radio" name="browser" value="safari">Safari<br>
45
+ [7]<input type="radio" name="browser" value="opera">Opera<br>
46
+ [7]<input type="radio" name="browser" value="other browser">other<br></td>
47
+ </tr>
48
+ <tr>
49
+ <td>My Hobbies:[8]<br>
50
+ <select multiple="multiple" name="hobbies" size="5">
51
+ <option value="programming">Hacking JavaScript
52
+ <option value="surfing">Surfing the Web
53
+ <option value="caffeine">Drinking Coffee
54
+ <option value="annoying">Annoying my friends
55
+ <option value="zipping">Zipping around on my scooter
56
+ </select></td>
57
+ <td align="center" valign="center">My Favorite Color:<br>[9]
58
+ <select name="color">
59
+ <option value="0">
60
+ <option value="red">Red
61
+ <option value="orange">Orange
62
+ <option value="yellow">Yellow
63
+ <option value="green">Green
64
+ <option value="blue">Blue
65
+ <option value="indigo">Indigo
66
+ <option value="violet">Violet
67
+ <option value="black">Black
68
+ <option value="brown">Brown
69
+ <option value="white">White
70
+ <option value="grey">Grey
71
+ <option value="light grey">Light Grey
72
+ <option value="flat eggshell white">Off White
73
+ <option value="taupe">Taupe
74
+ <option value="beige">Beige
75
+ <option value="10256">Mauve
76
+ <option value="peach">Peach
77
+ <option value="purple">Purple
78
+ <option value="maroon">Maroon
79
+ <option value="fuchsia">Fuchsia
80
+ <option value="silver">Silver
81
+ <option value="gold">Gold
82
+ <option value="zinc">Zinc
83
+ </select></td>
84
+ </tr>
85
+ </table>
86
+ </form>
87
+ <table border="9" bgcolor="beige" cellspacing="1" cellpadding="4">
88
+ <tr>
89
+ <td colspan="7"><h4>Form Element Types</h4></td>
90
+ </tr>
91
+ <tr>
92
+ <td>[1] text</td> <td>[2] password</td> <td>[3] textarea</td>
93
+ <td>[4] file (upload)</td> <td>[5] checkbox (group)</td> <td colspan="2">[6] checkbox (independent)</td>
94
+ </tr>
95
+ <tr>
96
+ <td>[7] radio</td> <td>[8] select (multiple)</td>
97
+ <td>[9] select</td> <td>[10] button</td>
98
+ <td>[11] submit</td> <td>[12] reset</td><td>[13] hidden</td>
99
+ </tr>
100
+ </table>
101
+ <form name="events_button_form">
102
+ <table border="3" bgcolor="beige" cellspacing="1" cellpadding="4">
103
+ <tr><td> <h4>Turn on Event Handlers</h4></td></tr>
104
+ <tr><td>
105
+ <input type="button" value="Enable Handlers" name="enableEventsButton" onclick="addhandlers(document.everything)">
106
+ <input type="button" value="Disable Handlers" name="disableEventsButton" onclick="removehandlers(document.everything)">
107
+ </td>
108
+ </tr>
109
+ </table>
110
+ </form>
111
+ </div>
112
+ </body>
@@ -0,0 +1,55 @@
1
+ /*
2
+ functions for "A one-of-everything HTML form":
3
+ from David Flanagan, Chapter 18 of "JavaScript: The Definitive Guide", O'Reilly, 5th edition
4
+ */
5
+
6
+ // This generic function appends details of an event to the big Textarea
7
+ // element in the form above. It is called from various event handlers.
8
+ function report(element, event) {
9
+ if (selectable(element.type)) {
10
+ value = "";
11
+ for(var i = 0; i < element.options.length; i++)
12
+ if (element.options[i].selected)
13
+ value += element.options[i].value + " ";
14
+ }
15
+ else if (element.type == "textarea") value = "...";
16
+ else value = element.value;
17
+
18
+ var msg = event + ":" + element.name + ' (' + value + ')\n';
19
+ var t = element.form.event_reports_area;
20
+ t.value = t.value + msg;
21
+
22
+ }
23
+ // This function adds a bunch of event handlers to every element in a form.
24
+ // Note that the event handlers all call report().
25
+
26
+ // We are defining event handlers by assigning functions to the properties
27
+ // of JavaScript objects rather than by assigning strings to the attributes
28
+ // of HTML elements.
29
+ function addhandlers(f) {
30
+ for(var i = 0; i < f.elements.length; i++) {
31
+ var e = f.elements[i];
32
+ e.onclick = function() {report(this, 'Click');}
33
+ e.onchange = function() {report(this, 'Change');}
34
+ e.onfocus = function() {report(this, 'Focus');}
35
+ e.onblur = function() {report(this, 'Blur');}
36
+ }
37
+
38
+ // Special case event handlers for the 3 buttons
39
+ f.clearButton.onclick = function() { this.form.event_reports_area.value = ''; report(this, 'Click'); }
40
+ f.submitButton.onclick = function() { report(this, 'Click'); return false;}
41
+ f.resetButton.onclick = function() { this.form.reset(); report(this, 'Click'); return false; }
42
+ }
43
+
44
+ function removehandlers(f) {
45
+ for(var i = 0; i < f.elements.length; i++) {
46
+ var e = f.elements[i];
47
+ e.onchange = null;
48
+ e.onfocus = null;
49
+ e.onblur = null;
50
+ if (e.name == "clearButton") continue;
51
+ e.onclick = null;
52
+ }
53
+ }
54
+
55
+ function selectable(type) { return (/^select/i).test(type);}
@@ -0,0 +1,58 @@
1
+ require 'webrick'
2
+
3
+ class HoneyDoDemoServlet < WEBrick::HTTPServlet::AbstractServlet
4
+
5
+ def do_POST(request, response)
6
+ status, content_type, body = format_form_values(request)
7
+
8
+ response.status = status
9
+ response['Content-Type'] = content_type
10
+ response.body = body
11
+ end
12
+
13
+ # makes a document with a table of submitted form values from any POST request body.
14
+ def format_form_values(request)
15
+ html = "<html><head><title>#{Time.now.strftime('%A %B %d, %H:%M:%S')}</title></head>"
16
+ html += "\n<body><table border='3' bgcolor='beige' cellpadding='3' cellspacing = '2' id='form_values_table'>"
17
+ html += "\n<h2>Submitted from: &nbsp;<span>#{request.header['referer'].first}</span></h2>"
18
+
19
+ parse_query(request.body).sort.collect do | pair |
20
+ html += write_row(*pair)
21
+ end
22
+
23
+ html += "\n</table></body></html>"
24
+
25
+ return 200, "text/html", html
26
+ end
27
+
28
+ # <pre> the value cell to get newlines
29
+ def write_row(name, value)
30
+ "\n<tr><td>#{name}:&nbsp;</td><td id='#{name}'><pre>#{value}</pre></td></tr>"
31
+ end
32
+
33
+ # For some reason WEBrick doesn't append multiple values for checkboxes and multi-selects, thus this hack.
34
+ def parse_query(body)
35
+ query = {}
36
+ body.split(/[;&]/).each do |form_item|
37
+ key, value = form_item.split(/=/)
38
+ key = WEBrick::HTTPUtils::unescape_form(key)
39
+ value = WEBrick::HTTPUtils::unescape_form(value.to_s)
40
+ if query[key]
41
+ query[key] += "|" + value
42
+ else
43
+ query[key] = value
44
+ end
45
+ end
46
+ return query
47
+ end
48
+ end
49
+
50
+ if $0 == __FILE__ then
51
+ server = WEBrick::HTTPServer.new(:Port => 8000,
52
+ :Host => 'localhost',
53
+ :DocumentRoot => Dir::pwd + '/page')
54
+
55
+ server.mount "/display", HoneyDoDemoServlet
56
+ trap "INT" do server.shutdown end
57
+ server.start
58
+ end
data/doc/INSTALLATION ADDED
@@ -0,0 +1,20 @@
1
+ === 1 - Have SeleniumRC[http://selenium-rc.seleniumhq.org/]
2
+ === 2 - Get the gem
3
+ gem install honey-do
4
+ === 3 - Move user-extensions.js
5
+ 1. If you're already using a <tt>user-extensions.js</tt>, then merge the contents of <tt>honey-do/js/user-extensions.js</tt> in with yours.
6
+ 1. Otherwise, move or copy <tt>honey-do/js/user-extensions.js</tt> to a folder convenient to your selenium proxy server.
7
+ 1. Start your selenium proxy with the <tt>-userExtensions</tt> option. For example, if your working directory contains both <tt>user-extensions.js</tt> and the <tt>selenium-remote-control-0.9.2</tt> folder:
8
+
9
+ java -jar selenium-remote-control-0.9.2\server\selenium-server.jar -userExtensions user-extensions.js
10
+
11
+ See <tt>honey-do/bin</tt> for other cmd line examples.
12
+
13
+ See also SeleniumRC command line reference[http://selenium-rc.seleniumhq.org/options.html]
14
+ === 4 - +require+ / +include+
15
+ require 'honey_do'
16
+ require 'selenium'
17
+
18
+ <b>Nothing to +include+</b>.
19
+
20
+ See <tt>functional_testcase/functional_testcase.rb</tt> and <tt>test/test_master.rb</tt> for examples.
@@ -0,0 +1,81 @@
1
+ =Running The Tests
2
+
3
+ ===Once you've installed[link:/files/doc/INSTALLATION.html]
4
+
5
+ ====1 - Start your selenium proxy server
6
+ Make sure the honey_do version of <i>user-extensions.js</i> gets loaded. An example, minimal command line would be:
7
+ java -jar .\selenium-remote-control-0.9.2\server\selenium-server.jar -userExtensions user-extensions.js
8
+
9
+ See also <i>honey-do/bin</i> for other examples.
10
+
11
+ ====2 - Start honey-do/demo/webrick-servlet.rb
12
+ ruby webrick-servlet.rb # you don't need to say 'ruby' on win32
13
+
14
+ You'll then have a webserver listening at http://localhost:8000
15
+
16
+ ====3 - Run honey-do/Rakefile as
17
+ honey-do/rake test
18
+
19
+ This gathers and runs all files ending in <i>_test.rb</i>, and an extended version of Test::Unit::TestCase handles starting and stopping the Selenium driver.
20
+ At the end, it will print to <tt>stdout</tt> a list of tests run, with run times. A few of the tests print things as part of their illustrative mission.
21
+
22
+ Run just the tests in a given file at the command line:
23
+ ruby fast_and_pretty_test.rb # no need for 'ruby' on win32
24
+
25
+ Run individual tests or sub-suites at the command line like this:
26
+ ruby checkbox_test.rb test_check_multiple_independents
27
+ ruby checkbox_test.rb test_check_uncheck test_check_multiple_in_group test_check_multiple_independents
28
+
29
+ ====The Demo Page
30
+ <tt>everything.html</tt> is a contrived example form, with all of the possible element types represented (thanks to David Flanagan).
31
+ There are event handlers to prove that HoneyDo fires them, but there is no Ajax.
32
+
33
+ ====The Servlet
34
+ HoneyDoDemoServlet generates a display page showing whatever form data was submitted, but only for POST requests. (HoneyDo itself doesn't know or care if a form is sent via GET or POST, however.)
35
+
36
+ ====Changing the Browser
37
+ In <i>functional_testcase/functional_testcase.rb</i>, see the <tt>browser()</tt> method on Test::Unit::TestCase.
38
+
39
+ Selenium::SeleniumDriver.new("localhost", 4444, "*firefox", "http://localhost:8000", 60000)
40
+
41
+ Use <tt>"*iexplore"</tt> for that MS thing.
42
+
43
+ =Sample Output
44
+
45
+ C:\honey-do>rake test
46
+ (in C:/honey-do)
47
+ c:/ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb
48
+ Loaded suite c:/ruby/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
49
+ Started
50
+ .....................................................
51
+ TEST OUTPUT:
52
+ *** *** TEST_HONEY_DO_TIME *** ***
53
+ honey_do_input, setting whole form 10 times: 0.608999967575073
54
+
55
+ *** *** TEST_ONE_FIELD_AT_A_TIME_HONEY_DO_TIME *** ***
56
+ one_field_at_a_time_input, setting whole form 10 times: 3.8289999961853
57
+
58
+ *** *** TEST_SELENIUM_TIME *** ***
59
+ selenium_input, setting whole form 10 times: 3.53099989891052
60
+ .
61
+ .
62
+ .
63
+
64
+ TESTS RUN:
65
+ 1 - test_check_multiple_in_group(TestCheckboxFeatures) - 0.328
66
+ 2 - test_check_multiple_independents(TestCheckboxFeatures) - 0.25
67
+ .
68
+ .
69
+ .
70
+ 18 - test_honey_do_time(TestFastAndPretty) - 0.766
71
+ 19 - test_one_field_at_a_time_honey_do_time(TestFastAndPretty) - 3.938
72
+ 20 - test_selenium_time(TestFastAndPretty) - 3.703
73
+ .
74
+ .
75
+ .
76
+ 53 - test_triple_colon_is_input_delimiter_with_mulitiple_fields(TestTextareaInput) - 0.265
77
+ 54 - test_triple_colon_is_input_delimiter_with_single_field(TestTextareaInput) - 0.25
78
+ .
79
+ Finished in 34.797 seconds.
80
+
81
+ 54 tests, 131 assertions, 0 failures, 0 errors
@@ -0,0 +1,99 @@
1
+ # Extend Test::Unit::Testcase
2
+ # Sel. session is started in setup() of first testcase, and stopped in teardown() of last
3
+ require 'test/unit'
4
+ require 'selenium'
5
+
6
+ module Test::Unit
7
+ class TestCase
8
+ @@total_test_count = nil
9
+ def TestCase.total_test_count
10
+ @@total_test_count ||= 0
11
+ end
12
+
13
+ def TestCase.add_to_total_test_count(count_to_add)
14
+ @@total_test_count = total_test_count + count_to_add
15
+ end
16
+
17
+ # command line arguments which could be individual test method names
18
+ @@args = Array.new(ARGV)
19
+
20
+ # list of 1 or more test method names to follow a test file name
21
+ def TestCase.cmd_line_tests
22
+ @@args.select {|arg| arg =~ /^test_/}
23
+ end
24
+
25
+ # for each TestCase.suite
26
+ def self.all_test_method_names
27
+ @all_test_method_names ||= public_instance_methods(true).select {|m| m =~ /^test_/}
28
+ end
29
+
30
+ # gets tests from TestCase.suite or from the command line
31
+ def self.tests
32
+ tests = all_test_method_names
33
+ # intersection of arrays: remove any tests not on the command line
34
+ return (tests & cmd_line_tests) unless cmd_line_tests.empty?
35
+
36
+ return tests
37
+ end
38
+
39
+ # * collects test method names into suite object
40
+ # * accumulates total test count from mulitple suites
41
+ def self.suite
42
+ suite = TestSuite.new(name)
43
+ tests.sort.each do |test|
44
+ catch(:invalid_test) do
45
+ suite << new(test)
46
+ end
47
+ end
48
+
49
+ add_to_total_test_count(suite.tests.size) unless suite.empty?
50
+ return suite
51
+ end
52
+
53
+ def start_time()
54
+ @start_time ||= Time.now()
55
+ end
56
+
57
+ def elapsed()
58
+ Time.now() - start_time
59
+ end
60
+
61
+ @@browser = nil
62
+ # <tt>SeleniumDriver</tt> reference
63
+ def browser
64
+ @@browser ||= Selenium::SeleniumDriver.new("localhost", 4444, "*firefox", "http://localhost:8000", 60000)
65
+ end
66
+
67
+ def selenium_driver_started?
68
+ browser.inspect.include?("@session_id")
69
+ end
70
+
71
+ # starts <tt>SeleniumDriver</tt> if necessary
72
+ def setup()
73
+ browser.start unless selenium_driver_started?
74
+ start_time()
75
+ end
76
+
77
+ @@print_buffer = []
78
+ @@test_names = []
79
+ # stops <tt>SeleniumDriver</tt> after the last test in the last suite
80
+ def teardown
81
+ @@test_names << "#{name} - #{elapsed}"
82
+
83
+ if run_count.eql?(TestCase.total_test_count)
84
+ browser.stop()
85
+ unless @@print_buffer.empty?
86
+ puts "\nTEST OUTPUT:"
87
+ puts @@print_buffer
88
+ end
89
+ puts "\nTESTS RUN:"
90
+ @@test_names.each_with_index {|test_name, i| puts "#{i + 1} - #{test_name}"}
91
+ end
92
+ end
93
+
94
+ def run_count
95
+ @_result.run_count + 1
96
+ end
97
+
98
+ end
99
+ end