testcentricity 3.0.1 → 3.0.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +1 -0
- data/Rakefile +2 -2
- data/config/cucumber.yml +1 -0
- data/config/test_data/data.yml +66 -0
- data/features/deep_links.feature +12 -0
- data/features/navigation.feature +12 -0
- data/features/step_definitions/generic_steps.rb +14 -19
- data/features/support/android/screens/base_app_screen.rb +2 -0
- data/features/support/android/screens/biometrics_screen.rb +17 -0
- data/features/support/android/screens/geo_location_screen.rb +28 -0
- data/features/support/android/screens/product_item_screen.rb +39 -0
- data/features/support/android/screens/products_screen.rb +15 -1
- data/features/support/android/screens/qr_code_scanner_screen.rb +15 -0
- data/features/support/android/sections/list_items/product_list_item.rb +13 -0
- data/features/support/data/product_data.rb +27 -0
- data/features/support/data/user_data.rb +17 -0
- data/features/support/env.rb +2 -1
- data/features/support/ios/screens/base_app_screen.rb +1 -0
- data/features/support/ios/screens/biometrics_screen.rb +17 -0
- data/features/support/ios/screens/geo_location_screen.rb +32 -0
- data/features/support/ios/screens/product_item_screen.rb +39 -0
- data/features/support/ios/screens/products_screen.rb +15 -1
- data/features/support/ios/screens/qr_code_scanner_screen.rb +11 -0
- data/features/support/ios/sections/list_items/product_list_item.rb +13 -0
- data/features/support/shared_components/screens/base_app_screen.rb +4 -0
- data/features/support/world_data.rb +4 -1
- data/features/support/world_pages.rb +4 -0
- data/lib/testcentricity/app_elements/alert.rb +64 -0
- data/lib/testcentricity/app_elements/app_element_helper.rb +55 -12
- data/lib/testcentricity/version.rb +1 -1
- data/testcentricity.gemspec +4 -4
- metadata +58 -43
- data/config/test_data/LOCAL_data.yml +0 -11
- data/features/support/ios/sections/list_items/product_cell_item.rb +0 -13
- data/features/support/ios/sections/modals/base_modal.rb +0 -23
- data/features/support/ios/sections/modals/logout_modal.rb +0 -6
- data/features/support/ios/sections/modals/reset_app_state_modal.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4c7b4120e8589701d29a850600108df295c1c199fa8c19e9f6f92bd76b49a47
|
4
|
+
data.tar.gz: 956e90ac6395ed32e69c2fd7558977398b8c42c075577d858090ce194da8c668
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b389a6ce9b701bb380ae02d34b7dc2880617aa315738a59082dfabf3c00fc2411d4f7ab99b3102f048918c9cc77e2e02b3b475fae3836986bfcc2237d7495112
|
7
|
+
data.tar.gz: b8d6b8ce2cb4cf7f98d66b8fe7ea7a9e4a1db0a07d3410660d5e6c06d962dc0bb48ce761d8998b4ac1475165ae042f8116109f049b8241e6eda7f64eb7147430
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
4
|
|
5
|
+
## [3.0.4] - 02-JUNE-2022
|
6
|
+
|
7
|
+
### Added
|
8
|
+
* Added `AppUIElement.wait_until_enabled` method.
|
9
|
+
|
10
|
+
### Updated
|
11
|
+
* Incorporated all changes from the [TestCentricity™ Web gem](https://rubygems.org/gems/testcentricity_web) version 4.2.4, which is
|
12
|
+
bundled with this gem.
|
13
|
+
|
14
|
+
|
15
|
+
## [3.0.3] - 30-MAY-2022
|
16
|
+
|
17
|
+
### Added
|
18
|
+
* Added `AppAlert.await_and_respond` method.
|
19
|
+
|
20
|
+
|
21
|
+
## [3.0.2] - 26-MAY-2022
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
* Added runtime dependencies `curb` and `json` to gemspec.
|
25
|
+
* Fixed CHANGELOG url in gemspec.
|
26
|
+
|
27
|
+
|
5
28
|
## [3.0.1] - 26-MAY-2022
|
6
29
|
|
7
30
|
### Added
|
data/README.md
CHANGED
@@ -536,6 +536,7 @@ With TestCentricity, all native app UI elements are based on the `AppUIElement`
|
|
536
536
|
element.wait_until_gone(seconds)
|
537
537
|
element.wait_until_visible(seconds)
|
538
538
|
element.wait_until_hidden(seconds)
|
539
|
+
element.wait_until_enabled(seconds)
|
539
540
|
element.wait_until_value_is(value, seconds)
|
540
541
|
element.wait_until_value_changes(seconds)
|
541
542
|
|
data/Rakefile
CHANGED
@@ -35,7 +35,7 @@ desc 'Run required specs and Cucumber features'
|
|
35
35
|
task required: [:required_specs, :android_sim, :ios_sim]
|
36
36
|
|
37
37
|
|
38
|
-
desc 'Run all specs
|
38
|
+
desc 'Run all specs'
|
39
39
|
task all_specs: [:required_specs, :web_specs]
|
40
40
|
|
41
41
|
|
@@ -52,7 +52,7 @@ end
|
|
52
52
|
desc 'Build and release new version'
|
53
53
|
task :release do
|
54
54
|
version = TestCentricity::VERSION
|
55
|
-
puts "Releasing #{version}, y/n?"
|
55
|
+
puts "Releasing version #{version} of TestCentricity gem, y/n?"
|
56
56
|
exit(1) unless $stdin.gets.chomp == 'y'
|
57
57
|
sh 'gem build testcentricity.gemspec && ' \
|
58
58
|
"gem push testcentricity-#{version}.gem"
|
data/config/cucumber.yml
CHANGED
@@ -21,6 +21,7 @@ bvt: <%= acceptance %>
|
|
21
21
|
#==============
|
22
22
|
|
23
23
|
report: <%= reports %> REPORTING=true
|
24
|
+
parallel: PARALLEL=true REPORTING=true --require features --format pretty --format html --out reports/test_results<%= ENV['TEST_ENV_NUMBER'] %>.html --format junit --out reports --format json --out reports/test_results<%= ENV['TEST_ENV_NUMBER'] %>.json
|
24
25
|
|
25
26
|
|
26
27
|
#==============
|
data/config/test_data/data.yml
CHANGED
@@ -8,3 +8,69 @@ Environments:
|
|
8
8
|
IOS_IPA_PATH: '/Users/Shared/Test_Apps/iOS/iOS-Real-Device-MyRNDemoApp.1.3.0-162.ipa'
|
9
9
|
ANDROID_APK_PATH: '/Users/Shared/Test_Apps/Android/Android-MyDemoAppRN.1.2.0.build-231.apk'
|
10
10
|
|
11
|
+
# This section is populated with user credential data required to test the Login screen features
|
12
|
+
User_creds:
|
13
|
+
valid_data:
|
14
|
+
username: 'bob@example.com'
|
15
|
+
password: '10203040'
|
16
|
+
invalid_user:
|
17
|
+
username: 'iggy.snicklefritz@example.com'
|
18
|
+
password: '10203040'
|
19
|
+
locked_account:
|
20
|
+
username: 'alice@example.com'
|
21
|
+
password: '10203040'
|
22
|
+
no_username:
|
23
|
+
password: '10203040'
|
24
|
+
no_password:
|
25
|
+
username: 'bob@example.com'
|
26
|
+
|
27
|
+
# This section is populated with Product data required for testing the Shopping features
|
28
|
+
Products:
|
29
|
+
1:
|
30
|
+
id: 1
|
31
|
+
name: 'Sauce Labs Backpack'
|
32
|
+
description: 'carry.allTheThings() with the sleek, streamlined Sly Pack that melds uncompromising style with unequaled laptop and tablet protection.'
|
33
|
+
price: '$29.99'
|
34
|
+
review: 4
|
35
|
+
colors: ['BLACK', 'BLUE', 'GRAY', 'RED']
|
36
|
+
defaultColor: 'BLACK'
|
37
|
+
2:
|
38
|
+
id: 2
|
39
|
+
name: 'Sauce Labs Bike Light'
|
40
|
+
description: "A red light isn't the desired state in testing but it sure helps when riding your bike at night. Water-resistant with 3 lighting modes, 1 AAA battery included."
|
41
|
+
price: '$9.99'
|
42
|
+
review: 4
|
43
|
+
colors: ['BLACK']
|
44
|
+
defaultColor: 'BLACK'
|
45
|
+
3:
|
46
|
+
id: 3
|
47
|
+
name: 'Sauce Labs Bolt T-Shirt'
|
48
|
+
description: 'Get your testing superhero on with the Sauce Labs bolt T-shirt. From American Apparel, 100% ringspun combed cotton, heather gray with red bolt.'
|
49
|
+
price: '$15.99'
|
50
|
+
review: 4
|
51
|
+
colors: ['BLACK']
|
52
|
+
defaultColor: 'BLACK'
|
53
|
+
4:
|
54
|
+
id: 4
|
55
|
+
name: 'Sauce Labs Fleece Jacket'
|
56
|
+
description: "It's not every day that you come across a midweight quarter-zip fleece jacket capable of handling everything from a relaxing day outdoors to a busy day at the office."
|
57
|
+
price: '$49.99'
|
58
|
+
review: 4
|
59
|
+
colors: ['GRAY']
|
60
|
+
defaultColor: 'GRAY'
|
61
|
+
5:
|
62
|
+
id: 5
|
63
|
+
name: 'Sauce Labs Onesie'
|
64
|
+
description: "Rib snap infant onesie for the junior automation engineer in development. Reinforced 3-snap bottom closure, two-needle hemmed sleeved and bottom won't unravel."
|
65
|
+
price: '$7.99'
|
66
|
+
review: 4
|
67
|
+
colors: ['BLACK', 'GRAY', 'RED']
|
68
|
+
defaultColor: 'RED'
|
69
|
+
6:
|
70
|
+
id: 6
|
71
|
+
name: 'Test.allTheThings() T-Shirt'
|
72
|
+
description: 'This classic Sauce Labs t-shirt is perfect to wear when cozying up to your keyboard to automate a few tests. Super-soft and comfy ringspun combed cotton.'
|
73
|
+
price: '$15.99'
|
74
|
+
review: 4
|
75
|
+
colors: ['BLACK', 'GRAY', 'RED']
|
76
|
+
defaultColor: 'RED'
|
data/features/deep_links.feature
CHANGED
@@ -24,3 +24,15 @@ Feature: App screen deep links
|
|
24
24
|
|Products |Checkout - Address |
|
25
25
|
|Products |Checkout - Payment |
|
26
26
|
|About |Products |
|
27
|
+
|
28
|
+
|
29
|
+
Scenario Outline: Verify Product Item screen can be directly accessed via deep links
|
30
|
+
Given I am on the Products screen
|
31
|
+
And I access the data for product id <product_id>
|
32
|
+
When I load the Product Item screen
|
33
|
+
Then I expect the Product Item screen to be correctly displayed
|
34
|
+
|
35
|
+
Examples:
|
36
|
+
|product_id |
|
37
|
+
|1 |
|
38
|
+
|5 |
|
data/features/navigation.feature
CHANGED
@@ -29,3 +29,15 @@ Feature: Screen Navigation
|
|
29
29
|
|Login |
|
30
30
|
|Webview |
|
31
31
|
|SauceBot Video |
|
32
|
+
|
33
|
+
|
34
|
+
Scenario Outline: Verify screen navigation features with popup modals
|
35
|
+
When I tap the <screen_name> navigation menu item
|
36
|
+
And I accept the popup request modal
|
37
|
+
Then I expect the <screen_name> screen to be correctly displayed
|
38
|
+
|
39
|
+
Examples:
|
40
|
+
|screen_name |
|
41
|
+
|QR Code Scanner |
|
42
|
+
|Geo Location |
|
43
|
+
|Biometrics |
|
@@ -18,6 +18,7 @@ When(/^I (?:click|tap) the ([^\"]*) navigation menu item$/) do |screen_name|
|
|
18
18
|
# find and navigate to the specified target page/screen
|
19
19
|
target_page = PageManager.find_page(screen_name)
|
20
20
|
target_page.navigate_to
|
21
|
+
PageManager.current_page = target_page
|
21
22
|
end
|
22
23
|
|
23
24
|
|
@@ -30,25 +31,9 @@ Then(/^I expect the (.*) (?:page|screen) to be correctly displayed$/) do |screen
|
|
30
31
|
end
|
31
32
|
|
32
33
|
|
33
|
-
When(/^I enter user credentials with (.*)$/) do |
|
34
|
-
|
35
|
-
|
36
|
-
username = 'bob@example.com'
|
37
|
-
password = '10203040'
|
38
|
-
when :invalid_user
|
39
|
-
username = 'iggy.snicklefritz@example.com'
|
40
|
-
password = '10203040'
|
41
|
-
when :locked_account
|
42
|
-
username = 'alice@example.com'
|
43
|
-
password = '10203040'
|
44
|
-
when :no_username
|
45
|
-
password = '10203040'
|
46
|
-
when :no_password
|
47
|
-
username = 'bob@example.com'
|
48
|
-
else
|
49
|
-
raise "#{reason} is not a valid selector"
|
50
|
-
end
|
51
|
-
login_screen.login(username, password)
|
34
|
+
When(/^I enter user credentials with (.*)$/) do |creds|
|
35
|
+
cred_data = user_data_source.find_user_creds(creds.gsub(/\s+/, '_').downcase)
|
36
|
+
login_screen.login(cred_data.username, cred_data.password)
|
52
37
|
end
|
53
38
|
|
54
39
|
|
@@ -70,3 +55,13 @@ end
|
|
70
55
|
Then(/^I expect the navigation menu to be hidden$/) do
|
71
56
|
PageManager.current_page.verify_nav_menu(state = :closed)
|
72
57
|
end
|
58
|
+
|
59
|
+
|
60
|
+
When(/^I (.*) the popup request modal$/) do |action|
|
61
|
+
PageManager.current_page.modal_action(action)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
When(/^I access the data for product id (.*)$/) do |product_id|
|
66
|
+
product_data_source.find_product(product_id)
|
67
|
+
end
|
@@ -8,6 +8,8 @@ class BaseAppScreen < TestCentricity::ScreenObject
|
|
8
8
|
cart_button: { accessibility_id: 'cart badge' }
|
9
9
|
labels header_label: { xpath: '//android.view.ViewGroup[@content-desc="container header"]/android.widget.TextView' },
|
10
10
|
cart_quantity: { xpath: '//android.view.ViewGroup[@content-desc="cart badge"]/android.widget.TextView' }
|
11
|
+
alerts grant_modal: { id: 'com.android.permissioncontroller:id/grant_dialog' },
|
12
|
+
alert_modal: { id: 'android:id/parentPanel' }
|
11
13
|
section :nav_menu, NavMenu
|
12
14
|
|
13
15
|
def verify_page_ui
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class BiometricsScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Biometrics' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'biometrics screen' } }
|
4
|
+
trait(:navigator) { nav_menu.open_biometrics }
|
5
|
+
|
6
|
+
# Biometrics screen UI elements
|
7
|
+
switch :fingerprint_switch, { accessibility_id: 'biometrics switch'}
|
8
|
+
|
9
|
+
def verify_page_ui
|
10
|
+
super
|
11
|
+
ui = {
|
12
|
+
header_label => { visible: true, caption: 'FingerPrint' },
|
13
|
+
fingerprint_switch => { visible: true, enabled: false }
|
14
|
+
}
|
15
|
+
verify_ui_states(ui)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class GeoLocationScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Geo Location' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'geo location screen' } }
|
4
|
+
trait(:page_url) { 'geo-locations' }
|
5
|
+
trait(:navigator) { nav_menu.open_geo_location }
|
6
|
+
|
7
|
+
# Geo Location screen UI elements
|
8
|
+
buttons start_observing_button: { accessibility_id: 'Start Observing button'},
|
9
|
+
stop_observing_button: { accessibility_id: 'Stop Observing button'}
|
10
|
+
labels latitude_data: { accessibility_id: 'latitude data' },
|
11
|
+
longitude_data: { accessibility_id: 'longitude data' }
|
12
|
+
|
13
|
+
def verify_page_ui
|
14
|
+
super
|
15
|
+
ui = {
|
16
|
+
header_label => { visible: true, caption: 'Geo Location' },
|
17
|
+
start_observing_button => { visible: true, enabled: false, caption: 'Start Observing' },
|
18
|
+
stop_observing_button => { visible: true, enabled: true, caption: 'Stop Observing' },
|
19
|
+
latitude_data => { visible: true, caption: { not_equal: '0' } },
|
20
|
+
longitude_data => { visible: true, caption: { not_equal: '0' } }
|
21
|
+
}
|
22
|
+
verify_ui_states(ui)
|
23
|
+
end
|
24
|
+
|
25
|
+
def modal_action(action)
|
26
|
+
grant_modal.await_and_respond(action.downcase.to_sym, timeout = 1, button_name = 'Only this time')
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class ProductItemScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Product Item' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'product screen' } }
|
4
|
+
trait(:page_url) { "product-details/#{ProductData.current.id.to_s}" }
|
5
|
+
|
6
|
+
# Product Item screen UI elements
|
7
|
+
labels price_value: { accessibility_id: 'product price' },
|
8
|
+
description_value: { accessibility_id: 'product description' },
|
9
|
+
quantity_value: { accessibility_id: 'counter amount' },
|
10
|
+
highlights_label: { xpath: '//android.view.ViewGroup/android.widget.TextView[2]' }
|
11
|
+
image :product_image, { xpath: '//android.view.ViewGroup/android.widget.ImageView' }
|
12
|
+
buttons add_to_cart_button: { accessibility_id: 'Add To Cart button' },
|
13
|
+
minus_qty_button: { accessibility_id: 'counter minus button' },
|
14
|
+
plus_qty_button: { accessibility_id: 'counter plus button' },
|
15
|
+
black_color_button: { accessibility_id: 'black circle' },
|
16
|
+
blue_color_button: { accessibility_id: 'blue circle' },
|
17
|
+
gray_color_button: { accessibility_id: 'gray circle' },
|
18
|
+
red_color_button: { accessibility_id: 'red circle' }
|
19
|
+
|
20
|
+
def verify_page_ui
|
21
|
+
super
|
22
|
+
ui = {
|
23
|
+
header_label => { visible: true, caption: ProductData.current.name },
|
24
|
+
price_value => { visible: true, caption: ProductData.current.price },
|
25
|
+
highlights_label => { visible: true, caption: 'Product Highlights' },
|
26
|
+
description_value => { visible: true, caption: ProductData.current.description },
|
27
|
+
product_image => { visible: true, enabled: true },
|
28
|
+
black_color_button => { visible: ProductData.current.colors.include?('BLACK') },
|
29
|
+
blue_color_button => { visible: ProductData.current.colors.include?('BLUE') },
|
30
|
+
gray_color_button => { visible: ProductData.current.colors.include?('GRAY') },
|
31
|
+
red_color_button => { visible: ProductData.current.colors.include?('RED') },
|
32
|
+
minus_qty_button => { visible: true, enabled: true },
|
33
|
+
quantity_value => { visible: true, enabled: true, caption: '1' },
|
34
|
+
plus_qty_button => { visible: true, enabled: true },
|
35
|
+
add_to_cart_button => { visible: true, enabled: true, caption: 'Add To Cart' }
|
36
|
+
}
|
37
|
+
verify_ui_states(ui)
|
38
|
+
end
|
39
|
+
end
|
@@ -3,10 +3,24 @@ class ProductsScreen < BaseAppScreen
|
|
3
3
|
trait(:page_locator) { { accessibility_id: 'products screen' } }
|
4
4
|
trait(:page_url) { 'store-overview' }
|
5
5
|
|
6
|
+
# Products screen UI elements
|
7
|
+
list :product_list, { xpath: '//android.widget.ScrollView' }
|
8
|
+
section :product_list_item, ProductListItem
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
# define the list item element for the Product list object
|
13
|
+
list_elements = { list_item: { xpath: '//android.view.ViewGroup[@content-desc="store item"]' } }
|
14
|
+
product_list.define_list_elements(list_elements)
|
15
|
+
# associate the Product List Item indexed section object with the Product list object
|
16
|
+
product_list_item.set_list_index(product_list)
|
17
|
+
end
|
18
|
+
|
6
19
|
def verify_page_ui
|
7
20
|
super
|
8
21
|
ui = {
|
9
|
-
header_label => { visible: true, caption: 'Products' }
|
22
|
+
header_label => { visible: true, caption: 'Products' },
|
23
|
+
product_list => { visible: true, itemcount: 6 }
|
10
24
|
}
|
11
25
|
verify_ui_states(ui)
|
12
26
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class QRCodeScannerScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'QR Code Scanner' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'qr code screen' } }
|
4
|
+
trait(:page_url) { 'qr-code-scanner' }
|
5
|
+
trait(:navigator) { nav_menu.open_qr_code_scanner }
|
6
|
+
|
7
|
+
def verify_page_ui
|
8
|
+
super
|
9
|
+
verify_ui_states(header_label => { visible: true, caption: 'QR Code Scanner' })
|
10
|
+
end
|
11
|
+
|
12
|
+
def modal_action(action)
|
13
|
+
grant_modal.await_and_respond(action.downcase.to_sym, timeout = 1, button_name = 'Only this time')
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ProductListItem < TestCentricity::ScreenSection
|
2
|
+
trait(:section_name) { 'Product List Item' }
|
3
|
+
trait(:section_locator) { { xpath: '//android.view.ViewGroup[@content-desc="store item"]' } }
|
4
|
+
|
5
|
+
# Product Cell Item UI elements
|
6
|
+
labels product_name: { xpath: '//android.widget.TextView[@content-desc="store item text"]' },
|
7
|
+
product_price: { xpath: '//android.widget.TextView[@content-desc="store item price"]' }
|
8
|
+
buttons review_star_1: { xpath: '//android.view.ViewGroup[@content-desc="review star 1"]' },
|
9
|
+
review_star_2: { xpath: '//android.view.ViewGroup[@content-desc="review star 2"]' },
|
10
|
+
review_star_3: { xpath: '//android.view.ViewGroup[@content-desc="review star 3"]' },
|
11
|
+
review_star_4: { xpath: '//android.view.ViewGroup[@content-desc="review star 4"]' },
|
12
|
+
review_star_5: { xpath: '//android.view.ViewGroup[@content-desc="review star 5"]' }
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ProductDataSource < TestCentricity::DataSource
|
2
|
+
def find_product(product_id)
|
3
|
+
ProductData.current = ProductData.new(environs.read('Products', product_id.to_i))
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class ProductData < TestCentricity::DataPresenter
|
9
|
+
attribute :id, Integer
|
10
|
+
attribute :name, String
|
11
|
+
attribute :description, String
|
12
|
+
attribute :price, String
|
13
|
+
attribute :review, Integer
|
14
|
+
attribute :colors, Array
|
15
|
+
attribute :default_color, String
|
16
|
+
|
17
|
+
def initialize(data)
|
18
|
+
@id = data[:id]
|
19
|
+
@name = data[:name]
|
20
|
+
@description = data[:description]
|
21
|
+
@price = data[:price]
|
22
|
+
@review = data[:review]
|
23
|
+
@colors = data[:colors]
|
24
|
+
@default_color = data[:defaultColor]
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class UserDataSource < TestCentricity::DataSource
|
2
|
+
def find_user_creds(node_name)
|
3
|
+
UserData.current = UserData.new(environs.read('User_creds', node_name))
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class UserData < TestCentricity::DataPresenter
|
9
|
+
attribute :username, String
|
10
|
+
attribute :password, String
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
@username = data[:username]
|
14
|
+
@password = data[:password]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -4,8 +4,8 @@ require 'capybara/cucumber'
|
|
4
4
|
require 'parallel_tests'
|
5
5
|
require 'require_all'
|
6
6
|
require 'simplecov'
|
7
|
-
require 'testcentricity'
|
8
7
|
require 'testcentricity_web'
|
8
|
+
require 'testcentricity'
|
9
9
|
|
10
10
|
include TestCentricity
|
11
11
|
|
@@ -14,6 +14,7 @@ SimpleCov.command_name("Features-#{ENV['PLATFORM']}-#{Time.now.strftime('%Y%m%d%
|
|
14
14
|
require_relative 'world_data'
|
15
15
|
require_relative 'world_pages'
|
16
16
|
|
17
|
+
require_rel 'data'
|
17
18
|
require_rel 'shared_components'
|
18
19
|
|
19
20
|
# conditionally require page and section objects based on target platform
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class BiometricsScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Biometrics' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'biometrics screen' } }
|
4
|
+
trait(:navigator) { nav_menu.open_biometrics }
|
5
|
+
|
6
|
+
# Biometrics screen UI elements
|
7
|
+
switch :face_id_switch, { accessibility_id: 'biometrics switch'}
|
8
|
+
|
9
|
+
def verify_page_ui
|
10
|
+
super
|
11
|
+
ui = {
|
12
|
+
header_label => { visible: true, caption: 'FaceID' },
|
13
|
+
face_id_switch => { visible: true, enabled: false, value: '0' }
|
14
|
+
}
|
15
|
+
verify_ui_states(ui)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class GeoLocationScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Geo Location' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'geo location screen' } }
|
4
|
+
trait(:page_url) { 'geo-locations' }
|
5
|
+
trait(:navigator) { nav_menu.open_geo_location }
|
6
|
+
|
7
|
+
# Geo Location screen UI elements
|
8
|
+
buttons start_observing_button: { accessibility_id: 'Start Observing button'},
|
9
|
+
stop_observing_button: { accessibility_id: 'Stop Observing button'}
|
10
|
+
labels latitude_label: { accessibility_id: 'Latitude:' },
|
11
|
+
latitude_data: { accessibility_id: 'latitude data' },
|
12
|
+
longitude_label: { accessibility_id: 'Longitude:' },
|
13
|
+
longitude_data: { accessibility_id: 'longitude data' }
|
14
|
+
|
15
|
+
def verify_page_ui
|
16
|
+
super
|
17
|
+
ui = {
|
18
|
+
header_label => { visible: true, caption: 'Geo Location' },
|
19
|
+
start_observing_button => { visible: true, enabled: false, caption: 'Start Observing' },
|
20
|
+
stop_observing_button => { visible: true, enabled: true, caption: 'Stop Observing' },
|
21
|
+
latitude_label => { visible: true, caption: 'Latitude:' },
|
22
|
+
latitude_data => { visible: true, caption: { not_equal: '0' } },
|
23
|
+
longitude_label => { visible: true, caption: 'Longitude:' },
|
24
|
+
longitude_data => { visible: true, caption: { not_equal: '0' } }
|
25
|
+
}
|
26
|
+
verify_ui_states(ui)
|
27
|
+
end
|
28
|
+
|
29
|
+
def modal_action(action)
|
30
|
+
alert_modal.await_and_respond(action.downcase.to_sym, timeout = 1, button_name = 'Allow Once')
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class ProductItemScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'Product Item' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'product screen' } }
|
4
|
+
trait(:page_url) { "product-details/#{ProductData.current.id.to_s}" }
|
5
|
+
|
6
|
+
# Product Item screen UI elements
|
7
|
+
labels price_value: { accessibility_id: 'product price' },
|
8
|
+
description_value: { accessibility_id: 'product description' },
|
9
|
+
quantity_value: { accessibility_id: 'counter amount' },
|
10
|
+
highlights_label: { accessibility_id: 'Product Highlights' }
|
11
|
+
image :product_image, { xpath: '//XCUIElementTypeImage' }
|
12
|
+
buttons add_to_cart_button: { accessibility_id: 'Add To Cart button' },
|
13
|
+
minus_qty_button: { accessibility_id: 'counter minus button' },
|
14
|
+
plus_qty_button: { accessibility_id: 'counter plus button' },
|
15
|
+
black_color_button: { accessibility_id: 'black circle' },
|
16
|
+
blue_color_button: { accessibility_id: 'blue circle' },
|
17
|
+
gray_color_button: { accessibility_id: 'gray circle' },
|
18
|
+
red_color_button: { accessibility_id: 'red circle' }
|
19
|
+
|
20
|
+
def verify_page_ui
|
21
|
+
super
|
22
|
+
ui = {
|
23
|
+
header_label => { visible: true, caption: ProductData.current.name },
|
24
|
+
price_value => { visible: true, caption: ProductData.current.price },
|
25
|
+
highlights_label => { visible: true, caption: 'Product Highlights' },
|
26
|
+
description_value => { visible: true, caption: ProductData.current.description },
|
27
|
+
product_image => { visible: true, enabled: true },
|
28
|
+
black_color_button => { visible: ProductData.current.colors.include?('BLACK') },
|
29
|
+
blue_color_button => { visible: ProductData.current.colors.include?('BLUE') },
|
30
|
+
gray_color_button => { visible: ProductData.current.colors.include?('GRAY') },
|
31
|
+
red_color_button => { visible: ProductData.current.colors.include?('RED') },
|
32
|
+
minus_qty_button => { visible: true, enabled: true },
|
33
|
+
quantity_value => { visible: true, enabled: true, caption: '1' },
|
34
|
+
plus_qty_button => { visible: true, enabled: true },
|
35
|
+
add_to_cart_button => { visible: true, enabled: true, caption: 'Add To Cart' }
|
36
|
+
}
|
37
|
+
verify_ui_states(ui)
|
38
|
+
end
|
39
|
+
end
|
@@ -3,10 +3,24 @@ class ProductsScreen < BaseAppScreen
|
|
3
3
|
trait(:page_locator) { { accessibility_id: 'products screen' } }
|
4
4
|
trait(:page_url) { 'store-overview' }
|
5
5
|
|
6
|
+
# Products screen UI elements
|
7
|
+
list :product_list, { xpath: '//XCUIElementTypeScrollView/XCUIElementTypeOther[1]' }
|
8
|
+
section :product_list_item, ProductListItem
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
# define the list item element for the Product list object
|
13
|
+
list_elements = { list_item: { xpath: '//XCUIElementTypeOther[@name="store item"]' } }
|
14
|
+
product_list.define_list_elements(list_elements)
|
15
|
+
# associate the Product List Item indexed section object with the Product list object
|
16
|
+
product_list_item.set_list_index(product_list)
|
17
|
+
end
|
18
|
+
|
6
19
|
def verify_page_ui
|
7
20
|
super
|
8
21
|
ui = {
|
9
|
-
header_label => { visible: true, caption: 'Products' }
|
22
|
+
header_label => { visible: true, caption: 'Products' },
|
23
|
+
# product_list => { visible: true, itemcount: 6 }
|
10
24
|
}
|
11
25
|
verify_ui_states(ui)
|
12
26
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class QRCodeScannerScreen < BaseAppScreen
|
2
|
+
trait(:page_name) { 'QR Code Scanner' }
|
3
|
+
trait(:page_locator) { { accessibility_id: 'qr code screen' } }
|
4
|
+
trait(:page_url) { 'qr-code-scanner' }
|
5
|
+
trait(:navigator) { nav_menu.open_qr_code_scanner }
|
6
|
+
|
7
|
+
def verify_page_ui
|
8
|
+
super
|
9
|
+
verify_ui_states(header_label => { visible: true, caption: 'QR Code Scanner' })
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ProductListItem < TestCentricity::ScreenSection
|
2
|
+
trait(:section_name) { 'Product List Item' }
|
3
|
+
trait(:section_locator) { { xpath: '//XCUIElementTypeOther[@name="store item"]' } }
|
4
|
+
|
5
|
+
# Product Cell Item UI elements
|
6
|
+
labels product_name: { xpath: '//XCUIElementTypeStaticText[@name="store item text"]' },
|
7
|
+
product_price: { xpath: '//XCUIElementTypeStaticText[@name="store item price"]' }
|
8
|
+
buttons review_star_1: { xpath: '//XCUIElementTypeOther[@name="review star 1"]' },
|
9
|
+
review_star_2: { xpath: '//XCUIElementTypeOther[@name="review star 2"]' },
|
10
|
+
review_star_3: { xpath: '//XCUIElementTypeOther[@name="review star 3"]' },
|
11
|
+
review_star_4: { xpath: '//XCUIElementTypeOther[@name="review star 4"]' },
|
12
|
+
review_star_5: { xpath: '//XCUIElementTypeOther[@name="review star 5"]' }
|
13
|
+
end
|
@@ -4,7 +4,10 @@ module WorldData
|
|
4
4
|
# by the TestCentricity™ DataManager. Data Object class definitions are contained in the features/support/data folder.
|
5
5
|
#
|
6
6
|
def data_objects
|
7
|
-
{
|
7
|
+
{
|
8
|
+
user_data_source: UserDataSource,
|
9
|
+
product_data_source: ProductDataSource
|
10
|
+
}
|
8
11
|
end
|
9
12
|
end
|
10
13
|
|
@@ -15,8 +15,12 @@ module WorldPages
|
|
15
15
|
login_screen: LoginScreen,
|
16
16
|
webview_screen: WebViewScreen,
|
17
17
|
products_screen: ProductsScreen,
|
18
|
+
product_item_screen: ProductItemScreen,
|
18
19
|
checkout_address_screen: CheckoutAddressScreen,
|
19
20
|
checkout_payment_screen: CheckoutPaymentScreen,
|
21
|
+
qr_code_scanner_screen: QRCodeScannerScreen,
|
22
|
+
geo_location_screen: GeoLocationScreen,
|
23
|
+
biometrics_screen: BiometricsScreen,
|
20
24
|
saucebot_video_screen: SauceBotScreen
|
21
25
|
}
|
22
26
|
end
|
@@ -5,6 +5,16 @@ module TestCentricity
|
|
5
5
|
@type = :alert
|
6
6
|
end
|
7
7
|
|
8
|
+
# Wait until the alert modal is visible, or until the specified wait time has expired. If the wait time is nil, then the
|
9
|
+
# wait time will be Environ.default_max_wait_time. Unlike wait_until_visible or wait_until_exists, this method does not
|
10
|
+
# raise an exception if the alert modal does not appear within the specified wait time.
|
11
|
+
# Returns true if the alert modal is visible.
|
12
|
+
#
|
13
|
+
# @return [Integer]
|
14
|
+
# @param seconds [Integer or Float] wait time in seconds
|
15
|
+
# @example
|
16
|
+
# permissions_modal.await(2)
|
17
|
+
#
|
8
18
|
def await(seconds)
|
9
19
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
10
20
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
@@ -14,14 +24,68 @@ module TestCentricity
|
|
14
24
|
false
|
15
25
|
end
|
16
26
|
|
27
|
+
# Performs the action required accept the currently visible alert modal. If the alert modal is still visible after
|
28
|
+
# 5 seconds, an exception is raised.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# alert_modal.accept
|
32
|
+
#
|
17
33
|
def accept
|
18
34
|
alert_accept
|
19
35
|
wait_until_gone(5)
|
20
36
|
end
|
21
37
|
|
38
|
+
# Performs the action required dismiss the currently visible alert modal. If the alert modal is still visible after
|
39
|
+
# 5 seconds, an exception is raised.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# alert_modal.dismiss
|
43
|
+
#
|
22
44
|
def dismiss
|
23
45
|
alert_dismiss
|
24
46
|
wait_until_gone(5)
|
25
47
|
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @return [Integer]
|
51
|
+
# @param action [Symbol] action to perform if alert modal is visible. Acceptable values are :allow, :accept, :dont_allow, or :dismiss
|
52
|
+
# @param timeout [Integer or Float] wait time in seconds. Defaults to 2 if not specified
|
53
|
+
# @param button_name [String] optional caption of button to tap associated with the specified action
|
54
|
+
# @example
|
55
|
+
# alert_modal.await_and_respond(:dismiss)
|
56
|
+
# OR
|
57
|
+
# permissions_modal.await_and_respond(:accept, timeout = 1, button_name = 'Allow Once')
|
58
|
+
#
|
59
|
+
def await_and_respond(action, timeout = 2, button_name = nil)
|
60
|
+
if await(timeout)
|
61
|
+
case action
|
62
|
+
when :allow, :accept
|
63
|
+
if button_name.nil?
|
64
|
+
accept
|
65
|
+
elsif Environ.is_ios?
|
66
|
+
$driver.execute_script('mobile: alert', { action: 'accept', buttonLabel: button_name })
|
67
|
+
else
|
68
|
+
$driver.execute_script('mobile: acceptAlert', { buttonLabel: button_name })
|
69
|
+
end
|
70
|
+
when :dont_allow, :dismiss
|
71
|
+
if button_name.nil?
|
72
|
+
dismiss
|
73
|
+
elsif Environ.is_ios?
|
74
|
+
$driver.execute_script('mobile: alert', { action: 'dismiss', buttonLabel: button_name })
|
75
|
+
else
|
76
|
+
$driver.execute_script('mobile: dismissAlert', { buttonLabel: button_name })
|
77
|
+
end
|
78
|
+
else
|
79
|
+
raise "#{action} is not a valid selector"
|
80
|
+
end
|
81
|
+
if Environ.is_ios? && await(1)
|
82
|
+
buttons = $driver.execute_script('mobile: alert', { action: 'getButtons' })
|
83
|
+
raise "Could not perform #{action} action on active modal. Available modal buttons are #{buttons}"
|
84
|
+
end
|
85
|
+
true
|
86
|
+
else
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
26
90
|
end
|
27
91
|
end
|
@@ -174,12 +174,16 @@ module TestCentricity
|
|
174
174
|
# @example
|
175
175
|
# run_button.wait_until_exists(0.5)
|
176
176
|
#
|
177
|
-
def wait_until_exists(seconds = nil)
|
177
|
+
def wait_until_exists(seconds = nil, post_exception = true)
|
178
178
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
179
179
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
180
180
|
wait.until { exists? }
|
181
181
|
rescue
|
182
|
-
|
182
|
+
if post_exception
|
183
|
+
raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless exists?
|
184
|
+
else
|
185
|
+
exists?
|
186
|
+
end
|
183
187
|
end
|
184
188
|
|
185
189
|
# Wait until the object no longer exists, or until the specified wait time has expired. If the wait time is nil, then
|
@@ -189,12 +193,16 @@ module TestCentricity
|
|
189
193
|
# @example
|
190
194
|
# logout_button.wait_until_gone(5)
|
191
195
|
#
|
192
|
-
def wait_until_gone(seconds = nil)
|
196
|
+
def wait_until_gone(seconds = nil, post_exception = true)
|
193
197
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
194
198
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
195
199
|
wait.until { !exists? }
|
196
200
|
rescue
|
197
|
-
|
201
|
+
if post_exception
|
202
|
+
raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if exists?
|
203
|
+
else
|
204
|
+
exists?
|
205
|
+
end
|
198
206
|
end
|
199
207
|
|
200
208
|
# Wait until the object is visible, or until the specified wait time has expired. If the wait time is nil, then the
|
@@ -204,12 +212,16 @@ module TestCentricity
|
|
204
212
|
# @example
|
205
213
|
# run_button.wait_until_visible(0.5)
|
206
214
|
#
|
207
|
-
def wait_until_visible(seconds = nil)
|
215
|
+
def wait_until_visible(seconds = nil, post_exception = true)
|
208
216
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
209
217
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
210
218
|
wait.until { visible? }
|
211
219
|
rescue
|
212
|
-
|
220
|
+
if post_exception
|
221
|
+
raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless visible?
|
222
|
+
else
|
223
|
+
visible?
|
224
|
+
end
|
213
225
|
end
|
214
226
|
|
215
227
|
# Wait until the object is hidden, or until the specified wait time has expired. If the wait time is nil, then the
|
@@ -219,12 +231,35 @@ module TestCentricity
|
|
219
231
|
# @example
|
220
232
|
# run_button.wait_until_hidden(10)
|
221
233
|
#
|
222
|
-
def wait_until_hidden(seconds = nil)
|
234
|
+
def wait_until_hidden(seconds = nil, post_exception = true)
|
223
235
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
224
236
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
225
237
|
wait.until { hidden? }
|
226
238
|
rescue
|
227
|
-
|
239
|
+
if post_exception
|
240
|
+
raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if visible?
|
241
|
+
else
|
242
|
+
hidden?
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Wait until the object is enabled, or until the specified wait time has expired. If the wait time is nil, then the
|
247
|
+
# wait time will be Environ.default_max_wait_time.
|
248
|
+
#
|
249
|
+
# @param seconds [Integer or Float] wait time in seconds
|
250
|
+
# @example
|
251
|
+
# run_button.wait_until_enabled(10)
|
252
|
+
#
|
253
|
+
def wait_until_enabled(seconds = nil, post_exception = true)
|
254
|
+
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
255
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
256
|
+
wait.until { enabled? }
|
257
|
+
rescue
|
258
|
+
if post_exception
|
259
|
+
raise "UI #{object_ref_message} remained disabled after #{timeout} seconds" unless enabled?
|
260
|
+
else
|
261
|
+
enabled?
|
262
|
+
end
|
228
263
|
end
|
229
264
|
|
230
265
|
# Wait until the object's value equals the specified value, or until the specified wait time has expired. If the wait
|
@@ -237,12 +272,16 @@ module TestCentricity
|
|
237
272
|
# or
|
238
273
|
# total_weight_field.wait_until_value_is({ :greater_than => '250' }, 5)
|
239
274
|
#
|
240
|
-
def wait_until_value_is(value, seconds = nil)
|
275
|
+
def wait_until_value_is(value, seconds = nil, post_exception = true)
|
241
276
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
242
277
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
243
278
|
wait.until { compare(value, get_value) }
|
244
279
|
rescue
|
245
|
-
|
280
|
+
if post_exception
|
281
|
+
raise "Value of UI #{object_ref_message} failed to equal '#{value}' after #{timeout} seconds" unless get_value == value
|
282
|
+
else
|
283
|
+
get_value == value
|
284
|
+
end
|
246
285
|
end
|
247
286
|
|
248
287
|
# Wait until the object's value changes to a different value, or until the specified wait time has expired. If the
|
@@ -252,13 +291,17 @@ module TestCentricity
|
|
252
291
|
# @example
|
253
292
|
# basket_grand_total_label.wait_until_value_changes(5)
|
254
293
|
#
|
255
|
-
def wait_until_value_changes(seconds = nil)
|
294
|
+
def wait_until_value_changes(seconds = nil, post_exception = true)
|
256
295
|
value = get_value
|
257
296
|
timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
|
258
297
|
wait = Selenium::WebDriver::Wait.new(timeout: timeout)
|
259
298
|
wait.until { get_value != value }
|
260
299
|
rescue
|
261
|
-
|
300
|
+
if post_exception
|
301
|
+
raise "Value of UI #{object_ref_message} failed to change from '#{value}' after #{timeout} seconds" if get_value == value
|
302
|
+
else
|
303
|
+
get_value == value
|
304
|
+
end
|
262
305
|
end
|
263
306
|
|
264
307
|
def width
|
data/testcentricity.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.required_ruby_version = '>= 2.7.5'
|
11
11
|
spec.authors = ['A.J. Mrozinski']
|
12
12
|
spec.email = ['testcentricity@gmail.com']
|
13
|
-
spec.summary = %q{A Page Object Model Framework for
|
13
|
+
spec.summary = %q{A Page Object Model Framework for native mobile app and desktop/mobile web portal testing}
|
14
14
|
spec.description = '
|
15
15
|
The TestCentricity™ core framework for native mobile iOS and Android apps and desktop/mobile web testing implements a Page Object
|
16
16
|
Model DSL for use with Cucumber, Appium, Capybara, and Selenium-Webdriver v4.x. The gem also facilitates the configuration of the
|
@@ -33,8 +33,6 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency 'bundler'
|
34
34
|
spec.add_development_dependency 'cucumber'
|
35
35
|
spec.add_development_dependency 'cuke_modeler', '~> 3.0'
|
36
|
-
spec.add_development_dependency 'curb'
|
37
|
-
spec.add_development_dependency 'json'
|
38
36
|
spec.add_development_dependency 'parallel_tests'
|
39
37
|
spec.add_development_dependency 'rake'
|
40
38
|
spec.add_development_dependency 'require_all', '=1.5.0'
|
@@ -43,6 +41,8 @@ Gem::Specification.new do |spec|
|
|
43
41
|
spec.add_development_dependency 'yard', ['>= 0.9.0']
|
44
42
|
|
45
43
|
spec.add_runtime_dependency 'appium_lib'
|
46
|
-
spec.add_runtime_dependency '
|
44
|
+
spec.add_runtime_dependency 'curb'
|
45
|
+
spec.add_runtime_dependency 'json'
|
46
|
+
spec.add_runtime_dependency 'testcentricity_web', '>= 4.2.4'
|
47
47
|
spec.add_runtime_dependency 'test-unit'
|
48
48
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: testcentricity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- A.J. Mrozinski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,34 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: curb
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: json
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: parallel_tests
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,20 +150,48 @@ dependencies:
|
|
178
150
|
- - ">="
|
179
151
|
- !ruby/object:Gem::Version
|
180
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: curb
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: json
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: testcentricity_web
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
185
|
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: 4.2.
|
187
|
+
version: 4.2.4
|
188
188
|
type: :runtime
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - ">="
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: 4.2.
|
194
|
+
version: 4.2.4
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: test-unit
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -233,7 +233,6 @@ files:
|
|
233
233
|
- Rakefile
|
234
234
|
- config/cucumber.yml
|
235
235
|
- config/locales/en-US.yml
|
236
|
-
- config/test_data/LOCAL_data.yml
|
237
236
|
- config/test_data/data.yml
|
238
237
|
- features/deep_links.feature
|
239
238
|
- features/login.feature
|
@@ -241,27 +240,35 @@ files:
|
|
241
240
|
- features/step_definitions/generic_steps.rb
|
242
241
|
- features/support/android/screens/about_screen.rb
|
243
242
|
- features/support/android/screens/base_app_screen.rb
|
243
|
+
- features/support/android/screens/biometrics_screen.rb
|
244
244
|
- features/support/android/screens/checkout_address_screen.rb
|
245
245
|
- features/support/android/screens/checkout_payment_screen.rb
|
246
|
+
- features/support/android/screens/geo_location_screen.rb
|
246
247
|
- features/support/android/screens/login_screen.rb
|
248
|
+
- features/support/android/screens/product_item_screen.rb
|
247
249
|
- features/support/android/screens/products_screen.rb
|
250
|
+
- features/support/android/screens/qr_code_scanner_screen.rb
|
248
251
|
- features/support/android/screens/saucebot_screen.rb
|
249
252
|
- features/support/android/screens/webview_screen.rb
|
253
|
+
- features/support/android/sections/list_items/product_list_item.rb
|
250
254
|
- features/support/android/sections/nav_widgets/nav_menu.rb
|
255
|
+
- features/support/data/product_data.rb
|
256
|
+
- features/support/data/user_data.rb
|
251
257
|
- features/support/env.rb
|
252
258
|
- features/support/hooks.rb
|
253
259
|
- features/support/ios/screens/about_screen.rb
|
254
260
|
- features/support/ios/screens/base_app_screen.rb
|
261
|
+
- features/support/ios/screens/biometrics_screen.rb
|
255
262
|
- features/support/ios/screens/checkout_address_screen.rb
|
256
263
|
- features/support/ios/screens/checkout_payment_screen.rb
|
264
|
+
- features/support/ios/screens/geo_location_screen.rb
|
257
265
|
- features/support/ios/screens/login_screen.rb
|
266
|
+
- features/support/ios/screens/product_item_screen.rb
|
258
267
|
- features/support/ios/screens/products_screen.rb
|
268
|
+
- features/support/ios/screens/qr_code_scanner_screen.rb
|
259
269
|
- features/support/ios/screens/saucebot_screen.rb
|
260
270
|
- features/support/ios/screens/webview_screen.rb
|
261
|
-
- features/support/ios/sections/list_items/
|
262
|
-
- features/support/ios/sections/modals/base_modal.rb
|
263
|
-
- features/support/ios/sections/modals/logout_modal.rb
|
264
|
-
- features/support/ios/sections/modals/reset_app_state_modal.rb
|
271
|
+
- features/support/ios/sections/list_items/product_list_item.rb
|
265
272
|
- features/support/ios/sections/nav_widgets/nav_bar.rb
|
266
273
|
- features/support/ios/sections/nav_widgets/nav_menu.rb
|
267
274
|
- features/support/shared_components/screens/base_app_screen.rb
|
@@ -337,8 +344,8 @@ requirements:
|
|
337
344
|
rubygems_version: 3.1.6
|
338
345
|
signing_key:
|
339
346
|
specification_version: 4
|
340
|
-
summary: A Page Object Model Framework for
|
341
|
-
testing
|
347
|
+
summary: A Page Object Model Framework for native mobile app and desktop/mobile web
|
348
|
+
portal testing
|
342
349
|
test_files:
|
343
350
|
- features/deep_links.feature
|
344
351
|
- features/login.feature
|
@@ -346,27 +353,35 @@ test_files:
|
|
346
353
|
- features/step_definitions/generic_steps.rb
|
347
354
|
- features/support/android/screens/about_screen.rb
|
348
355
|
- features/support/android/screens/base_app_screen.rb
|
356
|
+
- features/support/android/screens/biometrics_screen.rb
|
349
357
|
- features/support/android/screens/checkout_address_screen.rb
|
350
358
|
- features/support/android/screens/checkout_payment_screen.rb
|
359
|
+
- features/support/android/screens/geo_location_screen.rb
|
351
360
|
- features/support/android/screens/login_screen.rb
|
361
|
+
- features/support/android/screens/product_item_screen.rb
|
352
362
|
- features/support/android/screens/products_screen.rb
|
363
|
+
- features/support/android/screens/qr_code_scanner_screen.rb
|
353
364
|
- features/support/android/screens/saucebot_screen.rb
|
354
365
|
- features/support/android/screens/webview_screen.rb
|
366
|
+
- features/support/android/sections/list_items/product_list_item.rb
|
355
367
|
- features/support/android/sections/nav_widgets/nav_menu.rb
|
368
|
+
- features/support/data/product_data.rb
|
369
|
+
- features/support/data/user_data.rb
|
356
370
|
- features/support/env.rb
|
357
371
|
- features/support/hooks.rb
|
358
372
|
- features/support/ios/screens/about_screen.rb
|
359
373
|
- features/support/ios/screens/base_app_screen.rb
|
374
|
+
- features/support/ios/screens/biometrics_screen.rb
|
360
375
|
- features/support/ios/screens/checkout_address_screen.rb
|
361
376
|
- features/support/ios/screens/checkout_payment_screen.rb
|
377
|
+
- features/support/ios/screens/geo_location_screen.rb
|
362
378
|
- features/support/ios/screens/login_screen.rb
|
379
|
+
- features/support/ios/screens/product_item_screen.rb
|
363
380
|
- features/support/ios/screens/products_screen.rb
|
381
|
+
- features/support/ios/screens/qr_code_scanner_screen.rb
|
364
382
|
- features/support/ios/screens/saucebot_screen.rb
|
365
383
|
- features/support/ios/screens/webview_screen.rb
|
366
|
-
- features/support/ios/sections/list_items/
|
367
|
-
- features/support/ios/sections/modals/base_modal.rb
|
368
|
-
- features/support/ios/sections/modals/logout_modal.rb
|
369
|
-
- features/support/ios/sections/modals/reset_app_state_modal.rb
|
384
|
+
- features/support/ios/sections/list_items/product_list_item.rb
|
370
385
|
- features/support/ios/sections/nav_widgets/nav_bar.rb
|
371
386
|
- features/support/ios/sections/nav_widgets/nav_menu.rb
|
372
387
|
- features/support/shared_components/screens/base_app_screen.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
class ProductCellItem < TestCentricity::ScreenSection
|
2
|
-
trait(:section_name) { 'Product Cell Item' }
|
3
|
-
trait(:section_locator) { { xpath: "//XCUIElementTypeOther[@name='store item']" } }
|
4
|
-
|
5
|
-
# Product Cell Item UI elements
|
6
|
-
labels product_name: { xpath: '(//XCUIElementTypeOther)[1]' },
|
7
|
-
product_price: { xpath: "//XCUIElementTypeStaticText[@name='store item price']" }
|
8
|
-
buttons review_star_1: { xpath: "//XCUIElementTypeOther[@name='review star 1']" },
|
9
|
-
review_star_2: { xpath: "//XCUIElementTypeOther[@name='review star 2']" },
|
10
|
-
review_star_3: { xpath: "//XCUIElementTypeOther[@name='review star 3']" },
|
11
|
-
review_star_4: { xpath: "//XCUIElementTypeOther[@name='review star 4']" },
|
12
|
-
review_star_5: { xpath: "//XCUIElementTypeOther[@name='review star 5']" }
|
13
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class BaseIOSModal < TestCentricity::ScreenSection
|
2
|
-
trait(:section_name) { 'Base iOS Modal' }
|
3
|
-
trait(:section_locator) { { class: 'XCUIElementTypeAlert' } }
|
4
|
-
|
5
|
-
# Base Modal UI elements
|
6
|
-
alert :alert_modal, { class: 'XCUIElementTypeAlert' }
|
7
|
-
|
8
|
-
def await_and_respond(action, timeout)
|
9
|
-
if alert_modal.await(timeout)
|
10
|
-
case action
|
11
|
-
when :allow, :accept
|
12
|
-
alert_modal.accept
|
13
|
-
when :dont_allow, :dismiss
|
14
|
-
alert_modal.dismiss
|
15
|
-
else
|
16
|
-
raise "#{action} is not a valid selector"
|
17
|
-
end
|
18
|
-
true
|
19
|
-
else
|
20
|
-
false
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|