testcentricity_web 4.1.4 → 4.1.7

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 (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