SimpliTest 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)