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.
- checksums.yaml +4 -4
- data/.gitignore +18 -0
- data/.simplecov +5 -0
- data/CHANGELOG.md +43 -1
- data/Gemfile.lock +60 -2
- data/README.md +166 -21
- data/Rakefile +60 -1
- data/config/cucumber.yml +163 -0
- data/config/test_data/data.json +12 -0
- data/config/test_data/data.xls +0 -0
- data/config/test_data/data.yml +12 -0
- data/docker-compose-v3.yml +48 -0
- data/features/basic_test_page_css.feature +35 -0
- data/features/basic_test_page_xpath.feature +27 -0
- data/features/media_players.feature +87 -0
- data/features/step_definitions/generic_steps.rb.rb +44 -0
- data/features/step_definitions/media_steps.rb +30 -0
- data/features/support/env.rb +50 -0
- data/features/support/hooks.rb +245 -0
- data/features/support/pages/base_test_page.rb +11 -0
- data/features/support/pages/basic_css_test_page.rb +52 -0
- data/features/support/pages/basic_test_page.rb +297 -0
- data/features/support/pages/basic_xpath_test_page.rb +53 -0
- data/features/support/pages/custom_controls_page.rb +16 -0
- data/features/support/pages/indexed_sections_page.rb +16 -0
- data/features/support/pages/media_test_page.rb +195 -0
- data/features/support/sections/header_nav.rb +28 -0
- data/features/support/world_data.rb +12 -0
- data/features/support/world_pages.rb +18 -0
- data/lib/testcentricity_web/data_objects/environment.rb +16 -0
- data/lib/testcentricity_web/version.rb +1 -1
- data/lib/testcentricity_web/web_core/page_object.rb +8 -8
- data/lib/testcentricity_web/web_core/page_objects_helper.rb +41 -8
- data/lib/testcentricity_web/web_core/page_section.rb +1 -16
- data/lib/testcentricity_web/web_core/webdriver_helper.rb +77 -120
- data/lib/testcentricity_web/web_elements/checkbox.rb +3 -4
- data/lib/testcentricity_web/web_elements/media.rb +50 -4
- data/lib/testcentricity_web/web_elements/radio.rb +5 -1
- data/lib/testcentricity_web/web_elements/select_list.rb +18 -7
- data/lib/testcentricity_web/web_elements/ui_elements_helper.rb +13 -0
- data/reports/.keep +1 -0
- data/test_site/basic_test_page.html +290 -0
- data/test_site/custom_controls_page.html +58 -0
- data/test_site/images/Granny.jpg +0 -0
- data/test_site/images/Wilder.jpg +0 -0
- data/test_site/images/You_Betcha.jpg +0 -0
- data/test_site/indexed_sections_page.html +58 -0
- data/test_site/media/MIB2-subtitles-pt-BR.vtt +49 -0
- data/test_site/media/MIB2.mp4 +0 -0
- data/test_site/media/MP4_small.mp4 +0 -0
- data/test_site/media/MPS_sample.mp3 +0 -0
- data/test_site/media_page.html +86 -0
- data/testcentricity_web.gemspec +5 -0
- metadata +125 -4
- 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,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
|
-
|
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
|
-
|
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
|
245
|
-
# buttons, data must either be a Boolean or a String that evaluates to a Boolean value (Yes, No, 1, 0, true,
|
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.
|
289
|
-
|
290
|
-
|
291
|
-
data_field.
|
292
|
-
data_field.
|
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
|