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,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/MethodLength
4
+
5
+ require 'minitest/autorun'
6
+ # require 'webmock/minitest'
7
+ require_relative '../percy/lib/app_percy'
8
+ require_relative '../percy/metadata/android_metadata'
9
+ require_relative '../percy/metadata/ios_metadata'
10
+ require_relative '../percy/providers/app_automate'
11
+ require_relative '../percy/providers/generic_provider'
12
+ require_relative '../percy/lib/cli_wrapper'
13
+ require_relative 'mocks/mock_methods'
14
+
15
+ # Test suite for the AppPercy class
16
+ class TestAppPercy < Minitest::Test
17
+ COMPARISON_RESPONSE = { 'link' => 'https://snapshot_url', 'success' => true }.freeze
18
+
19
+ def setup
20
+ @mock_android_webdriver = Minitest::Mock.new
21
+ @mock_ios_webdriver = Minitest::Mock.new
22
+ @bridge = Minitest::Mock.new
23
+ @http = Minitest::Mock.new
24
+ @server_url = Minitest::Mock.new
25
+ end
26
+
27
+ def test_android_on_app_automate
28
+ 3.times do
29
+ @mock_android_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
30
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
31
+ @bridge.expect(:instance_variable_get, @http, [:@http])
32
+ @server_url.expect(:to_s, 'url-of-browserstack-cloud')
33
+ end
34
+ @mock_android_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
35
+ 5.times do
36
+ @mock_android_webdriver.expect(:capabilities, get_android_capabilities)
37
+ end
38
+
39
+ ENV['PERCY_DISABLE_REMOTE_UPLOADS'] = 'true'
40
+
41
+ app_percy = AppPercy.new(@mock_android_webdriver)
42
+
43
+ assert_instance_of(AndroidMetadata, app_percy.metadata)
44
+ assert_instance_of(AppAutomate, app_percy.provider)
45
+ end
46
+
47
+ def test_android_on_non_app_automate
48
+ 3.times do
49
+ @mock_android_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
50
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
51
+ @bridge.expect(:instance_variable_get, @http, [:@http])
52
+ @server_url.expect(:to_s, 'some-remote-url')
53
+ end
54
+ 2.times do
55
+ @mock_android_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
56
+ end
57
+ 5.times do
58
+ @mock_android_webdriver.expect(:capabilities, get_android_capabilities)
59
+ end
60
+ app_percy = AppPercy.new(@mock_android_webdriver)
61
+ assert_instance_of(AndroidMetadata, app_percy.metadata)
62
+ assert_instance_of(GenericProvider, app_percy.provider)
63
+ end
64
+
65
+ def test_ios_on_app_automate
66
+ mock_driver_remote_url(@mock_ios_webdriver, 'url-of-browserstack-cloud')
67
+ @mock_ios_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
68
+ 5.times do
69
+ @mock_ios_webdriver.expect(:capabilities, get_ios_capabilities)
70
+ end
71
+ app_percy = AppPercy.new(@mock_ios_webdriver)
72
+ assert_instance_of(IOSMetadata, app_percy.metadata)
73
+ assert_instance_of(AppAutomate, app_percy.provider)
74
+ end
75
+
76
+ def test_ios_on_non_app_automate
77
+ 2.times do
78
+ @mock_ios_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
79
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
80
+ @bridge.expect(:instance_variable_get, @http, [:@http])
81
+ @server_url.expect(:to_s, 'some-remote-url')
82
+ end
83
+ @mock_ios_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
84
+ 3.times do
85
+ @mock_ios_webdriver.expect(:capabilities, get_ios_capabilities)
86
+ end
87
+ app_percy = AppPercy.new(@mock_ios_webdriver)
88
+ assert_instance_of(IOSMetadata, app_percy.metadata)
89
+ assert_instance_of(GenericProvider, app_percy.provider)
90
+ end
91
+
92
+ def test_screenshot_with_percy_options_disabled
93
+ disable_percy_options(@mock_android_webdriver, num = 5)
94
+ make_mock_driver_appium(@mock_android_webdriver)
95
+ mock_driver_remote_url(@mock_android_webdriver, 'some-other-url', num = 2)
96
+ app_percy = AppPercy.new(@mock_android_webdriver)
97
+ assert_nil app_percy.screenshot('screenshot 1')
98
+ end
99
+
100
+ def test_screenshot_with_percyoptions_disabled
101
+ disable_percy_options(@mock_android_webdriver, num = 5)
102
+ make_mock_driver_appium(@mock_android_webdriver)
103
+ mock_driver_remote_url(@mock_android_webdriver, 'some-other-url', num = 2)
104
+ app_percy = AppPercy.new(@mock_android_webdriver)
105
+ assert_nil app_percy.screenshot('screenshot 1')
106
+ end
107
+
108
+ def test_percy_options_ignore_errors
109
+ @mock_android_webdriver.expect(:capabilities, {
110
+ 'platformName': 'android',
111
+ 'percy:options' => { 'ignoreErrors' => false }
112
+ })
113
+
114
+ assert_raises(Exception) do
115
+ AppPercy.screenshot(@mock_android_webdriver, 'screenshot')
116
+ end
117
+ end
118
+
119
+ def test_invalid_provider
120
+ url = 'some-cloud-url'
121
+ mock_driver_remote_url(@mock_android_webdriver, url, 2)
122
+ @mock_android_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
123
+ caps = get_android_capabilities
124
+ 4.times do
125
+ @mock_android_webdriver.expect(:capabilities, caps)
126
+ end
127
+
128
+ GenericProvider.stub(:supports, false) do
129
+ assert_raises(UnknownProvider) do
130
+ _provider = AppPercy.new(@mock_android_webdriver).provider
131
+ end
132
+ end
133
+ end
134
+
135
+ def test_invalid_driver
136
+ assert_raises(DriverNotSupported) do
137
+ AppPercy.new(Object.new)
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def disable_percy_options(mock_webdriver, num = 1)
144
+ num.times do
145
+ mock_webdriver.expect(:capabilities, {
146
+ 'platformName' => 'android',
147
+ 'percy:options' => { 'enabled' => false }
148
+ })
149
+ end
150
+ end
151
+
152
+ def mock_driver_remote_url(mock_webdriver, url, num = 1)
153
+ num.times do
154
+ mock_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
155
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
156
+ @bridge.expect(:instance_variable_get, @http, [:@http])
157
+ @server_url.expect(:to_s, url)
158
+ end
159
+ end
160
+
161
+ def make_mock_driver_appium(mock_driver)
162
+ mock_driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
163
+ end
164
+
165
+ def ignore_errors_test(mock_webdriver)
166
+ mock_webdriver.expect(:capabilities, {
167
+ 'platformName': 'android',
168
+ 'percy:options' => { 'ignoreErrors' => false }
169
+ })
170
+
171
+ assert_raises(Exception) do
172
+ AppPercy.screenshot(mock_webdriver, 'screenshot')
173
+ end
174
+ end
175
+ end
data/specs/cache.rb ADDED
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'minitest/mock'
5
+ require 'time'
6
+ require_relative '../percy/lib/cache'
7
+
8
+ # Test suite for the Cache class
9
+ class TestCache < Minitest::Test
10
+ def setup
11
+ @cache = Cache.new
12
+ @session_id = 'session_id_123'
13
+ @prop = 'window_size'
14
+ @value = { 'top' => 'Top Value' }
15
+ Cache.set_cache(@session_id, @prop, @value)
16
+ end
17
+
18
+ def test_set_cache
19
+ assert_raises(Exception) { Cache.set_cache(123, 123, 123) }
20
+ assert_raises(Exception) { Cache.set_cache(@session_id, 123, 123) }
21
+
22
+ assert_includes @cache.cache, @session_id
23
+ assert_equal @cache.cache[@session_id][@prop], @value
24
+ end
25
+
26
+ def test_get_cache_invalid_args
27
+ assert_raises(Exception) { Cache.get_cache(123, 123) }
28
+ assert_raises(Exception) { Cache.get_cache(@session_id, 123) }
29
+ end
30
+
31
+ def test_get_cache_success
32
+ mock_cleanup_cache = Minitest::Mock.new
33
+ mock_cleanup_cache.expect(:call, nil)
34
+
35
+ Cache.stub(:cleanup_cache, -> { mock_cleanup_cache.call }) do
36
+ window_size = Cache.get_cache(@session_id, @prop)
37
+ assert_equal @value, window_size
38
+ mock_cleanup_cache.verify
39
+ end
40
+ end
41
+
42
+ def test_cleanup_cache
43
+ previous_value = Cache::CACHE_TIMEOUT
44
+ Cache.send(:remove_const, :CACHE_TIMEOUT)
45
+ Cache.const_set(:CACHE_TIMEOUT, 1)
46
+
47
+ cache_timeout = Cache::CACHE_TIMEOUT
48
+ sleep(cache_timeout + 1)
49
+ assert_includes @cache.cache, @session_id
50
+ Cache.cleanup_cache
51
+ refute_includes @cache.cache, @session_id
52
+
53
+ Cache.send(:remove_const, :CACHE_TIMEOUT)
54
+ Cache.const_set(:CACHE_TIMEOUT, previous_value)
55
+ end
56
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/MethodLength
4
+
5
+ require 'minitest/autorun'
6
+ require 'webmock/minitest'
7
+ require 'json'
8
+ require_relative '../percy/lib/cli_wrapper'
9
+ require_relative '../percy/lib/tile'
10
+
11
+ def mock_screenshot(fail: false)
12
+ stub_request(:post, 'http://localhost:5338/percy/comparison')
13
+ .to_return(body: '{"link": "snapshot-url-link", "success": true}', status: (fail ? 500 : 200))
14
+ end
15
+
16
+ def mock_poa_screenshot(fail: false)
17
+ stub_request(:post, 'http://localhost:5338/percy/automateScreenshot')
18
+ .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
19
+ end
20
+
21
+ # Test suite for the CLIWrapper class
22
+ class TestCLIWrapper < Minitest::Test
23
+ def setup
24
+ @cli_wrapper = CLIWrapper.new
25
+ @ignored_elements_data = {
26
+ 'ignore_elements_data' => {
27
+ 'selector' => 'xpath: some_xpath',
28
+ 'coOrdinates' => { 'top' => 123, 'bottom' => 234, 'left' => 234, 'right' => 455 }
29
+ }
30
+ }
31
+ @considered_elements_data = {
32
+ 'consider_elements_data' => {
33
+ 'selector' => 'xpath: some_xpath',
34
+ 'coOrdinates' => { 'top' => 50, 'bottom' => 100, 'left' => 0, 'right' => 100 }
35
+ }
36
+ }
37
+ WebMock.enable!
38
+ end
39
+
40
+ def teardown
41
+ WebMock.disable!
42
+ end
43
+
44
+ def test_post_screenshot_throws_error
45
+ mock_screenshot(fail: true)
46
+
47
+ assert_raises(CLIException) do
48
+ @cli_wrapper.post_screenshots('some-name', {}, [], 'some-debug-url')
49
+ end
50
+ end
51
+
52
+ def test_post_failed_event
53
+ mock_poa_screenshot(fail: true)
54
+ @snapshot_name = 'test_snapshot'
55
+ @session_id = 'test_session_id'
56
+ @command_executor_url = 'http://example.com'
57
+ @capabilities = { 'browser' => 'chrome' }
58
+ @desired_capabilities = { 'platform' => 'Windows' }
59
+ @options = { 'option_key' => 'option_value' }
60
+
61
+ expected_request_body = {
62
+ 'sessionId' => @session_id,
63
+ 'commandExecutorUrl' => @command_executor_url,
64
+ 'capabilities' => @capabilities,
65
+ 'sessionCapabilities' => @desired_capabilities,
66
+ 'snapshotName' => @snapshot_name,
67
+ 'options' => @options,
68
+ 'client_info' => 'your_mocked_client_info',
69
+ 'environment_info' => 'your_mocked_environment_info'
70
+ }
71
+
72
+ assert_raises(CLIException, 'TestError') do
73
+ @cli_wrapper.post_poa_screenshots(
74
+ @snapshot_name,
75
+ @session_id,
76
+ @command_executor_url,
77
+ @capabilities,
78
+ @desired_capabilities,
79
+ @options
80
+ )
81
+ end
82
+ end
83
+
84
+ def test_post_screenshot_with_ignore_region_null
85
+ mock_screenshot
86
+
87
+ assert_equal(
88
+ @cli_wrapper.post_screenshots('some-name', {}, [Tile.new(10, 10, 20, 20, filepath: 'some-file-path')],
89
+ 'some-debug-url'),
90
+ { 'link' => 'snapshot-url-link', 'success' => true }
91
+ )
92
+ end
93
+
94
+ def test_post_screenshot_with_ignore_region_present
95
+ mock_screenshot
96
+
97
+ assert_equal(
98
+ @cli_wrapper.post_screenshots('some-name', {}, [Tile.new(10, 10, 20, 20, filepath: 'some-file-path')],
99
+ 'some-debug-url', @ignored_elements_data),
100
+ { 'link' => 'snapshot-url-link', 'success' => true }
101
+ )
102
+ end
103
+
104
+ def test_request_body
105
+ tile = Tile.new(10, 10, 20, 20, filepath: 'some-file-path')
106
+ tag = { 'name' => 'Tag' }
107
+ name = 'some-name'
108
+ debug_url = 'debug-url'
109
+ response = @cli_wrapper.request_body(name, tag, [tile], debug_url, @ignored_elements_data,
110
+ @considered_elements_data)
111
+ assert_equal response['name'], name
112
+ assert_equal response['external_debug_url'], debug_url
113
+ assert_equal response['tag'], tag
114
+ assert_equal response['tiles'], [tile.to_h]
115
+ assert_equal response['ignored_elements_data'], @ignored_elements_data
116
+ assert_equal response['considered_elements_data'], @considered_elements_data
117
+ end
118
+
119
+ def test_request_body_when_optional_values_are_null
120
+ tile = Tile.new(10, 10, 20, 20, filepath: 'some-file-path')
121
+ tag = { 'name' => 'Tag' }
122
+ name = 'some-name'
123
+ debug_url = nil
124
+ ignored_elements_data = nil
125
+ considered_elements_data = nil
126
+ response = @cli_wrapper.send(:request_body, name, tag, [tile], debug_url, ignored_elements_data,
127
+ considered_elements_data)
128
+ assert_equal response['name'], name
129
+ assert_equal response['external_debug_url'], debug_url
130
+ assert_equal response['tag'], tag
131
+ assert_equal response['tiles'], [tile.to_h]
132
+ assert_nil response['ignored_elements_data']
133
+ assert_nil response['considered_elements_data']
134
+ end
135
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/MethodLength
4
+
5
+ require 'minitest/autorun'
6
+ require 'minitest/mock'
7
+ require 'appium_lib'
8
+
9
+ require_relative '../percy/metadata/driver_metadata'
10
+
11
+ # Test suite for the DriverMetadata class
12
+ class TestDriverMetadata < Minitest::Test
13
+ def setup
14
+ @mock_webdriver = Minitest::Mock.new
15
+ @http = Minitest::Mock.new
16
+ @bridge = Minitest::Mock.new
17
+ @server_url = Minitest::Mock.new
18
+
19
+ @metadata = DriverMetadata.new(@mock_webdriver)
20
+ end
21
+
22
+ def test_session_id
23
+ session_id = 'session_id_123'
24
+ @mock_webdriver.expect(:session_id, session_id)
25
+
26
+ assert(session_id, @metadata.session_id)
27
+ end
28
+
29
+ def test_command_executor_url
30
+ url = 'https://example-hub:4444/wd/hub'
31
+ session_id = 'session_id_123'
32
+ 2.times do
33
+ @mock_webdriver.expect(:session_id, session_id)
34
+ end
35
+ @mock_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
36
+ @http.expect(:instance_variable_get, @server_url, [:@server_url])
37
+ @bridge.expect(:instance_variable_get, @http, [:@http])
38
+ @server_url.expect(:to_s, url)
39
+
40
+ assert(url, @metadata.command_executor_url)
41
+ end
42
+
43
+ def test_capabilities
44
+ session_id = 'session_id_123'
45
+ 2.times do
46
+ @mock_webdriver.expect(:session_id, session_id)
47
+ end
48
+ capabilities = { 'platform' => 'chrome_android', 'browserVersion' => '115.0.1' }
49
+ @mock_webdriver.expect(:capabilities, capabilities)
50
+
51
+ assert(capabilities, @metadata.capabilities)
52
+ end
53
+
54
+ def test_session_capabilities
55
+ session_id = 'session_id_123'
56
+ @mock_webdriver.expect(:session_id, session_id)
57
+ @mock_webdriver.expect(:session_id, session_id)
58
+ @mock_webdriver.expect(:desired_capabilities, {
59
+ 'platform' => 'chrome_android',
60
+ 'browserVersion' => '115.0.1',
61
+ 'session_name' => 'abc'
62
+ })
63
+ session_caps = {
64
+ 'platform' => 'chrome_android',
65
+ 'browserVersion' => '115.0.1',
66
+ 'session_name' => 'abc'
67
+ }
68
+
69
+ assert(session_caps, @metadata.session_capabilities)
70
+ end
71
+ end