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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +1 -0
  4. data/Rakefile +2 -2
  5. data/config/cucumber.yml +1 -0
  6. data/config/test_data/data.yml +66 -0
  7. data/features/deep_links.feature +12 -0
  8. data/features/navigation.feature +12 -0
  9. data/features/step_definitions/generic_steps.rb +14 -19
  10. data/features/support/android/screens/base_app_screen.rb +2 -0
  11. data/features/support/android/screens/biometrics_screen.rb +17 -0
  12. data/features/support/android/screens/geo_location_screen.rb +28 -0
  13. data/features/support/android/screens/product_item_screen.rb +39 -0
  14. data/features/support/android/screens/products_screen.rb +15 -1
  15. data/features/support/android/screens/qr_code_scanner_screen.rb +15 -0
  16. data/features/support/android/sections/list_items/product_list_item.rb +13 -0
  17. data/features/support/data/product_data.rb +27 -0
  18. data/features/support/data/user_data.rb +17 -0
  19. data/features/support/env.rb +2 -1
  20. data/features/support/ios/screens/base_app_screen.rb +1 -0
  21. data/features/support/ios/screens/biometrics_screen.rb +17 -0
  22. data/features/support/ios/screens/geo_location_screen.rb +32 -0
  23. data/features/support/ios/screens/product_item_screen.rb +39 -0
  24. data/features/support/ios/screens/products_screen.rb +15 -1
  25. data/features/support/ios/screens/qr_code_scanner_screen.rb +11 -0
  26. data/features/support/ios/sections/list_items/product_list_item.rb +13 -0
  27. data/features/support/shared_components/screens/base_app_screen.rb +4 -0
  28. data/features/support/world_data.rb +4 -1
  29. data/features/support/world_pages.rb +4 -0
  30. data/lib/testcentricity/app_elements/alert.rb +64 -0
  31. data/lib/testcentricity/app_elements/app_element_helper.rb +55 -12
  32. data/lib/testcentricity/version.rb +1 -1
  33. data/testcentricity.gemspec +4 -4
  34. metadata +58 -43
  35. data/config/test_data/LOCAL_data.yml +0 -11
  36. data/features/support/ios/sections/list_items/product_cell_item.rb +0 -13
  37. data/features/support/ios/sections/modals/base_modal.rb +0 -23
  38. data/features/support/ios/sections/modals/logout_modal.rb +0 -6
  39. 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: 31f94846b0235fd3b3507025052c4bc2672fc06d3646dd6e51f30a746522ceb8
4
- data.tar.gz: 399961e0864716031576c8e12cd244b379ac2a67e2319d730c64cc1a431af49e
3
+ metadata.gz: e4c7b4120e8589701d29a850600108df295c1c199fa8c19e9f6f92bd76b49a47
4
+ data.tar.gz: 956e90ac6395ed32e69c2fd7558977398b8c42c075577d858090ce194da8c668
5
5
  SHA512:
6
- metadata.gz: 76f54f2ca7d2338a17a112907b8d084c84e458f9f8b5f6e0bf82405734d252acd6042daaef739d76f60675f8167a25c297f8cf996413f099e9e528b194903d47
7
- data.tar.gz: a5ac5ff41bfef9124b088492824c2583f5465fa855e42f6574a06498ac863503b9a280a88b58ff2bd3eb879fa75fb3176b914ad87da0a18586395c4f90309199
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 and Cucumber features'
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
  #==============
@@ -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'
@@ -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 |
@@ -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 |reason|
34
- case reason.gsub(/\s+/, '_').downcase.to_sym
35
- when :valid_data
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
@@ -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
@@ -5,6 +5,7 @@ class BaseAppScreen < TestCentricity::ScreenObject
5
5
 
6
6
  # Base App screen UI elements
7
7
  label :header_label, { accessibility_id: 'container header' }
8
+ alert :alert_modal, { class: 'XCUIElementTypeAlert' }
8
9
  sections nav_bar: NavBar,
9
10
  nav_menu: NavMenu
10
11
 
@@ -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
@@ -28,4 +28,8 @@ module SharedBaseAppScreen
28
28
  raise "#{action} is not a valid selector"
29
29
  end
30
30
  end
31
+
32
+ def modal_action(action)
33
+ alert_modal.await_and_respond(action.downcase.to_sym, timeout = 1)
34
+ end
31
35
  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
- raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless exists?
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
- raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if exists?
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
- raise "Could not find UI #{object_ref_message} after #{timeout} seconds" unless visible?
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
- raise "UI #{object_ref_message} remained visible after #{timeout} seconds" if visible?
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
- raise "Value of UI #{object_ref_message} failed to equal '#{value}' after #{timeout} seconds" unless get_value == value
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
- raise "Value of UI #{object_ref_message} failed to change from '#{value}' after #{timeout} seconds" if get_value == value
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
@@ -1,3 +1,3 @@
1
1
  module TestCentricity
2
- VERSION = '3.0.1'
2
+ VERSION = '3.0.4'
3
3
  end
@@ -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 desktop/mobile web and native mobile app testing}
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 'testcentricity_web', '>= 4.2.2'
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.1
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-05-26 00:00:00.000000000 Z
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.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.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/product_cell_item.rb
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 desktop/mobile web and native mobile app
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/product_cell_item.rb
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,11 +0,0 @@
1
- Form_data:
2
- primary:
3
- username: "Ignatius von Snicklefritz"
4
- password: "T0p_Sekrit"
5
- image_filename: "Wilder.jpg"
6
- multi_select: "Selection Item 2"
7
- drop_down_item: "Drop Down Item 5"
8
- check1: true
9
- check2: true
10
- check3: false
11
- radio_select: 2
@@ -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
@@ -1,6 +0,0 @@
1
- class LogOutModal < BaseIOSModal
2
- trait(:section_name) { 'Log Out Modal' }
3
-
4
- # Log Out Modal UI elements
5
- alert :alert_modal, { xpath: "//XCUIElementTypeAlert[@name='Log Out']" }
6
- end
@@ -1,6 +0,0 @@
1
- class ResetAppStateModal < BaseIOSModal
2
- trait(:section_name) { 'Reset App State Modal' }
3
-
4
- # Reset App State Modal UI elements
5
- alert :alert_modal, { xpath: "//XCUIElementTypeAlert[@name='Reset App State']" }
6
- end