testcentricity_web 4.1.4 → 4.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.simplecov +5 -0
  4. data/CHANGELOG.md +43 -1
  5. data/Gemfile.lock +60 -2
  6. data/README.md +166 -21
  7. data/Rakefile +60 -1
  8. data/config/cucumber.yml +163 -0
  9. data/config/test_data/data.json +12 -0
  10. data/config/test_data/data.xls +0 -0
  11. data/config/test_data/data.yml +12 -0
  12. data/docker-compose-v3.yml +48 -0
  13. data/features/basic_test_page_css.feature +35 -0
  14. data/features/basic_test_page_xpath.feature +27 -0
  15. data/features/media_players.feature +87 -0
  16. data/features/step_definitions/generic_steps.rb.rb +44 -0
  17. data/features/step_definitions/media_steps.rb +30 -0
  18. data/features/support/env.rb +50 -0
  19. data/features/support/hooks.rb +245 -0
  20. data/features/support/pages/base_test_page.rb +11 -0
  21. data/features/support/pages/basic_css_test_page.rb +52 -0
  22. data/features/support/pages/basic_test_page.rb +297 -0
  23. data/features/support/pages/basic_xpath_test_page.rb +53 -0
  24. data/features/support/pages/custom_controls_page.rb +16 -0
  25. data/features/support/pages/indexed_sections_page.rb +16 -0
  26. data/features/support/pages/media_test_page.rb +195 -0
  27. data/features/support/sections/header_nav.rb +28 -0
  28. data/features/support/world_data.rb +12 -0
  29. data/features/support/world_pages.rb +18 -0
  30. data/lib/testcentricity_web/data_objects/environment.rb +16 -0
  31. data/lib/testcentricity_web/version.rb +1 -1
  32. data/lib/testcentricity_web/web_core/page_object.rb +8 -8
  33. data/lib/testcentricity_web/web_core/page_objects_helper.rb +41 -8
  34. data/lib/testcentricity_web/web_core/page_section.rb +1 -16
  35. data/lib/testcentricity_web/web_core/webdriver_helper.rb +77 -120
  36. data/lib/testcentricity_web/web_elements/checkbox.rb +3 -4
  37. data/lib/testcentricity_web/web_elements/media.rb +50 -4
  38. data/lib/testcentricity_web/web_elements/radio.rb +5 -1
  39. data/lib/testcentricity_web/web_elements/select_list.rb +18 -7
  40. data/lib/testcentricity_web/web_elements/ui_elements_helper.rb +13 -0
  41. data/reports/.keep +1 -0
  42. data/test_site/basic_test_page.html +290 -0
  43. data/test_site/custom_controls_page.html +58 -0
  44. data/test_site/images/Granny.jpg +0 -0
  45. data/test_site/images/Wilder.jpg +0 -0
  46. data/test_site/images/You_Betcha.jpg +0 -0
  47. data/test_site/indexed_sections_page.html +58 -0
  48. data/test_site/media/MIB2-subtitles-pt-BR.vtt +49 -0
  49. data/test_site/media/MIB2.mp4 +0 -0
  50. data/test_site/media/MP4_small.mp4 +0 -0
  51. data/test_site/media/MPS_sample.mp3 +0 -0
  52. data/test_site/media_page.html +86 -0
  53. data/testcentricity_web.gemspec +5 -0
  54. metadata +125 -4
  55. data/test_site/test_page.html +0 -11
@@ -0,0 +1,195 @@
1
+ # Page Object class definition for Media Test page with CSS locators
2
+
3
+ class MediaTestPage < BaseTestPage
4
+ trait(:page_name) { 'Media Test' }
5
+ trait(:page_locator) { 'div.media-page-body' }
6
+ trait(:page_url) { '/media_page.html' }
7
+ trait(:navigator) { header_nav.open_media_page }
8
+
9
+ # Media Test page UI elements
10
+ videos video_player: 'video#video_player'
11
+ audios audio_player: 'audio#audio_player'
12
+
13
+ def verify_page_ui
14
+ preload = case
15
+ when Environ.browser == :safari || Environ.device_os == :ios
16
+ 'auto'
17
+ when Environ.browser == :firefox
18
+ ''
19
+ else
20
+ 'metadata'
21
+ end
22
+ video_player.wait_until_ready_state_is(4, 10)
23
+ ui = {
24
+ self => { exists: true, secure: false, title: 'Media Page' },
25
+ header_label => { visible: true, caption: 'Media Page' },
26
+ video_player => {
27
+ visible: true,
28
+ paused: true,
29
+ autoplay: false,
30
+ loop: false,
31
+ ended: false,
32
+ controls: true,
33
+ current_time: 0,
34
+ ready_state: 4,
35
+ default_playback_rate: 1,
36
+ playback_rate: 1,
37
+ seeking: false,
38
+ default_muted: false,
39
+ muted: false,
40
+ volume: 1,
41
+ preload: preload,
42
+ poster: '',
43
+ src: '',
44
+ duration: 17.6
45
+ },
46
+ audio_player => {
47
+ visible: true,
48
+ paused: true,
49
+ autoplay: false,
50
+ loop: false,
51
+ ended: false,
52
+ controls: true,
53
+ current_time: 0,
54
+ ready_state: 4,
55
+ default_playback_rate: 1,
56
+ playback_rate: 1,
57
+ seeking: false,
58
+ default_muted: false,
59
+ muted: false,
60
+ volume: 1,
61
+ preload: preload,
62
+ src: '',
63
+ duration: 3.45
64
+ }
65
+ }
66
+ verify_ui_states(ui)
67
+ end
68
+
69
+ def perform_action(media_type, action)
70
+ player = dispatch_player(media_type)
71
+ case action.downcase.to_sym
72
+ when :play
73
+ player.play
74
+ player.send_keys(:enter) if player.paused?
75
+ player.click if player.paused?
76
+ when :pause
77
+ player.pause
78
+ when :mute
79
+ player.mute
80
+ when :unmute
81
+ player.unmute
82
+ else
83
+ raise "#{action} is not a valid selector"
84
+ end
85
+ sleep(2)
86
+ end
87
+
88
+ def verify_media_state(media_type, state)
89
+ player = dispatch_player(media_type)
90
+ duration = player.duration
91
+ props = case state.downcase.to_sym
92
+ when :playing
93
+ {
94
+ visible: true,
95
+ paused: false,
96
+ ended: false,
97
+ current_time: { greater_than: 0 },
98
+ seeking: false
99
+ }
100
+ when :paused
101
+ {
102
+ visible: true,
103
+ paused: true,
104
+ ended: false,
105
+ current_time: { greater_than: 0 },
106
+ seeking: false
107
+ }
108
+ when :ended
109
+ {
110
+ visible: true,
111
+ paused: true,
112
+ ended: true,
113
+ current_time: duration,
114
+ seeking: false
115
+ }
116
+ when :muted
117
+ {
118
+ visible: true,
119
+ muted: true
120
+ }
121
+ when :unmuted
122
+ {
123
+ visible: true,
124
+ muted: false
125
+ }
126
+ else
127
+ raise "#{state} is not a valid selector"
128
+ end
129
+ verify_ui_states(player => props)
130
+ end
131
+
132
+ def set_playback_rate(media_type, rate)
133
+ player = dispatch_player(media_type)
134
+ player.playback_rate = rate.to_f
135
+ reset_play(player)
136
+ end
137
+
138
+ def verify_playback_rate(media_type, rate)
139
+ player = dispatch_player(media_type)
140
+ ui = {
141
+ player => {
142
+ playback_rate: rate.to_f,
143
+ visible: true,
144
+ paused: false,
145
+ current_time: { greater_than: 0 },
146
+ seeking: false
147
+ }
148
+ }
149
+ verify_ui_states(ui)
150
+ end
151
+
152
+ def set_volume(media_type, volume)
153
+ player = dispatch_player(media_type)
154
+ player.volume = volume.to_f
155
+ reset_play(player)
156
+ end
157
+
158
+ def verify_volume(media_type, volume)
159
+ player = dispatch_player(media_type)
160
+ ui = {
161
+ player => {
162
+ volume: volume.to_f,
163
+ visible: true,
164
+ muted: false,
165
+ paused: false,
166
+ current_time: { greater_than: 0 },
167
+ seeking: false
168
+ }
169
+ }
170
+ verify_ui_states(ui)
171
+ end
172
+
173
+ private
174
+
175
+ def dispatch_player(media_type)
176
+ player = case media_type.downcase.to_sym
177
+ when :video
178
+ video_player
179
+ when :audio
180
+ audio_player
181
+ else
182
+ raise "#{media_type} is not a valid selector"
183
+ end
184
+ player.wait_until_ready_state_is(4, 10)
185
+ player
186
+ end
187
+
188
+ def reset_play(player)
189
+ player.pause
190
+ player.current_time = 0
191
+ player.play
192
+ player.send_keys(:enter) if player.paused?
193
+ sleep(1)
194
+ end
195
+ end
@@ -0,0 +1,28 @@
1
+ # Section Object class definition for Top Navigation Bar
2
+
3
+ class NavHeader < TestCentricity::PageSection
4
+ trait(:section_locator) { 'div#nav_bar' }
5
+ trait(:section_name) { 'Top Navigation Bar' }
6
+
7
+ # Top Navigation Bar UI elements
8
+ links form_link: 'a#form_link',
9
+ media_link: 'a#media_link',
10
+ indexed_sections_link: 'a#indexed_sections_link',
11
+ custom_controls_link: 'a#custom_controls_link'
12
+
13
+ def open_form_page
14
+ form_link.click
15
+ end
16
+
17
+ def open_media_page
18
+ media_link.click
19
+ end
20
+
21
+ def open_indexed_sections_page
22
+ indexed_sections_link.click
23
+ end
24
+
25
+ def open_custom_controls_page
26
+ custom_controls_link.click
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module WorldData
2
+ #
3
+ # data_objects method returns a hash table of your web app's data objects and associated data object classes to be instantiated
4
+ # by the TestCentricity™ DataManager. Data Object class definitions are contained in the features/support/data folder.
5
+ #
6
+ def data_objects
7
+ {}
8
+ end
9
+ end
10
+
11
+
12
+ World(WorldData)
@@ -0,0 +1,18 @@
1
+ module WorldPages
2
+ #
3
+ # page_objects method returns a hash table of your web app's page objects and associated page classes to be instantiated
4
+ # by the TestCentricity™ PageManager. Page Object class definitions are contained in the features/support/pages folder.
5
+ #
6
+ def page_objects
7
+ {
8
+ basic_css_test_page: BasicCSSTestPage,
9
+ basic_xpath_test_page: BasicXpathTestPage,
10
+ media_test_page: MediaTestPage,
11
+ indexed_sections_page: IndexedSectionsPage,
12
+ custom_controls_page: CustomControlsPage
13
+ }
14
+ end
15
+ end
16
+
17
+
18
+ World(WorldPages)
@@ -71,6 +71,7 @@ module TestCentricity
71
71
  @screen_shots = []
72
72
 
73
73
  attr_accessor :test_environment
74
+ attr_accessor :app_host
74
75
  attr_accessor :browser
75
76
  attr_accessor :browser_size
76
77
  attr_accessor :headless
@@ -133,9 +134,20 @@ module TestCentricity
133
134
  @db_username = data['DB_USERNAME']
134
135
  @db_password = data['DB_PASSWORD']
135
136
 
137
+ url = @hostname.blank? ? "#{@base_url}#{@append}" : "#{@hostname}/#{@base_url}#{@append}"
138
+ @app_host = if @user_id.blank? || @password.blank?
139
+ "#{@protocol}://#{url}"
140
+ else
141
+ "#{@protocol}://#{@user_id}:#{@password}@#{url}"
142
+ end
143
+
136
144
  super
137
145
  end
138
146
 
147
+ def self.app_host
148
+ @app_host
149
+ end
150
+
139
151
  def self.session_code
140
152
  if @session_code.nil?
141
153
  characters = ('a'..'z').to_a
@@ -328,6 +340,10 @@ module TestCentricity
328
340
  @platform = platform
329
341
  end
330
342
 
343
+ def self.platform
344
+ @platform
345
+ end
346
+
331
347
  def self.is_mobile?
332
348
  @platform == :mobile
333
349
  end
@@ -1,3 +1,3 @@
1
1
  module TestCentricityWeb
2
- VERSION = '4.1.4'
2
+ VERSION = '4.1.7'
3
3
  end
@@ -1,5 +1,9 @@
1
1
  module TestCentricity
2
2
  class PageObject < BasePageSectionObject
3
+ def initialize
4
+ set_locator_type(page_locator) if defined?(page_locator)
5
+ end
6
+
3
7
  # Declare and instantiate a single generic UI Element for this page object.
4
8
  #
5
9
  # @param element_name [Symbol] name of UI object (as a symbol)
@@ -308,19 +312,15 @@ module TestCentricity
308
312
  end
309
313
 
310
314
  def open_portal
311
- environment = Environ.current
312
- url = environment.hostname.blank? ? "#{environment.base_url}#{environment.append}" : "#{environment.hostname}/#{environment.base_url}#{environment.append}"
313
- if environment.user_id.blank? || environment.password.blank?
314
- visit "#{environment.protocol}://#{url}"
315
- else
316
- visit "#{environment.protocol}://#{environment.user_id}:#{environment.password}@#{url}"
317
- end
315
+ visit Environ.current.app_host
318
316
  Environ.portal_state = :open
319
317
  end
320
318
 
321
319
  def verify_page_exists
322
320
  raise "Page object #{self.class.name} does not have a page_locator trait defined" unless defined?(page_locator)
323
- unless page.has_selector?(page_locator)
321
+
322
+ set_locator_type(page_locator) if @locator_type.blank?
323
+ unless page.has_selector?(@locator_type, page_locator)
324
324
  body_class = find(:xpath, '//body')[:class]
325
325
  error_message = %(
326
326
  Expected page to have selector '#{page_locator}' but found '#{body_class}' instead.
@@ -6,6 +6,26 @@ module TestCentricity
6
6
  include Capybara::Node::Matchers
7
7
  include Test::Unit::Assertions
8
8
 
9
+ attr_accessor :locator_type
10
+
11
+ XPATH_SELECTORS = ['//', '[@', '[contains(']
12
+ CSS_SELECTORS = ['#', ':nth-child(', ':first-child', ':last-child', ':nth-of-type(', ':first-of-type', ':last-of-type', '^=', '$=', '*=', ':contains(']
13
+
14
+ def set_locator_type(locator = nil)
15
+ locator = @locator if locator.nil?
16
+ is_xpath = XPATH_SELECTORS.any? { |selector| locator.include?(selector) }
17
+ is_css = CSS_SELECTORS.any? { |selector| locator.include?(selector) }
18
+ @locator_type = if is_xpath && !is_css
19
+ :xpath
20
+ elsif is_css && !is_xpath
21
+ :css
22
+ elsif !is_css && !is_xpath
23
+ :css
24
+ else
25
+ :css
26
+ end
27
+ end
28
+
9
29
  # Define a trait for this page or section object.
10
30
  #
11
31
  # @param trait_name [Symbol] name of trait (as a symbol)
@@ -31,6 +51,8 @@ module TestCentricity
31
51
  ui_object.get_attribute(:name)
32
52
  when :title
33
53
  ui_object.title
54
+ when :secure
55
+ ui_object.secure?
34
56
  when :exists
35
57
  ui_object.exists?
36
58
  when :enabled
@@ -63,6 +85,8 @@ module TestCentricity
63
85
  ui_object.indeterminate?
64
86
  when :value, :caption
65
87
  ui_object.get_value
88
+ when :required
89
+ ui_object.required?
66
90
  when :maxlength
67
91
  ui_object.get_max_length
68
92
  when :rowcount
@@ -241,8 +265,11 @@ module TestCentricity
241
265
  end
242
266
 
243
267
  # Populate the specified UI elements on this page or section object with the associated data from a Hash passed as an
244
- # argument. Data values must be in the form of a String for textfield and selectlist controls. For checkbox and radio
245
- # buttons, data must either be a Boolean or a String that evaluates to a Boolean value (Yes, No, 1, 0, true, false).
268
+ # argument. Data values must be in the form of a String for textfield, selectlist, and filefield controls. For checkbox
269
+ # and radio buttons, data must either be a Boolean or a String that evaluates to a Boolean value (Yes, No, 1, 0, true,
270
+ # false). For range controls, data must be an Integer. For input(type='color') color picker controls, which are specified
271
+ # as a textfield, data must be in the form of a hex color String. For section objects, data values must be a String, and
272
+ # the section object must have a set method defined.
246
273
  #
247
274
  # The optional wait_time parameter is used to specify the time (in seconds) to wait for each UI element to become
248
275
  # visible before entering the associated data value. This option is useful in situations where entering data, or
@@ -285,14 +312,20 @@ module TestCentricity
285
312
  check_state = data_param.is_a?(String) ? data_param.to_bool : data_param
286
313
  data_field.set_selected_state(check_state)
287
314
  when :textfield
288
- data_field.set("#{data_param}\t")
289
- if integrity_check && data_field.get_value != data_param
290
- data_field.set('')
291
- data_field.send_keys(data_param)
292
- data_field.send_keys(:tab)
315
+ if data_field.get_attribute(:type) == 'color'
316
+ data_field.set(data_param)
317
+ else
318
+ data_field.set("#{data_param}\t")
319
+ if integrity_check && data_field.get_value != data_param
320
+ data_field.set('')
321
+ data_field.send_keys(data_param)
322
+ data_field.send_keys(:tab)
323
+ end
293
324
  end
294
- when :section
325
+ when :section, :range
295
326
  data_field.set(data_param)
327
+ when :filefield
328
+ data_field.file_upload(data_param)
296
329
  end
297
330
  end
298
331
  end
@@ -5,10 +5,6 @@ module TestCentricity
5
5
  attr_accessor :parent
6
6
  attr_accessor :parent_list
7
7
  attr_accessor :list_index
8
- attr_accessor :locator_type
9
-
10
- XPATH_SELECTORS = ['//', '[@', '[contains(']
11
- CSS_SELECTORS = ['#', ':nth-child(', ':first-child', ':last-child', ':nth-of-type(', ':first-of-type', ':last-of-type', '^=', '$=', '*=', ':contains(']
12
8
 
13
9
  def initialize(name, parent, locator, context)
14
10
  @name = name
@@ -17,18 +13,7 @@ module TestCentricity
17
13
  @context = context
18
14
  @parent_list = nil
19
15
  @list_index = nil
20
-
21
- is_xpath = XPATH_SELECTORS.any? { |selector| @locator.include?(selector) }
22
- is_css = CSS_SELECTORS.any? { |selector| @locator.include?(selector) }
23
- @locator_type = if is_xpath && !is_css
24
- :xpath
25
- elsif is_css && !is_xpath
26
- :css
27
- elsif !is_css && !is_xpath
28
- :css
29
- else
30
- :css
31
- end
16
+ set_locator_type
32
17
  end
33
18
 
34
19
  def get_locator