percy-appium-app 0.0.1

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +55 -0
  3. data/.github/dependabot.yml +14 -0
  4. data/.github/release-drafter.yml +28 -0
  5. data/.github/workflows/Semgrep.yml +48 -0
  6. data/.github/workflows/changelog.yml +11 -0
  7. data/.github/workflows/release.yml +22 -0
  8. data/.github/workflows/stale.yml +30 -0
  9. data/.github/workflows/test.yml +40 -0
  10. data/Gemfile +13 -0
  11. data/LICENSE +21 -0
  12. data/Makefile +3 -0
  13. data/README.md +1 -0
  14. data/Rakefile +1 -0
  15. data/percy/common/common.rb +19 -0
  16. data/percy/configs/devices.json +232 -0
  17. data/percy/environment.rb +25 -0
  18. data/percy/exceptions/exceptions.rb +13 -0
  19. data/percy/lib/app_percy.rb +55 -0
  20. data/percy/lib/cache.rb +53 -0
  21. data/percy/lib/cli_wrapper.rb +132 -0
  22. data/percy/lib/ignore_region.rb +8 -0
  23. data/percy/lib/percy_automate.rb +59 -0
  24. data/percy/lib/percy_options.rb +37 -0
  25. data/percy/lib/region.rb +22 -0
  26. data/percy/lib/tile.rb +28 -0
  27. data/percy/metadata/android_metadata.rb +79 -0
  28. data/percy/metadata/driver_metadata.rb +40 -0
  29. data/percy/metadata/ios_metadata.rb +83 -0
  30. data/percy/metadata/metadata.rb +108 -0
  31. data/percy/metadata/metadata_resolver.rb +21 -0
  32. data/percy/providers/app_automate.rb +159 -0
  33. data/percy/providers/generic_provider.rb +205 -0
  34. data/percy/providers/provider_resolver.rb +17 -0
  35. data/percy/screenshot.rb +23 -0
  36. data/percy/version.rb +5 -0
  37. data/percy-appium-app.gemspec +37 -0
  38. data/specs/android_metadata.rb +79 -0
  39. data/specs/app_automate.rb +124 -0
  40. data/specs/app_percy.rb +175 -0
  41. data/specs/cache.rb +56 -0
  42. data/specs/cli_wrapper.rb +135 -0
  43. data/specs/driver_metadata.rb +71 -0
  44. data/specs/generic_providers.rb +370 -0
  45. data/specs/ignore_regions.rb +51 -0
  46. data/specs/ios_metadata.rb +88 -0
  47. data/specs/metadata.rb +105 -0
  48. data/specs/metadata_resolver.rb +41 -0
  49. data/specs/mocks/mock_methods.rb +147 -0
  50. data/specs/percy_options.rb +114 -0
  51. data/specs/screenshot.rb +342 -0
  52. data/specs/tile.rb +33 -0
  53. metadata +194 -0
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ def get_android_capabilities
6
+ {
7
+ 'platform' => 'LINUX',
8
+ 'webStorageEnabled' => false,
9
+ 'takesScreenshot' => true,
10
+ 'javascriptEnabled' => true,
11
+ 'databaseEnabled' => false,
12
+ 'networkConnectionEnabled' => true,
13
+ 'locationContextEnabled' => false,
14
+ 'warnings' => {},
15
+ 'desired' => {
16
+ 'percy:options' => {
17
+ 'enabled' => true,
18
+ 'ignoreErrors' => false
19
+ },
20
+ 'percyOptions' => {
21
+ 'enabled' => true,
22
+ 'ignoreErrors' => false
23
+ },
24
+ 'platformName' => 'Android',
25
+ 'bstack:options' => {
26
+ 'appiumVersion' => '1.17.0'
27
+ },
28
+ 'goog:chromeOptions' => {},
29
+ 'newCommandTimeout' => 0,
30
+ 'deviceName' => 'google pixel 4',
31
+ 'chromedriverPorts' => [
32
+ [
33
+ 18_144,
34
+ 18_154
35
+ ]
36
+ ],
37
+ 'automationName' => 'uiautomator2',
38
+ 'systemPort' => 8204,
39
+ 'chromedriverPort' => 18_084,
40
+ 'build' => 'android-builds',
41
+ 'os_version' => '10.0',
42
+ 'sessionName' => 'first-session',
43
+ 'skipServerInstallation' => true,
44
+ 'udid' => '9A301FFAZ0043B',
45
+ 'appPackage' => 'org.wikipedia.alpha',
46
+ 'appActivity' => 'org.wikipedia.main.MainActivity',
47
+ 'nativeWebScreenshot' => true,
48
+ 'disableSuppressAccessibilityService' => true
49
+ },
50
+ 'percy:options' => {
51
+ 'enabled' => true,
52
+ 'ignoreErrors' => true
53
+ },
54
+ 'percyOptions' => {
55
+ 'enabled' => true,
56
+ 'ignoreErrors' => true
57
+ },
58
+ 'platformName' => 'Android',
59
+ 'bstack:options' => {
60
+ 'appiumVersion' => '1.17.0'
61
+ },
62
+ 'goog:chromeOptions' => {},
63
+ 'newCommandTimeout' => 0,
64
+ 'deviceName' => '9A301FFAZ0043B',
65
+ 'chromedriverPorts' => [
66
+ [
67
+ 18_144,
68
+ 18_154
69
+ ]
70
+ ],
71
+ 'automationName' => 'uiautomator2',
72
+ 'systemPort' => 8204,
73
+ 'chromedriverPort' => 18_084,
74
+ 'build' => 'android-builds',
75
+ 'os_version' => '10.0',
76
+ 'sessionName' => 'first-session',
77
+ 'skipServerInstallation' => true,
78
+ 'udid' => '9A301FFAZ0043B',
79
+ 'appPackage' => 'org.wikipedia.alpha',
80
+ 'appActivity' => 'org.wikipedia.main.MainActivity',
81
+ 'nativeWebScreenshot' => true,
82
+ 'disableSuppressAccessibilityService' => true,
83
+ 'deviceUDID' => '9A301FFAZ0043B',
84
+ 'deviceApiLevel' => 29,
85
+ 'platformVersion' => '10',
86
+ 'deviceScreenSize' => '1080x2280',
87
+ 'deviceScreenDensity' => 440,
88
+ 'deviceModel' => 'Pixel 4',
89
+ 'deviceManufacturer' => 'Google',
90
+ 'pixelRatio' => 2.75,
91
+ 'statBarHeight' => 83,
92
+ 'viewportRect' => {
93
+ 'left' => 0,
94
+ 'top' => 83,
95
+ 'width' => 1080,
96
+ 'height' => 2153
97
+ }
98
+ }
99
+ end
100
+
101
+ def get_ios_capabilities
102
+ {
103
+ 'webStorageEnabled' => false,
104
+ 'locationContextEnabled' => false,
105
+ 'browserName' => '',
106
+ 'platform' => 'MAC',
107
+ 'javascriptEnabled' => true,
108
+ 'databaseEnabled' => false,
109
+ 'takesScreenshot' => true,
110
+ 'networkConnectionEnabled' => false,
111
+ 'percy:options' => {
112
+ 'enabled' => true,
113
+ 'ignoreErrors' => true
114
+ },
115
+ 'percyOptions' => {
116
+ 'enabled' => true,
117
+ 'ignoreErrors' => false
118
+ },
119
+ 'platformName' => 'iOS',
120
+ 'bstack:options' => {
121
+ 'appiumVersion' => '1.21.0'
122
+ },
123
+ 'newCommandTimeout' => 0,
124
+ 'realMobile' => 'true',
125
+ 'deviceName' => 'iphone 14',
126
+ 'safariIgnoreFraudWarning' => true,
127
+ 'orientation' => 'PORTRAIT',
128
+ 'deviceOrientation' => 'PORTRAIT',
129
+ 'noReset' => true,
130
+ 'automationName' => 'XCUITest',
131
+ 'keychainPath' => '[REDACTED VALUE]',
132
+ 'keychainPassword' => '[REDACTED VALUE]',
133
+ 'useXctestrunFile' => true,
134
+ 'bootstrapPath' => '/usr/local/.browserstack/config/wda_derived_data_16_1.21.0_e9279c32-baa8-4e44-b03e-00aa591b5c2b/Build/Products',
135
+ 'browserstack.isTargetBased' => 'true',
136
+ 'build' => 'ios-builds',
137
+ 'os_version' => '16',
138
+ 'sessionName' => 'v0.0.1',
139
+ 'udid' => '00008110-001019020AA1401E',
140
+ 'bundleID' => 'com.browserstack.Sample-iOS',
141
+ 'bundleId' => 'com.browserstack.Sample-iOS',
142
+ 'webkitResponseTimeout' => 20_000,
143
+ 'safariInitialUrl' => 'http://mobile-internet-check.browserstack.com',
144
+ 'waitForQuiescence' => false,
145
+ 'wdaStartupRetries' => 3
146
+ }
147
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/mock'
5
+ require_relative '../percy/lib/percy_options'
6
+
7
+ # Test suite for the PercyOptions class
8
+ class TestPercyOptions < Minitest::Test
9
+ # Defaults
10
+ def test_percy_options_not_provided
11
+ capabilities = {}
12
+ percy_options = PercyOptions.new(capabilities)
13
+ assert_equal true, percy_options.enabled
14
+ assert_equal true, percy_options.ignore_errors
15
+ end
16
+
17
+ def test_percy_options_w3c_enabled
18
+ capabilities = { 'percy:options' => { 'enabled' => true } }
19
+ percy_options = PercyOptions.new(capabilities)
20
+ assert_equal true, percy_options.enabled
21
+ assert_equal true, percy_options.ignore_errors
22
+ end
23
+
24
+ def test_percy_options_json_wire_enabled
25
+ capabilities = { 'percy.enabled' => true }
26
+ percy_options = PercyOptions.new(capabilities)
27
+ assert_equal true, percy_options.enabled
28
+ assert_equal true, percy_options.ignore_errors
29
+ end
30
+
31
+ def test_percy_options_w3c_not_enabled
32
+ capabilities = { 'percy:options' => { 'enabled' => false } }
33
+ percy_options = PercyOptions.new(capabilities)
34
+ assert_equal false, percy_options.enabled
35
+ assert_equal true, percy_options.ignore_errors
36
+ end
37
+
38
+ def test_percy_options_json_wire_not_enabled
39
+ capabilities = { 'percy.enabled' => false }
40
+ percy_options = PercyOptions.new(capabilities)
41
+ assert_equal false, percy_options.enabled
42
+ assert_equal true, percy_options.ignore_errors
43
+ end
44
+
45
+ def test_percy_options_w3c_ignore_errors
46
+ capabilities = { 'percy:options' => { 'ignoreErrors' => true } }
47
+ percy_options = PercyOptions.new(capabilities)
48
+ assert_equal true, percy_options.ignore_errors
49
+ assert_equal true, percy_options.enabled
50
+ end
51
+
52
+ def test_percy_options_json_wire_ignore_errors
53
+ capabilities = { 'percy.ignoreErrors' => true }
54
+ percy_options = PercyOptions.new(capabilities)
55
+ assert_equal true, percy_options.ignore_errors
56
+ assert_equal true, percy_options.enabled
57
+ end
58
+
59
+ def test_percy_options_w3c_not_ignore_errors
60
+ capabilities = { 'percy:options' => { 'ignoreErrors' => false } }
61
+ percy_options = PercyOptions.new(capabilities)
62
+ assert_equal false, percy_options.ignore_errors
63
+ assert_equal true, percy_options.enabled
64
+ end
65
+
66
+ def test_percy_options_json_wire_not_ignore_errors
67
+ capabilities = { 'percy.ignoreErrors' => false }
68
+ percy_options = PercyOptions.new(capabilities)
69
+ assert_equal false, percy_options.ignore_errors
70
+ assert_equal true, percy_options.enabled
71
+ end
72
+
73
+ def test_percy_options_w3c_all_options_false
74
+ capabilities = { 'percy:options' => { 'ignoreErrors' => false, 'enabled' => false } }
75
+ percy_options = PercyOptions.new(capabilities)
76
+ assert_equal false, percy_options.ignore_errors
77
+ assert_equal false, percy_options.enabled
78
+ end
79
+
80
+ def test_percy_options_json_wire_all_options_false
81
+ capabilities = { 'percy.ignoreErrors' => false, 'percy.enabled' => false }
82
+ percy_options = PercyOptions.new(capabilities)
83
+ assert_equal false, percy_options.ignore_errors
84
+ # assert_equal false, percy_options.enabled
85
+ end
86
+
87
+ def test_percy_options_w3c_all_options_true
88
+ capabilities = { 'percy:options' => { 'ignoreErrors' => true, 'enabled' => true } }
89
+ percy_options = PercyOptions.new(capabilities)
90
+ assert_equal true, percy_options.ignore_errors
91
+ assert_equal true, percy_options.enabled
92
+ end
93
+
94
+ def test_percy_options_json_wire_all_options_true
95
+ capabilities = { 'percy.ignoreErrors' => true, 'percy.enabled' => true }
96
+ percy_options = PercyOptions.new(capabilities)
97
+ assert_equal true, percy_options.ignore_errors
98
+ assert_equal true, percy_options.enabled
99
+ end
100
+
101
+ def test_percy_options_json_wire_and_w3c_case_1
102
+ capabilities = { 'percy.ignoreErrors' => false, 'percy:options' => { 'enabled' => false } }
103
+ percy_options = PercyOptions.new(capabilities)
104
+ assert_equal false, percy_options.ignore_errors
105
+ assert_equal false, percy_options.enabled
106
+ end
107
+
108
+ def test_percy_options_json_wire_and_w3c_case_2
109
+ capabilities = { 'percy.enabled' => false, 'percy:options' => { 'ignoreErrors' => false } }
110
+ percy_options = PercyOptions.new(capabilities)
111
+ assert_equal false, percy_options.ignore_errors
112
+ assert_equal false, percy_options.enabled
113
+ end
114
+ end
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/MethodLength
4
+ # rubocop:disable Metrics/AbcSize
5
+
6
+ require 'minitest/autorun'
7
+ require 'json'
8
+ require 'webmock/minitest'
9
+ require 'appium_lib'
10
+ require 'webrick'
11
+
12
+ require_relative '../percy/screenshot'
13
+ require_relative '../percy/lib/app_percy'
14
+ require_relative 'mocks/mock_methods'
15
+
16
+ # Test suite for the percy_screenshot
17
+ class MockServerRequestHandler < WEBrick::HTTPServlet::AbstractServlet
18
+ def do_GET(_request, response)
19
+ response.status = 200
20
+ response['Content-Type'] = 'application/json'
21
+ response.body = 'Screenshot Me'
22
+ end
23
+ end
24
+
25
+ mock_server = WEBrick::HTTPServer.new(Port: 8000)
26
+ mock_server.mount('/', MockServerRequestHandler)
27
+ mock_server_thread = Thread.new { mock_server.start }
28
+
29
+ # Mock helpers
30
+ def mock_healthcheck(fail: false, fail_how: 'error', type: 'AppPercy')
31
+ health_body = JSON.dump(success: true, build: { 'id' => '123', 'url' => 'dummy_url' }, type: type)
32
+ health_headers = { 'X-Percy-Core-Version' => '1.27.0-beta.1' }
33
+ health_status = 200
34
+
35
+ if fail && fail_how == 'error'
36
+ health_body = '{"success": false, "error": "test"}'
37
+ health_status = 500
38
+ elsif fail && fail_how == 'wrong-version'
39
+ health_headers = { 'X-Percy-Core-Version' => '2.0.0' }
40
+ elsif fail && fail_how == 'no-version'
41
+ health_headers = {}
42
+ end
43
+
44
+ stub_request(:get, 'http://localhost:5338/percy/healthcheck')
45
+ .with(headers: health_headers)
46
+ .to_return(body: health_body, status: health_status)
47
+
48
+ stub_request(:get, 'http://localhost:5338/percy/healthcheck')
49
+ .with(
50
+ headers: {
51
+ 'Accept' => '*/*',
52
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
53
+ 'Host' => 'localhost:5338',
54
+ 'User-Agent' => 'Ruby'
55
+ }
56
+ )
57
+ .to_return(status: health_status, body: health_body, headers: health_headers)
58
+ end
59
+
60
+ def mock_screenshot(fail: false)
61
+ stub_request(:post, 'http://localhost:5338/percy/comparison')
62
+ .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
63
+ end
64
+
65
+ def mock_poa_screenshot(fail: false)
66
+ stub_request(:post, 'http://localhost:5338/percy/automateScreenshot')
67
+ .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
68
+ end
69
+
70
+ def mock_session_request
71
+ stub_request(:post, 'http://127.0.0.1:4723/wd/hub/session')
72
+ .to_return(status: 200, body: '', headers: {})
73
+ end
74
+
75
+ class TestPercyScreenshot < Minitest::Test
76
+ def setup
77
+ @mock_webdriver = Minitest::Mock.new
78
+ @bridge = Minitest::Mock.new
79
+ @http = Minitest::Mock.new
80
+ @server_url = Minitest::Mock.new
81
+ @mock_webdriver.expect(:class, Appium::Core::Base::Driver)
82
+ WebMock.enable!
83
+ @requests = []
84
+ WebMock.after_request do |request, _response|
85
+ @requests << request
86
+ end
87
+ end
88
+
89
+ def teardown
90
+ WebMock.disable!
91
+ end
92
+
93
+ def test_throws_error_when_app_percy_arg_type_mismatch
94
+ 6.times do
95
+ @mock_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
96
+ end
97
+ 30.times do
98
+ @mock_webdriver.expect(:capabilities, get_android_capabilities)
99
+ end
100
+ 6.times do
101
+ @mock_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
102
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
103
+ @bridge.expect(:instance_variable_get, @http, [:@http])
104
+ @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
105
+ end
106
+
107
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot(123) }
108
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot('screenshot 1', device_name: 123) }
109
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot('screenshot 1', full_screen: 123) }
110
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot('screenshot 1', orientation: 123) }
111
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot('screenshot 1', status_bar_height: 'height') }
112
+ assert_raises(TypeError) { AppPercy.new(@mock_webdriver).screenshot('screenshot 1', nav_bar_height: 'height') }
113
+ end
114
+
115
+ def test_throws_error_when_a_driver_is_not_provided
116
+ assert_raises(Exception) { percy_screenshot }
117
+ end
118
+
119
+ def test_throws_error_when_a_name_is_not_provided
120
+ assert_raises(Exception) { percy_screenshot(@mock_webdriver) }
121
+ end
122
+
123
+ def test_disables_screenshots_when_the_healthcheck_fails
124
+ mock_healthcheck(fail: true)
125
+
126
+ assert_output(/Percy is not running, disabling screenshots/) do
127
+ percy_screenshot(@mock_webdriver, 'screenshot 1')
128
+ percy_screenshot(@mock_webdriver, 'screenshot 2')
129
+ end
130
+
131
+ assert_equal('/percy/healthcheck', @requests.last.uri.path)
132
+ end
133
+
134
+ def test_disables_screenshots_when_the_healthcheck_version_is_wrong
135
+ mock_healthcheck(fail: true, fail_how: 'wrong-version')
136
+
137
+ assert_output(/Unsupported Percy CLI version, 2.0.0/) do
138
+ percy_screenshot(@mock_webdriver, 'screenshot 1')
139
+ percy_screenshot(@mock_webdriver, 'screenshot 2')
140
+ end
141
+
142
+ assert_equal('/percy/healthcheck', @requests.last.uri.path)
143
+ end
144
+
145
+ def test_posts_screenshot_poa
146
+ mock_healthcheck(type: 'automate')
147
+ mock_poa_screenshot
148
+ mock_session_request
149
+
150
+ driver = Minitest::Mock.new
151
+ @bridge = Minitest::Mock.new
152
+ @http = Minitest::Mock.new
153
+ @server_url = Minitest::Mock.new
154
+
155
+ 2.times do
156
+ driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
157
+ end
158
+ 10.times do
159
+ driver.expect(:session_id, 'Dummy_session_id')
160
+ end
161
+ 4.times do
162
+ driver.expect(:capabilities, { 'key' => 'value' })
163
+ end
164
+ 2.times do
165
+ driver.expect(:desired_capabilities, { 'key' => 'value' })
166
+ end
167
+ 2.times do
168
+ driver.expect(:instance_variable_get, @bridge, [:@bridge])
169
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
170
+ @bridge.expect(:instance_variable_get, @http, [:@http])
171
+ @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
172
+ end
173
+
174
+ element = Minitest::Mock.new
175
+ element.expect(:id, 'Dummy_id')
176
+
177
+ consider_element = Minitest::Mock.new
178
+ consider_element.expect(:id, 'Consider_Dummy_id')
179
+ @mock_webdriver.expect(:capabilities, { 'key' => 'value' })
180
+
181
+ percy_screenshot(driver, 'Snapshot 1', options: {})
182
+ percy_screenshot(driver, 'Snapshot 2', options: { 'enable_javascript' => true,
183
+ 'ignore_region_appium_elements' => [element],
184
+ 'consider_region_appium_elements' => [consider_element] })
185
+
186
+ assert_equal('/percy/automateScreenshot', @requests.last.uri.path)
187
+
188
+ s1 = JSON.parse(@requests[1].body)
189
+ assert_equal('Snapshot 1', s1['snapshotName'])
190
+ assert_equal('Dummy_session_id', s1['sessionId'])
191
+ assert_equal(
192
+ driver.instance_variable_get(:@bridge).instance_variable_get(:@http).instance_variable_get(:@server_url).to_s,
193
+ s1['commandExecutorUrl']
194
+ )
195
+ driver_caps = driver.capabilities
196
+ assert_equal(driver_caps, s1['capabilities'])
197
+ driver_desired_caps = driver.desired_capabilities
198
+ assert_equal(driver_desired_caps, s1['sessionCapabilities'])
199
+ assert_match(%r{percy-appium-app/\d+}, s1['client_info'])
200
+ assert_match(%r{appium/\d+}, s1['environment_info'][0])
201
+ assert_match(%r{ruby/\d+\.\d+\.\d+}, s1['environment_info'][1])
202
+
203
+ s2 = JSON.parse(@requests[-1].body)
204
+ assert_equal('Snapshot 2', s2['snapshotName'])
205
+ assert_equal(true, s2['options']['enable_javascript'])
206
+ assert_equal(['Dummy_id'], s2['options']['ignore_region_elements'])
207
+ assert_equal(['Consider_Dummy_id'], s2['options']['consider_region_elements'])
208
+ end
209
+
210
+ def test_posts_multiple_screenshots_to_the_local_percy_server
211
+ mock_healthcheck
212
+ mock_screenshot
213
+
214
+ driver = Minitest::Mock.new
215
+
216
+ 2.times do
217
+ driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
218
+ end
219
+
220
+ 10.times do
221
+ driver.expect(:session_id, 'Dummy_session_id')
222
+ end
223
+
224
+ 26.times do
225
+ driver.expect(:capabilities, get_android_capabilities)
226
+ end
227
+
228
+ 2.times do
229
+ driver.expect(:instance_variable_get, @bridge, [:@bridge])
230
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
231
+ @bridge.expect(:instance_variable_get, @http, [:@http])
232
+ @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
233
+ end
234
+
235
+ 6.times do
236
+ driver.expect(
237
+ :execute_script,
238
+ '{"success":true,"result":"[{\"sha\":\"sha-something\",\"status_bar\":null'\
239
+ ',\"nav_bar\":null,\"header_height\":0,\"footer_height\":0,\"index\":0}]"}',
240
+ [String]
241
+ )
242
+ end
243
+
244
+ 4.times do
245
+ driver.expect(:get_system_bars, {
246
+ 'statusBar' => { 'height' => 10, 'width' => 20 },
247
+ 'navigationBar' => { 'height' => 10, 'width' => 20 }
248
+ })
249
+ end
250
+
251
+ percy_screenshot(driver, 'screenshot 1')
252
+ percy_screenshot(driver, 'screenshot 2', full_screen: false)
253
+
254
+ assert_equal('/percy/comparison', @requests.last.uri.path)
255
+
256
+ body = JSON.parse(@requests[-1].body)
257
+ assert_match(%r{percy-appium-app/\d+}, body['client_info'])
258
+ assert_match(%r{appium/\d+}, body['environment_info'][0])
259
+ assert_match(%r{ruby/\d+\.\d+\.\d+}, body['environment_info'][1])
260
+ end
261
+
262
+ def test_ignore_region_screenshots_to_the_local_percy_server
263
+ mock_healthcheck
264
+ mock_screenshot
265
+
266
+ mock_element = Minitest::Mock.new
267
+ mock_element.expect(:location, { 'x' => 10, 'y' => 20 })
268
+ mock_element.expect(:size, { 'width' => 200, 'height' => 400 })
269
+
270
+ xpaths = ['//path/to/element']
271
+
272
+ driver = Minitest::Mock.new
273
+
274
+ 2.times do
275
+ driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
276
+ end
277
+
278
+ 10.times do
279
+ driver.expect(:session_id, 'Dummy_session_id')
280
+ end
281
+
282
+ 26.times do
283
+ driver.expect(:capabilities, get_android_capabilities)
284
+ end
285
+
286
+ 2.times do
287
+ driver.expect(:instance_variable_get, @bridge, [:@bridge])
288
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
289
+ @bridge.expect(:instance_variable_get, @http, [:@http])
290
+ @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
291
+ end
292
+
293
+ 5.times do
294
+ driver.expect(
295
+ :execute_script,
296
+ '{"success":true,"result":"[{\"sha\":\"sha-something\",\"status_bar\":null'\
297
+ ',\"nav_bar\":null,\"header_height\":0,\"footer_height\":0,\"index\":0}]"}',
298
+ [String]
299
+ )
300
+ end
301
+
302
+ 4.times do
303
+ driver.expect(:get_system_bars, {
304
+ 'statusBar' => { 'height' => 10, 'width' => 20 },
305
+ 'navigationBar' => { 'height' => 10, 'width' => 20 }
306
+ })
307
+ end
308
+ driver.expect(:find_element, mock_element,
309
+ [Appium::Core::Base::SearchContext::FINDERS[:xpath], '//path/to/element'])
310
+
311
+ percy_screenshot(driver, 'screenshot 1', ignore_regions_xpaths: xpaths)
312
+
313
+ assert_equal('/percy/comparison', @requests.last.uri.path)
314
+
315
+ body = JSON.parse(@requests[-1].body)
316
+ assert_match(%r{percy-appium-app/\d+}, body['client_info'])
317
+ assert_match(%r{appium/\d+}, body['environment_info'][0])
318
+ assert_equal(body['ignored_elements_data']['ignoreElementsData'].size, 1)
319
+
320
+ ignored_element_data = body['ignored_elements_data']['ignoreElementsData'][0]
321
+ assert_equal(ignored_element_data['selector'], 'xpath: //path/to/element')
322
+ assert_equal(ignored_element_data['coOrdinates']['top'], 20)
323
+ assert_equal(ignored_element_data['coOrdinates']['bottom'], 420)
324
+ assert_equal(ignored_element_data['coOrdinates']['left'], 10)
325
+ assert_equal(ignored_element_data['coOrdinates']['right'], 210)
326
+ end
327
+
328
+ def test_throws_error_when_a_driver_is_not_provided_poa
329
+ mock_healthcheck(type: 'automate')
330
+ assert_raises(Exception) { percy_screenshot }
331
+ end
332
+
333
+ def test_throws_error_when_invalid_driver_provided_poa
334
+ mock_healthcheck(type: 'automate')
335
+ assert_raises(Exception) { percy_screenshot('Wrong driver') }
336
+ end
337
+
338
+ def test_throws_error_when_a_name_is_not_provided_poa
339
+ mock_healthcheck(type: 'automate')
340
+ assert_raises(Exception) { percy_screenshot(@mock_webdriver) }
341
+ end
342
+ end
data/specs/tile.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../percy/lib/tile'
5
+
6
+ # Test suite for the Tile class.
7
+ class TileTest < Minitest::Test
8
+ def setup
9
+ @tile = Tile.new(20, 120, 150, 0, filepath: 'some-file-path', sha: 'some-sha')
10
+ @hash_tile = @tile.to_h
11
+ end
12
+
13
+ def test_tile_hash_keys
14
+ assert_equal @hash_tile, @tile.to_h
15
+ assert_includes @hash_tile, 'filepath'
16
+ assert_includes @hash_tile, 'status_bar_height'
17
+ assert_includes @hash_tile, 'nav_bar_height'
18
+ assert_includes @hash_tile, 'header_height'
19
+ assert_includes @hash_tile, 'footer_height'
20
+ assert_includes @hash_tile, 'fullscreen'
21
+ assert_includes @hash_tile, 'sha'
22
+ end
23
+
24
+ def test_tile_values
25
+ assert_equal 'some-file-path', @hash_tile['filepath']
26
+ assert_equal 20, @hash_tile['status_bar_height']
27
+ assert_equal 120, @hash_tile['nav_bar_height']
28
+ assert_equal 150, @hash_tile['header_height']
29
+ assert_equal 0, @hash_tile['footer_height']
30
+ assert_equal 'some-sha', @hash_tile['sha']
31
+ assert_equal false, @hash_tile['fullscreen'] # Default
32
+ end
33
+ end