SimpliTest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +22 -0
  3. data/SimpliTest.gemspec +42 -0
  4. data/bin/SimpliTest +22 -0
  5. data/lib/SimpliTest.rb +2 -0
  6. data/lib/SimpliTest/cli/main.rb +305 -0
  7. data/lib/SimpliTest/config/configuration.rb +107 -0
  8. data/lib/SimpliTest/config/directory_paths.rb +36 -0
  9. data/lib/SimpliTest/config/environment.rb +67 -0
  10. data/lib/SimpliTest/config/local_environment.rb +20 -0
  11. data/lib/SimpliTest/config/profiles/chrome.rb +18 -0
  12. data/lib/SimpliTest/config/profiles/firefox.rb +14 -0
  13. data/lib/SimpliTest/config/profiles/internet_explorer.rb +12 -0
  14. data/lib/SimpliTest/config/profiles/phantom.rb +9 -0
  15. data/lib/SimpliTest/config/profiles/phantom_debug.rb +12 -0
  16. data/lib/SimpliTest/config/profiles/selenium.rb +13 -0
  17. data/lib/SimpliTest/config/screen_size.rb +25 -0
  18. data/lib/SimpliTest/config/steps.rb +8 -0
  19. data/lib/SimpliTest/helpers/data_validation.rb +60 -0
  20. data/lib/SimpliTest/helpers/file.rb +38 -0
  21. data/lib/SimpliTest/helpers/project_setup.rb +186 -0
  22. data/lib/SimpliTest/helpers/step_helpers/custom_chrome_helpers.rb +14 -0
  23. data/lib/SimpliTest/helpers/step_helpers/custom_date_helpers.rb +64 -0
  24. data/lib/SimpliTest/helpers/step_helpers/custom_form_helpers.rb +66 -0
  25. data/lib/SimpliTest/helpers/step_helpers/custom_phantomjs_helpers.rb +49 -0
  26. data/lib/SimpliTest/helpers/step_helpers/custom_selenium_helpers.rb +41 -0
  27. data/lib/SimpliTest/helpers/step_helpers/html_selectors_helpers.rb +154 -0
  28. data/lib/SimpliTest/helpers/step_helpers/navigation_helpers.rb +196 -0
  29. data/lib/SimpliTest/helpers/step_helpers/tolerance_for_sync_issues.rb +38 -0
  30. data/lib/SimpliTest/helpers/step_helpers/within_helpers.rb +6 -0
  31. data/lib/SimpliTest/helpers/windows_ui.rb +52 -0
  32. data/lib/SimpliTest/steps/debugging_steps.rb +19 -0
  33. data/lib/SimpliTest/steps/form_steps.rb +352 -0
  34. data/lib/SimpliTest/steps/form_verification_steps.rb +190 -0
  35. data/lib/SimpliTest/steps/navigation_steps.rb +47 -0
  36. data/lib/SimpliTest/steps/scoper.rb +42 -0
  37. data/lib/SimpliTest/steps/verification_steps.rb +306 -0
  38. data/lib/SimpliTest/tasks/document.rb +169 -0
  39. data/lib/SimpliTest/tasks/examples.rb +18 -0
  40. data/lib/SimpliTest/tasks/internal_release.rb +39 -0
  41. data/lib/SimpliTest/tasks/testinstall.rb +5 -0
  42. data/lib/SimpliTest/templates/NewSimpliTestProject/Readme.txt +5 -0
  43. data/lib/SimpliTest/templates/NewSimpliTestProject/cucumber.yml +26 -0
  44. data/lib/SimpliTest/templates/NewSimpliTestProject/documentation/step_definitions.html +87 -0
  45. data/lib/SimpliTest/templates/NewSimpliTestProject/features/specifications/RegressionTests/HelloWorld.feature +4 -0
  46. data/lib/SimpliTest/templates/NewSimpliTestProject/features/specifications/SmokeTest/HelloWorld.feature +4 -0
  47. data/lib/SimpliTest/templates/NewSimpliTestProject/features/specifications/accessibilityTests/HelloWorld.feature +4 -0
  48. data/lib/SimpliTest/templates/NewSimpliTestProject/features/support/config/environments.yml +11 -0
  49. data/lib/SimpliTest/templates/NewSimpliTestProject/features/support/config/pages.yml +26 -0
  50. data/lib/SimpliTest/templates/NewSimpliTestProject/features/support/config/selectors.yml +44 -0
  51. data/lib/SimpliTest/templates/NewSimpliTestProject/features/support/config/settings.yml +26 -0
  52. data/lib/SimpliTest/templates/NewSimpliTestProject/features/support/env.rb +33 -0
  53. data/lib/SimpliTest/templates/NewSimpliTestProject/license.txt +30 -0
  54. data/lib/SimpliTest/templates/document/css/style.css +28 -0
  55. data/lib/SimpliTest/templates/document/index.html +60 -0
  56. data/lib/version.rb +3 -0
  57. metadata +395 -0
@@ -0,0 +1,14 @@
1
+ #The chrome profile loads Selenium Helpers as well. These methods
2
+ #override the methods available in Selenium Helpers
3
+ module CustomChromeHelpers
4
+ def maximize_window
5
+ page.driver.browser.manage.window.resize_to(MAX_WIDTH,MAX_HEIGHT)
6
+ end
7
+ def keydown_on(element)
8
+ element.native.send_key(:arrow_down)
9
+ end
10
+
11
+ def capture_screenshot(filename)
12
+ page.driver.browser.save_screenshot(filename)
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ module CustomDateHelpers
2
+ def calculated_date_from(date_rule)
3
+ # Takes strings like "Today's Date", "4 days ago", "5 days since" etc. and
4
+ # returns a calculated date in the default_date_format
5
+ potential_date_formats = /[0-9]+\/[0-9]+\/[0-9]+/x
6
+ first_and_last_regex = /the (nearest|first|last) (.*)/
7
+ date = case date_rule
8
+ when potential_date_formats
9
+ date_rule
10
+ when /Today's Date/i
11
+ Date.today
12
+ when /(\d+) days (ago|since)/i
13
+ number_of_days = $1.to_i rescue nil
14
+ time_travel_method = $2.downcase.to_sym
15
+ raise "Invalid number of days. Please enter a valid number" if number_of_days.nil?
16
+ number_of_days.send(:days).send(time_travel_method)
17
+ when first_and_last_regex
18
+ first_and_last_date_rule(rule)
19
+ end
20
+ date.respond_to?(:strftime) ? date.strftime(default_date_format) : date
21
+ end
22
+
23
+ def first_and_last_date_rule(phrase)
24
+ # Takes a phrase like "the first day of the month" or "last day of the
25
+ # previous month" and returns a calculated date in the default_date_format
26
+ case phrase
27
+ when /the (first|last) day of the month/i
28
+ Date.today.send(beginning_or_end($1))
29
+ when /the (first|last) day of next month/i
30
+ 1.month.since.send(beginning_or_end($1))
31
+ when /the (first|last) day of previous month/i
32
+ 1.month.ago.send(beginning_or_end($1))
33
+ when /the (first|last) day of (.*)/i
34
+ date_from_phrase($1, $2)
35
+ when /the nearest (first|last) day of month/i
36
+ rule = Date.today.day >= 15 ? "the #{$1} day of next month" : "the #{$1} day of the month"
37
+ calculated_date_from(rule)
38
+ end
39
+ end
40
+
41
+ def beginning_or_end(first_or_last)
42
+ first_or_last == 'first' ? :beginning_of_month : :end_of_month
43
+ end
44
+
45
+ def default_date_format
46
+ format_for_strftime SimpliTest.config_settings['DEFAULT_DATE_FORMAT']
47
+ end
48
+
49
+ def date_from_phrase(first_or_last, month)
50
+ month_name = month.downcase.camelize
51
+ month = Date::MONTHNAMES.index(month_name)
52
+ if month.nil?
53
+ raise "Oops you provided an invalid month name. Please use a valid month name from January to December"
54
+ end
55
+ date = Date.parse("#{month}/1") #mm/dd in current year
56
+ date = date.send(:end_of_month) if first_or_last == 'last'
57
+ end
58
+
59
+ def format_for_strftime(string)
60
+ string.gsub('mm', '%m').gsub('dd', '%d').gsub('yyyy', '%Y').gsub('yy', '%y')
61
+ end
62
+ end
63
+
64
+ World(CustomDateHelpers)
@@ -0,0 +1,66 @@
1
+ module CustomFormHelpers
2
+
3
+ def numerize(number_name)
4
+ case number_name
5
+ when 'first'; 0
6
+ when 'last'; -1
7
+ else number_name.match(/[0-9]/)[1].to_i + 1 rescue 0 #take the first one if you cant figure it out
8
+ end
9
+ end
10
+
11
+ def blur(selector_and_xpath)
12
+ css_selector, xpath = selector_and_xpath
13
+ selector = xpath ? xpath : css_selector
14
+ if xpath
15
+ return
16
+ else
17
+ #TODO: Hate how this done...need to come back to this
18
+ execute_script_for_driver(SimpliTest.config_driver, %Q{ $('#{selector}').blur(); })
19
+ end
20
+ end
21
+
22
+ def fill_in_masked_field(css_selector, value)
23
+ STDOUT.puts "Entering #{value} in Masked Field #{css_selector} now" #TODO: Delete this debugging statement
24
+ execute_script_for_driver(SimpliTest.config_driver, %Q{ $("#{css_selector}").val("#{value}"); })
25
+ STDOUT.puts "Blurring the field now" #TODO: Delete this debugging statement
26
+ execute_script_for_driver(SimpliTest.config_driver, %Q{ $("#{css_selector}").blur(); })
27
+ #page.driver.debug
28
+ end
29
+
30
+
31
+ def press_key(key)
32
+ keycode = keycode_for[key.downcase.gsub(' ','_')]
33
+ keypress_script = "var e = $.Event('keydown', { keyCode: #{keycode} }); $('body').trigger(e);"
34
+ execute_js(keypress_script)
35
+ end
36
+
37
+ def tab_on(element)
38
+ element.native.send_key(:tab)
39
+ end
40
+
41
+ def keypress_on(elem, key, charCode = 0)
42
+ keyCode = keycode_for[key]
43
+ elem.base.invoke('keypress', false, false, false, false, keyCode, charCode);
44
+ end
45
+
46
+ def keycode_for
47
+ {
48
+ :tab => 9,
49
+ :enter => 13,
50
+ :up_arrow => 38,
51
+ :down_arrow => 40,
52
+ :left_arrow => 37,
53
+ :right_arrow => 39,
54
+ :escape => 27,
55
+ :spacebar => 32,
56
+ :ctrl => 17,
57
+ :alt => 18,
58
+ :shift => 16,
59
+ :caps_lock => 20,
60
+ :backspace => 8,
61
+ :delete => 46
62
+ }
63
+ end
64
+ end
65
+ World(CustomFormHelpers)
66
+
@@ -0,0 +1,49 @@
1
+ module CustomPhantomjsHelpers
2
+ def maximize_window
3
+ page.driver.resize_window(MAX_WIDTH,MAX_HEIGHT)
4
+ end
5
+
6
+ def tab_on(element)
7
+ element.native.send_key(:Tab)
8
+ end
9
+
10
+ def accept_confirmation
11
+ #TODO: accept confirmation
12
+ raise "NotImplementedError"
13
+ end
14
+
15
+ def execute_js(script)
16
+ page.execute_script(script)
17
+ end
18
+
19
+ def get_text_from(element)
20
+ element.native.all_text
21
+ end
22
+
23
+ def click_element(element)
24
+ begin
25
+ element.click
26
+ rescue Capybara::Poltergeist::MouseEventFailed
27
+ element.trigger('click')
28
+ end
29
+ end
30
+
31
+ def key_in(character, element)
32
+ element.native.send_key(character)
33
+ end
34
+
35
+ def keydown_on(element)
36
+ key_in('Down', element)
37
+ end
38
+
39
+ def capture_screenshot(filename)
40
+ page.save_screenshot(filename)
41
+ end
42
+
43
+ def change_window(first_or_last)
44
+ raise "Invalid window name #{first_or_last}. You can only use 'first' or 'last'" unless first_or_last =~ /first|last/
45
+ window_handle = page.driver.browser.window_handles.send(first_or_last.to_sym)
46
+ page.driver.browser.switch_to_window(window_handle)
47
+ wait_for(page.driver.browser.window_handles.size, 1)
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ module CustomSeleniumHelpers
2
+ def maximize_window
3
+ page.driver.browser.manage.window.resize_to(MAX_WIDTH, MAX_HEIGHT)
4
+ end
5
+
6
+ def capture_screenshot(filename)
7
+ page.driver.browser.save_screenshot(filename)
8
+ end
9
+
10
+ def accept_confirmation
11
+ page.driver.browser.switch_to.alert.accept
12
+ end
13
+
14
+ def execute_js(script)
15
+ page.driver.browser.execute_script(script)
16
+ end
17
+
18
+ def get_text_from(element)
19
+ element.text
20
+ end
21
+
22
+ def click_element(element)
23
+ element.click
24
+ end
25
+
26
+ def key_in(character, element)
27
+ element.native.send_key(character)
28
+ end
29
+
30
+ def keydown_on(element)
31
+ key_in(:arrow_down, element)
32
+ end
33
+
34
+ def change_window(first_or_last)
35
+ raise "Invalid window name #{first_or_last}. You can only use 'first' or 'last'" unless first_or_last =~ /first|last/
36
+ window_handle = page.driver.browser.window_handles.send(first_or_last.to_sym)
37
+ page.driver.browser.switch_to.window(window_handle)
38
+ wait_for(page.driver.browser.window_handles.size, 1)
39
+ end
40
+ end
41
+
@@ -0,0 +1,154 @@
1
+ module HtmlSelectorsHelpers
2
+ #TODO: Rewrite without test app for template. The code here effing nuts'...clean this shit
3
+ @selectors_file = 'features/support/config/selectors.yml'
4
+
5
+ def pagination_links_for(page_number)
6
+ selector = "input[title='Page #{page_number}']"
7
+ all(:css, selector)
8
+ end
9
+
10
+ def pagination_element_for(index)
11
+ lookup = {
12
+ 'first' => 'firstButton',
13
+ 'last' => 'lastButton',
14
+ 'previous' => 'prevButton',
15
+ 'next' => 'nextButton'
16
+ }
17
+ selector = "##{lookup[index]}"
18
+ elements = all(:css, selector)
19
+ elements.first if elements
20
+ end
21
+
22
+ def select_options_for(locator)
23
+ element = get_element_from(locator)
24
+ get_text_from(element)
25
+ end
26
+
27
+ def get_element_from(locator, try_field=true, find_button_or_link=false)
28
+ selector, selector_type = selector_for(locator)
29
+ element = nil
30
+ patiently do
31
+ if selector_type == 'xpath'
32
+ element = page.find(:xpath, selector)
33
+ elsif selector_type == 'css'
34
+ element = page.find(:css, selector)
35
+ elsif try_field
36
+ element = page.find_field(selector)
37
+ elsif find_button_or_link
38
+ raise "Button or Link Not Found"
39
+ else
40
+ element = find(selector)
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ def get_button_or_link_from(locator)
47
+ begin
48
+ get_element_from(locator, false, true)
49
+ rescue
50
+ begin
51
+ page.find_link(locator)
52
+ rescue
53
+ page.find_button(locator)
54
+ end
55
+ end
56
+ end
57
+
58
+ def selector_for(locator)
59
+ if SimpliTest.config_selectors
60
+ mapping = mapping_for(locator)
61
+ mapping ? selector_from(mapping) : default_selector_for(locator)
62
+ else
63
+ default_selector_for(locator)
64
+ end
65
+ end
66
+
67
+ def mapping_for(selector)
68
+ SimpliTest.config_selectors[unquoted(selector)] || false
69
+ end
70
+
71
+ def selector_from(selector)
72
+ selector_type = is_xpath?(selector) ? 'xpath' : (is_css?(selector) ? 'css' : 'other')
73
+ selector = without_identifier(selector)
74
+ return selector, selector_type
75
+ end
76
+
77
+
78
+ def is_xpath?(locator)
79
+ selector_for(locator)[0].match(/xpath:(.*)/)
80
+ end
81
+
82
+ def is_css?(locator)
83
+ selector_for(locator)[0].match(/css:(.*)/)
84
+ end
85
+
86
+ def without_identifier(locator)
87
+ if match = (is_xpath?(locator) || is_css?(locator))
88
+ return match[1]
89
+ else
90
+ return locator
91
+ end
92
+ end
93
+
94
+
95
+ def unquoted(string)
96
+ #string[0] is the first char
97
+ #string[-1,1] is the last char
98
+ is_string_quoted = (string[0] == string[-1, 1]) && (%w[' "].include?(string[0]))
99
+ is_string_quoted ? string.gsub(/^"|"$/, '') : string
100
+ end
101
+
102
+ def default_selector_for(locator)
103
+ case locator
104
+
105
+ when "the page"
106
+ with_selector_type "html > body"
107
+ when "table's header"
108
+ with_selector_type "table tbody > tr th"
109
+ when /^paragraphs?$/
110
+ with_selector_type "p"
111
+ when "\"table tr\""
112
+ with_selector_type "table tr"
113
+ when "\"div.some_class\""
114
+ with_selector_type "div.some_class"
115
+ else
116
+ with_selector_type locator
117
+ end
118
+ end
119
+
120
+ def with_selector_type(selector, selector_type='other')
121
+ [selector, selector_type]
122
+ end
123
+
124
+ def validate_absence_of(text)
125
+ min_wait_time = SimpliTest.config_settings ? SimpliTest.config_settings['MIN_WAIT_TIME'] : 2
126
+ using_wait_time min_wait_time do #because we are validating absence we don't need to wait all 5 seconds?
127
+ begin
128
+ should_not have_link(text)
129
+ rescue RSpec::Expectations::ExpectationNotMetError, Capybara::ExpectationNotMet
130
+ element = get_button_or_link_from(text)
131
+ element.should_not be_visible if element.respond_to?(:visible?)
132
+ ## deprecating the approach below for performance reasons
133
+ ##xpath = potential_hidden_paths_for(text).join('|')
134
+ ##should have_xpath(xpath)
135
+ end
136
+ end
137
+ end
138
+
139
+ def selectors_from_section(section)
140
+ SimpliTest.config_selectors[section].values
141
+ end
142
+
143
+ #def potential_hidden_paths_for(text) #the different ways developers might choose to hide things in the DOM
144
+ #[
145
+ #"//*[@class='hidden' and contains(.,'#{text}')]",
146
+ #"//*[@class='invisible' and contains(.,'#{text}')]",
147
+ #"//*[@style='display: none;' and contains(.,'#{text}')]"
148
+ #]
149
+
150
+ #end
151
+
152
+ end
153
+
154
+ World(HtmlSelectorsHelpers)
@@ -0,0 +1,196 @@
1
+ module NavigationHelpers
2
+ # Maps a name to a path. Used by the
3
+ #
4
+ # When /^I go to (.+)$/ do |page_name|
5
+ #
6
+ #TODO: Rewrite without test app for template
7
+ def path_to(page_name)
8
+ if is_url?(page_name)
9
+ page_name
10
+ elsif SimpliTest.config_pages
11
+ matches = SimpliTest.config_pages.select { |p| unquoted(page_name) == p }
12
+ matches.any? ? url_from(matches, page_name) : default_path_for(page_name)
13
+ else
14
+ default_path_for(page_name)
15
+ end
16
+ end
17
+ # :nocov:
18
+ def url_from(matches, page_name)
19
+ if matches.length > 1
20
+ raise "Oops ambigous page name! More than one matched our search. It seems you have similar page names. \n" +
21
+ "Now, go and modify #{page_name} to something cooler in config/pages.yml"
22
+ else
23
+ path = matches.values.first
24
+ end
25
+ url_to(path) if path
26
+ end
27
+
28
+ def default_path_for(page_name)
29
+ case page_name
30
+
31
+ when /^home$/
32
+ '/'
33
+ when /^congratulations$/
34
+ '/congratulations'
35
+ when /^other$/
36
+ '/other/page'
37
+ when /^form$/
38
+ '/form/page'
39
+ when /^valid_links$/
40
+ '/valid-links'
41
+ when /^invalid_links$/
42
+ '/invalid-links'
43
+ # Add more mappings here.
44
+ # Here is an example that pulls values out of the Regexp:
45
+ #
46
+ # when /^(.*)'s profile page$/i
47
+ # user_profile_path(User.find_by_login($1))
48
+
49
+ else
50
+ begin
51
+ page_name =~ /^the (.*) page$/
52
+ path_components = $1.split(/\s+/)
53
+ self.send(path_components.push('path').join('_').to_sym)
54
+ rescue NoMethodError, ArgumentError
55
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
56
+ "Now, go and add a mapping in #{__FILE__}"
57
+ end
58
+ end
59
+ end
60
+ # :nocov:
61
+
62
+ def url_to(path)
63
+ if is_url?(path)
64
+ path
65
+ else
66
+ begin
67
+ URI.parse(SimpliTest.config_environments[SimpliTest.config_environment] + path).normalize.to_s
68
+ rescue
69
+ puts "It seems that the #{SimpliTest.config_environment} enviroment has not been set up. Please add it to features/config/environments.yml"
70
+ raise "Unidentified Environment Requested!!"
71
+ end
72
+ end
73
+ end
74
+
75
+ def is_url?(path)
76
+ uri = URI.parse(path)
77
+ uri if uri.scheme =~ /^https?$/
78
+ rescue URI::BadURIError, URI::InvalidURIError
79
+ false
80
+ end
81
+
82
+ def is_relative?(path)
83
+ uri = URI.parse(path)
84
+ uri.relative?
85
+ end
86
+
87
+ def is_valid_url_or_path?(path)
88
+ is_url?(path) || is_relative?(path)
89
+ end
90
+
91
+ def is_javascript_href?(href)
92
+ href =~ /^(javascript|#)/
93
+ end
94
+
95
+ def parse_dom_tree(url)
96
+ Nokogiri::HTML(open(url))
97
+ end
98
+
99
+ def valid_assets_links_from(nokogiri_html_document)
100
+ doc = nokogiri_html_document
101
+ links = stylesheet_references_on(doc) + javascript_references_on(doc)
102
+ links.select{ |l| !is_javascript_href?(l) }
103
+ end
104
+
105
+ def stylesheet_references_on(doc)
106
+ doc.css('link').collect{|l| l['href'] }.compact
107
+ end
108
+
109
+ def javascript_references_on(doc)
110
+ doc.css('script').collect{|l| l['src']}.compact
111
+ end
112
+
113
+ def links_on(doc)
114
+ doc.css('a').collect{|l| l['href']}.compact.select{ |l| !is_javascript_href?(l) }
115
+ end
116
+
117
+ def validate_response_from(page_name, href)
118
+ begin
119
+ root_path = path_to(page_name)
120
+ uri = is_relative?(href) ? (URI.join(root_path, href)) : URI.parse(href)
121
+ response = get_response_from(uri)
122
+ valid_status_groups = [Net::HTTPRedirection, Net::HTTPInformation, Net::HTTPSuccess ]
123
+ valid_status_groups.should include(response.class.superclass)
124
+ rescue RSpec::Expectations::ExpectationNotMetError
125
+ message = "Received a #{response.code} #{ response.msg } from #{href} on #{page_name}"
126
+ fail message
127
+ rescue
128
+ message = "Invalid reference: #{href} found on #{page_name} page"
129
+ fail message
130
+ end
131
+ end
132
+
133
+ #this returns a hash with a response code and the url to help provide good fail detail
134
+ def get_response_from(uri)
135
+ http = Net::HTTP.new(uri.host, uri.port)
136
+ http.read_timeout = http.open_timeout = SimpliTest.config[:settings]["HTTP_TIMEOUT"] || 3
137
+ request = Net::HTTP::Get.new(uri.request_uri)
138
+ if uri.scheme == 'https'
139
+ http.use_ssl = true
140
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
141
+ end
142
+ http.request(request)
143
+ end
144
+
145
+ #FIXME: Code not used. Should be deleted
146
+ def url_in_new_window
147
+ if handles = page.driver.browser.window_handles rescue nil
148
+ new_window = handles.last
149
+ page.within_window(new_window) do
150
+ return without_trailing_slash(current_url)
151
+ end
152
+ end
153
+ end
154
+
155
+ def without_trailing_slash(url)
156
+ url.chomp('/')
157
+ end
158
+
159
+ def with_encoded_white_space(string)
160
+ string.gsub(' ', '%20')
161
+ end
162
+
163
+ def with_unencoded_white_space(string)
164
+ string.gsub('%20', ' ')
165
+ end
166
+
167
+ def normalize(url)
168
+ normalized_uri = URI.parse(url).normalize
169
+ url = normalized_uri.to_s
170
+ encoded_url = URI::encode(url)
171
+ without_trailing_slash(encoded_url)
172
+ end
173
+
174
+ def normalize_path(path)
175
+ if path = is_url?(path)
176
+ path = path.path
177
+ end
178
+ with_encoded_white_space(without_trailing_slash(path))
179
+ end
180
+
181
+ def wait_for(left_argument, right_argument)
182
+ called_at = Time.now
183
+ until left_argument > right_argument do
184
+ past_wait_time = called_at < Time.now - SimpliTest.config['HTTP_TIMEOUT']
185
+ return if past_wait_time
186
+ sleep 1
187
+ end
188
+ end
189
+
190
+ def comparable_urls_for(target_url, source_url)
191
+ is_url?(target_url) ? [target_url, source_url].map{|u| normalize(u)} : [target_url, URI.parse(source_url).path]
192
+ end
193
+
194
+ end
195
+
196
+ World(NavigationHelpers)