percy-appium-app 0.0.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82432d403ea8909fd0dd473251c8b406378497ff06e6e1806144e83350598013
4
- data.tar.gz: 1f351d8ede37fef600554f98f1ac8bdc7b60124ea62374be65175a26b0cd74c0
3
+ metadata.gz: bdb1ff6892c099d297d97844d8dd24985e6be0361e78da39e274d844945ea869
4
+ data.tar.gz: ba73fd618e623111852a73900165ae0a9315fe5a8ebb768ca4d3eb8d86928db5
5
5
  SHA512:
6
- metadata.gz: 0bc03604787ae7757d95e29d33dfcdc176c9b0733c0b639ed04d75a7aac4061ff915046b34c0d65f21cf3360514f3c9f970dfef46e6bb38f363a5c2b3cd611d7
7
- data.tar.gz: c339d7e5282e110b6351284f72124cab0876ddb4b40c95606096219923ca64cf9b4cd4c8084022274625a6bbdd6c087a90bec4a6a105a3ed9c61a49b756bb1f3
6
+ metadata.gz: e94a5a62b0a3c82630b63ba011a423978c924bfd31c2ad4de7ea92c1f1a5ce532530a5fcbd2de72f8c89908c404ef353b1ac3c415abb3d3806e6f5eaac125875
7
+ data.tar.gz: 68fbb5b42c1df5e5b8d39f75debe939d7e76408ff8370e7998f04f60adec7fe37a875504e13b93b9876a39595b67195299d4f4a348407e4064770a5a20e6610d
@@ -27,7 +27,8 @@ jobs:
27
27
 
28
28
  container:
29
29
  # A Docker image with Semgrep installed. Do not change this.
30
- image: returntocorp/semgrep:1.166.0
30
+ image: returntocorp/semgrep
31
+
31
32
  # Skip any PR created by dependabot to avoid permission issues:
32
33
  if: (github.actor != 'dependabot[bot]')
33
34
 
@@ -11,7 +11,7 @@ jobs:
11
11
  with:
12
12
  ruby-version: 2.6
13
13
  bundler-cache: true
14
- - uses: actions/cache@v3
14
+ - uses: actions/cache@v2
15
15
  with:
16
16
  path: "./vendor/bundle"
17
17
  key: v1/${{ runner.os }}/ruby-2.6/${{ hashFiles('**/Gemfile.lock') }}
@@ -2,7 +2,6 @@ name: Test
2
2
  on:
3
3
  push:
4
4
  branches: [main]
5
- pull_request:
6
5
  workflow_dispatch:
7
6
  inputs:
8
7
  branch:
@@ -13,10 +12,6 @@ jobs:
13
12
  test:
14
13
  name: Test
15
14
  strategy:
16
- # Run every Ruby version to completion so one leg failing doesn't cancel
17
- # the others. Ruby 3.0's appium_lib API drift is fixed by pinning
18
- # appium_lib ~> 12.0 (see Gemfile), so all three legs are green.
19
- fail-fast: false
20
15
  matrix:
21
16
  os: [ubuntu-latest]
22
17
  ruby: ['2.6', '2.7', '3.0']
@@ -36,13 +31,10 @@ jobs:
36
31
  with:
37
32
  ruby-version: ${{matrix.ruby}}
38
33
  bundler-cache: true
39
- - uses: actions/cache@v3
34
+ - uses: actions/cache@v2
40
35
  with:
41
36
  path: "./vendor/bundle"
42
37
  key: v1/${{ runner.os }}/ruby-${{ matrix.ruby }}/${{ hashFiles('**/Gemfile.lock') }}
43
38
  restore-keys: v1/${{ runner.os }}/ruby-${{ matrix.ruby }}/
44
39
  - run: bundle install
45
- # Run each spec via the SimpleCov runner so per-process coverage is
46
- # recorded and merged, then collate the merged result and enforce the gate.
47
- - run: for file in specs/*.rb; do echo "$file"; bundle exec ruby specs/support/run_spec.rb "$file"; done
48
- - run: bundle exec ruby specs/support/coverage_check.rb
40
+ - run: for file in specs/*.rb; do echo $file; bundle exec ruby $file; done
data/.gitignore CHANGED
@@ -1,4 +1 @@
1
1
  Gemfile.lock
2
- /vendor/
3
- /.bundle/
4
- coverage/
data/Gemfile CHANGED
@@ -2,12 +2,8 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- # Pin appium_lib to the 12.x line so all Ruby legs (2.6/2.7/3.0) resolve
6
- # appium_lib_core ~> 5.0, which still exposes Appium::Core::Base::SearchContext.
7
- # Unpinned, Ruby 3.0 resolved appium_lib 15 / appium_lib_core 9.x, which removed
8
- # that constant and broke the specs. appium_console 3.0.0 requires appium_lib = 12.0.0.
9
- gem 'appium_console', '~> 3.0'
10
- gem 'appium_lib', '~> 12.0'
5
+ gem 'appium_console'
6
+ gem 'appium_lib', '>= 11.2.0'
11
7
  gem 'dotenv'
12
8
  gem 'minitest'
13
9
  gem 'rubocop', require: false
@@ -15,4 +11,3 @@ gem 'tempfile'
15
11
  gem 'webmock', '~> 3.18', '>= 3.18.1'
16
12
  gem 'webrick', '~> 1.3', '>= 1.3.1'
17
13
  gem 'rake', '~> 13.0'
18
- gem 'simplecov', require: false
@@ -15,16 +15,8 @@ module Percy
15
15
  def device_screen_size
16
16
  caps = capabilities
17
17
  caps = caps.as_json unless caps.is_a?(Hash)
18
- # Use string keys to match the IosMetadata implementation and every
19
- # consumer (generic_provider, app_automate, _get_tag), all of which read
20
- # device_screen_size['width'] / ['height'].
21
- if caps['deviceScreenSize'].nil?
22
- size = driver.window_size
23
- { 'width' => size.width.to_i, 'height' => size.height.to_i }
24
- else
25
- width, height = caps['deviceScreenSize'].split('x')
26
- { 'width' => width.to_i, 'height' => height.to_i }
27
- end
18
+ width, height = caps['deviceScreenSize'].split('x')
19
+ { 'width' => width.to_i, 'height' => height.to_i }
28
20
  end
29
21
 
30
22
  def get_system_bars
@@ -9,8 +9,7 @@ module Percy
9
9
  def self.resolve(driver)
10
10
  capabilities = driver.capabilities
11
11
  capabilities = capabilities.as_json unless capabilities.is_a?(Hash)
12
- key = capabilities.keys.find { |k| k.downcase.gsub('_', '') == 'platformname' }
13
- platform_name = capabilities[key]&.downcase
12
+ platform_name = capabilities.fetch('platformName', '').downcase
14
13
  case platform_name
15
14
  when 'android'
16
15
  Percy::AndroidMetadata.new(driver)
@@ -137,10 +137,7 @@ module Percy
137
137
 
138
138
  def get_regions_by_xpath(elements_array, xpaths)
139
139
  xpaths.each do |xpath|
140
- # Pass the :xpath finder symbol directly. appium_lib_core 12.x removed
141
- # Appium::Core::Base::SearchContext::FINDERS; the symbol form works
142
- # across all supported appium_lib versions.
143
- element = driver.find_element(:xpath, xpath)
140
+ element = driver.find_element(Appium::Core::Base::SearchContext::FINDERS[:xpath], xpath)
144
141
  selector = "xpath: #{xpath}"
145
142
  if element
146
143
  region = get_region_object(selector, element)
data/percy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Percy
4
- VERSION = '0.0.9'.freeze
4
+ VERSION = '1.0.0'.freeze
5
5
  end
@@ -29,35 +29,7 @@ class TestAndroidMetadata < Minitest::Test
29
29
  @mock_webdriver.expect(:capabilities, android_capabilities.merge('viewportRect' => viewport))
30
30
 
31
31
  assert(viewport, @android_metadata.viewport)
32
- @mock_webdriver.verify
33
- end
34
-
35
- def test_device_screen_size_when_device_screen_size_is_nil
36
- # Mock capabilities to return a hash without 'deviceScreenSize'
37
- android_capabilities = get_android_capabilities
38
- android_capabilities.delete('deviceScreenSize')
39
- @mock_webdriver.expect(:capabilities, android_capabilities)
40
-
41
- # Mock driver.window_size to return a double with width and height
42
- mock_window_size = Minitest::Mock.new
43
- mock_window_size.expect(:width, 1080)
44
- mock_window_size.expect(:height, 1920)
45
- @mock_webdriver.expect(:window_size, mock_window_size)
46
32
 
47
- # Call the method and assert the result
48
- result = @android_metadata.device_screen_size
49
- assert_equal({ 'width' => 1080, 'height' => 1920 }, result)
50
-
51
- # Verify mocks
52
- mock_window_size.verify
53
- @mock_webdriver.verify
54
- end
55
-
56
- def test_device_screen_size_when_device_screen_size_is_present
57
- # 'deviceScreenSize' => '1080x2280' from get_android_capabilities; parsed into string-keyed hash
58
- @mock_webdriver.expect(:capabilities, get_android_capabilities)
59
- result = @android_metadata.device_screen_size
60
- assert_equal({ 'width' => 1080, 'height' => 2280 }, result)
61
33
  @mock_webdriver.verify
62
34
  end
63
35
 
@@ -67,170 +67,15 @@ class TestAppAutomate < Minitest::Test
67
67
  end
68
68
 
69
69
  def test_execute_percy_screenshot_end
70
- # Wrap the stub return hashes in explicit braces. Under Ruby 3 keyword
71
- # argument separation, a bare trailing hash is otherwise parsed as keyword
72
- # args to Minitest's stub, raising ArgumentError.
73
- @app_automate.stub(:execute_percy_screenshot_begin, { 'deviceName' => 'abc', 'osVersion' => '123' }) do
70
+ @app_automate.stub(:execute_percy_screenshot_begin, 'deviceName' => 'abc', 'osVersion' => '123') do
74
71
  @app_automate.stub(:execute_percy_screenshot_end, nil) do
75
- @app_automate.stub(:screenshot, { 'link' => 'https://link' }) do
72
+ @app_automate.stub(:screenshot, 'link' => 'https://link') do
76
73
  @app_automate.screenshot('name')
77
74
  end
78
75
  end
79
76
  end
80
77
  end
81
78
 
82
- # Covers app_automate.rb lines 34-36: the rescue branch in #screenshot where the
83
- # super call fails, #execute_percy_screenshot_end is invoked with 'failure', and the
84
- # error is re-raised.
85
- def test_screenshot_failure_calls_end_and_reraises
86
- captured = {}
87
- @app_automate.stub(:execute_percy_screenshot_begin, nil) do
88
- # super (GenericProvider#screenshot) calls _get_tiles first; make it blow up.
89
- @app_automate.stub(:_get_tiles, ->(**_kwargs) { raise StandardError, 'boom' }) do
90
- @app_automate.stub(:execute_percy_screenshot_end,
91
- lambda do |name, url, status, sync = nil, message = nil|
92
- captured[:name] = name
93
- captured[:url] = url
94
- captured[:status] = status
95
- captured[:sync] = sync
96
- captured[:message] = message
97
- end) do
98
- error = assert_raises(StandardError) { @app_automate.screenshot('failing-shot') }
99
- assert_equal 'boom', error.message
100
- end
101
- end
102
- end
103
- assert_equal 'failing-shot', captured[:name]
104
- assert_equal 'failure', captured[:status]
105
- assert_equal 'boom', captured[:message]
106
- end
107
-
108
- # Covers app_automate.rb lines 24-26: when begin returns session details, the device
109
- # metadata and debug url are set before the super screenshot (driven through the stubbed
110
- # collaborators _get_tiles/_find_regions/_post_screenshots) succeeds.
111
- def test_screenshot_success_sets_metadata_and_debug_url
112
- session_details = {
113
- 'deviceName' => 'Google Pixel 7',
114
- 'osVersion' => '13.0',
115
- 'buildHash' => 'bhash',
116
- 'sessionHash' => 'shash'
117
- }
118
- post_response = { 'link' => 'https://link', 'data' => { 'ok' => true } }
119
- @app_automate.stub(:execute_percy_screenshot_begin, session_details) do
120
- @app_automate.stub(:_get_tiles, []) do
121
- @app_automate.stub(:_get_tag, {}) do
122
- @app_automate.stub(:_find_regions, []) do
123
- @app_automate.stub(:_post_screenshots, post_response) do
124
- @app_automate.stub(:execute_percy_screenshot_end, nil) do
125
- result = @app_automate.screenshot('shot')
126
- assert_equal({ 'ok' => true }, result)
127
- end
128
- end
129
- end
130
- end
131
- end
132
- end
133
- assert_equal 'Google Pixel 7', @metadata.device_name
134
- @mock_webdriver.expect(:capabilities, get_android_capabilities)
135
- assert_equal '13', @metadata.os_version
136
- assert_equal 'https://app-automate.browserstack.com/dashboard/v2/builds/bhash/sessions/shash',
137
- @app_automate.get_debug_url
138
- end
139
-
140
- # Covers app_automate.rb line 50: when PERCY_DISABLE_REMOTE_UPLOADS=true and the
141
- # screenshot is NOT fullpage, _get_tiles falls back to the generic provider's _get_tiles
142
- # (run for real against the mock webdriver), returning a single file-backed tile.
143
- def test_get_tiles_disable_remote_uploads_non_fullpage_falls_back_to_super
144
- ENV['PERCY_DISABLE_REMOTE_UPLOADS'] = 'true'
145
- @mock_webdriver.expect(:screenshot_as, 'png-bytes', [:png])
146
-
147
- tiles = nil
148
- @metadata.stub(:status_bar_height, 100) do
149
- @metadata.stub(:navigation_bar_height, 150) do
150
- tiles = @app_automate._get_tiles(fullpage: false)
151
- end
152
- end
153
-
154
- assert_equal 1, tiles.length
155
- dict_tile = tiles[0].to_h
156
- assert_includes dict_tile, 'filepath'
157
- assert(File.exist?(dict_tile['filepath']))
158
- assert_equal 100, dict_tile['status_bar_height']
159
- assert_equal 150, dict_tile['nav_bar_height']
160
- File.delete(tiles[0].filepath)
161
- ensure
162
- ENV.delete('PERCY_DISABLE_REMOTE_UPLOADS')
163
- end
164
-
165
- # Covers app_automate.rb lines 49 (fullpage warning puts) and the path that does NOT
166
- # fall back to super (line 50 skipped because fullpage_ss is true): full remote
167
- # screenshot orchestration continues with PERCY_DISABLE_REMOTE_UPLOADS=true.
168
- def test_get_tiles_disable_remote_uploads_fullpage_warns_and_continues
169
- ENV['PERCY_DISABLE_REMOTE_UPLOADS'] = 'true'
170
- metadata_mock = Minitest::Mock.new
171
- metadata_mock.expect(:device_screen_size, { 'width' => 1080, 'height' => 1920 })
172
- metadata_mock.expect(:scale_factor, 1)
173
- metadata_mock.expect(:status_bar_height, 100)
174
- metadata_mock.expect(:navigation_bar_height, 150)
175
-
176
- @metadata.stub(:device_screen_size, { 'width' => 1080, 'height' => 1920 }) do
177
- @metadata.stub(:scale_factor, 1) do
178
- @metadata.stub(:status_bar_height, 100) do
179
- @metadata.stub(:navigation_bar_height, 150) do
180
- @app_automate.stub(:execute_percy_screenshot, {
181
- 'result' => '[{"sha":"abc-1234","header_height":10,"footer_height":20}]'
182
- }) do
183
- tiles = @app_automate._get_tiles(fullpage: true)
184
- assert_equal 1, tiles.length
185
- assert_equal 'abc', tiles[0].sha
186
- assert_equal 100, tiles[0].status_bar_height
187
- assert_equal 150, tiles[0].nav_bar_height
188
- assert_equal 10, tiles[0].header_height
189
- assert_equal 20, tiles[0].footer_height
190
- end
191
- end
192
- end
193
- end
194
- end
195
- ensure
196
- ENV.delete('PERCY_DISABLE_REMOTE_UPLOADS')
197
- end
198
-
199
- # Covers app_automate.rb lines 100-104: the rescue in #execute_percy_screenshot_begin
200
- # when execute_script raises; it logs and returns nil.
201
- def test_execute_percy_screenshot_begin_handles_error_returns_nil
202
- @mock_webdriver.expect(:execute_script, nil) do
203
- raise StandardError, 'begin failed'
204
- end
205
- result = @app_automate.execute_percy_screenshot_begin('Screenshot 1')
206
- assert_nil result
207
- @mock_webdriver.verify
208
- end
209
-
210
- # Covers app_automate.rb lines 123-125: the rescue in #execute_percy_screenshot_end
211
- # when execute_script raises; it logs and swallows the error (returns nil).
212
- def test_execute_percy_screenshot_end_handles_error
213
- @mock_webdriver.expect(:execute_script, nil) do
214
- raise StandardError, 'end failed'
215
- end
216
- result = @app_automate.execute_percy_screenshot_end('Screenshot 1', 'snapshot-url', 'success')
217
- assert_nil result
218
- @mock_webdriver.verify
219
- end
220
-
221
- # Covers app_automate.rb lines 156-159: the rescue in #execute_percy_screenshot when
222
- # execute_script raises; it logs and re-raises the error.
223
- def test_execute_percy_screenshot_handles_error_reraises
224
- @mock_webdriver.expect(:execute_script, nil) do
225
- raise StandardError, 'screenshot failed'
226
- end
227
- error = assert_raises(StandardError) do
228
- @app_automate.execute_percy_screenshot(1080, 'singlepage', 5)
229
- end
230
- assert_equal 'screenshot failed', error.message
231
- @mock_webdriver.verify
232
- end
233
-
234
79
  def test_get_tiles
235
80
  # Mocking Percy::Metadata's session_id method
236
81
  metadata_mock = Minitest::Mock.new
data/specs/app_percy.rb CHANGED
@@ -136,33 +136,6 @@ class TestAppPercy < Minitest::Test
136
136
  end
137
137
  end
138
138
 
139
- def test_throws_error_when_string_arg_type_mismatch
140
- # Three constructions, each followed by a screenshot call with a
141
- # non-String value for test_case / labels / th_test_case_execution_id.
142
- 3.times do
143
- @mock_android_webdriver.expect(:is_a?, true, [Appium::Core::Base::Driver])
144
- end
145
- 20.times do
146
- @mock_android_webdriver.expect(:capabilities, get_android_capabilities)
147
- end
148
- 3.times do
149
- @mock_android_webdriver.expect(:instance_variable_get, @bridge, [:@bridge])
150
- @http.expect(:instance_variable_get, @server_url, [:@server_url])
151
- @bridge.expect(:instance_variable_get, @http, [:@http])
152
- @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
153
- end
154
-
155
- assert_raises(TypeError) do
156
- Percy::AppPercy.new(@mock_android_webdriver).screenshot('screenshot 1', test_case: 123)
157
- end
158
- assert_raises(TypeError) do
159
- Percy::AppPercy.new(@mock_android_webdriver).screenshot('screenshot 1', labels: 123)
160
- end
161
- assert_raises(TypeError) do
162
- Percy::AppPercy.new(@mock_android_webdriver).screenshot('screenshot 1', th_test_case_execution_id: 123)
163
- end
164
- end
165
-
166
139
  private
167
140
 
168
141
  def disable_percy_options(mock_webdriver, num = 1)
data/specs/cli_wrapper.rb CHANGED
@@ -16,20 +16,6 @@ def mock_poa_screenshot(fail: false)
16
16
  .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
17
17
  end
18
18
 
19
- def mock_healthcheck_with_version(version)
20
- stub_request(:get, 'http://localhost:5338/percy/healthcheck')
21
- .to_return(
22
- body: JSON.dump(success: true, build: { 'id' => '123', 'url' => 'dummy_url' }, type: 'automate'),
23
- status: 200,
24
- headers: { 'X-Percy-Core-Version' => version }
25
- )
26
- end
27
-
28
- def mock_failed_event(fail: false)
29
- stub_request(:post, 'http://localhost:5338/percy/events')
30
- .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
31
- end
32
-
33
19
  # Test suite for the Percy::CLIWrapper class
34
20
  class TestCLIWrapper < Minitest::Test
35
21
  def setup
@@ -157,56 +143,4 @@ class TestCLIWrapper < Minitest::Test
157
143
  assert_nil response['labels']
158
144
  assert_nil response['th_test_case_execution_id']
159
145
  end
160
-
161
- # Targets cli_wrapper.rb lines 42-43: CLI minor version below 27 disables the SDK.
162
- def test_percy_enabled_with_unsupported_minor_version
163
- Percy::CLIWrapper.instance_variable_set(:@percy_enabled, nil)
164
- mock_healthcheck_with_version('1.26.0')
165
-
166
- assert_output(/Please upgrade to the latest CLI version/) do
167
- assert_equal(false, Percy::CLIWrapper.percy_enabled?)
168
- end
169
- ensure
170
- Percy::CLIWrapper.instance_variable_set(:@percy_enabled, nil)
171
- end
172
-
173
- # Targets cli_wrapper.rb lines 74, 79, 80, 89: post_failed_event success path
174
- # builds the body, posts to /percy/events and parses the response.
175
- def test_post_failed_event_success
176
- mock_failed_event
177
-
178
- result = Percy::CLIWrapper.post_failed_event('some error')
179
-
180
- assert_equal({ 'success' => true }, result)
181
- end
182
-
183
- # Targets cli_wrapper.rb lines 83-86 (error branch) and 91-92 (rescue -> nil):
184
- # a non-200 response raises CLIException internally which is rescued and nil returned.
185
- def test_post_failed_event_handles_error_response
186
- mock_failed_event(fail: true)
187
-
188
- assert_nil Percy::CLIWrapper.post_failed_event('some error')
189
- end
190
-
191
- # Targets cli_wrapper.rb lines 91-92: a network/parse failure is rescued and nil returned.
192
- def test_post_failed_event_rescues_exceptions
193
- stub_request(:post, 'http://localhost:5338/percy/events').to_raise(StandardError.new('boom'))
194
-
195
- assert_nil Percy::CLIWrapper.post_failed_event('some error')
196
- end
197
-
198
- # Targets cli_wrapper.rb lines 121-122: response is a 2xx success but not exactly 200,
199
- # so the error message is extracted from the body and raised.
200
- def test_post_poa_screenshots_raises_on_non_200_success
201
- stub_request(:post, 'http://localhost:5338/percy/automateScreenshot')
202
- .to_return(body: '{"success": false, "error": "poa error"}', status: 201)
203
-
204
- error = assert_raises(CLIException) do
205
- @cli_wrapper.post_poa_screenshots(
206
- 'snapshot', 'session-id', 'http://example.com',
207
- { 'browser' => 'chrome' }, { 'platform' => 'Windows' }, {}
208
- )
209
- end
210
- assert_equal('poa error', error.message)
211
- end
212
146
  end
@@ -5,7 +5,6 @@ require 'minitest/mock'
5
5
  require 'appium_lib'
6
6
 
7
7
  require_relative '../percy/metadata/driver_metadata'
8
- require_relative '../percy/lib/cache'
9
8
 
10
9
  # Test suite for the Percy::DriverMetadata class
11
10
  class TestDriverMetadata < Minitest::Test
@@ -67,25 +66,4 @@ class TestDriverMetadata < Minitest::Test
67
66
 
68
67
  assert(session_caps, @metadata.session_capabilities)
69
68
  end
70
-
71
- def test_session_capabilities_caches_desired_capabilities_on_cache_miss
72
- # Force a clean cache and use a unique session id so the cache lookup misses
73
- # and the desired_capabilities branch (set_cache) is exercised.
74
- Percy::Cache.force_cleanup_cache
75
- session_id = 'session_id_session_caps_miss'
76
- desired_caps = {
77
- 'platform' => 'chrome_android',
78
- 'browserVersion' => '115.0.1',
79
- 'session_name' => 'abc'
80
- }
81
- # session_id is read by session_capabilities, get_cache and set_cache.
82
- 3.times { @mock_webdriver.expect(:session_id, session_id) }
83
- @mock_webdriver.expect(:desired_capabilities, desired_caps)
84
-
85
- fetched = @metadata.session_capabilities
86
- assert_equal(desired_caps, fetched)
87
- # Now the value is cached: read straight back without another driver call.
88
- @mock_webdriver.expect(:session_id, session_id)
89
- assert_equal(desired_caps, @metadata.session_capabilities)
90
- end
91
69
  end
@@ -13,13 +13,6 @@ class TestGenericProvider < Minitest::Test
13
13
  include Appium
14
14
  COMPARISON_RESPONSE = { 'comparison' => { 'id' => 123, 'url' => 'https://percy-build-url' } }.freeze
15
15
 
16
- # Some tests below permanently redefine Percy::Metadata#session_id and
17
- # Percy::AndroidMetadata#get_system_bars via class_eval to route them at fixed-count
18
- # mocks. Snapshot the pristine implementations once so teardown can restore them and the
19
- # suite stays order-independent (the original suite is flaky on certain seeds otherwise).
20
- PRISTINE_SESSION_ID = Percy::Metadata.instance_method(:session_id)
21
- PRISTINE_GET_SYSTEM_BARS = Percy::AndroidMetadata.instance_method(:get_system_bars)
22
-
23
16
  def setup
24
17
  @existing_dir = 'existing-dir'
25
18
  teardown
@@ -44,20 +37,6 @@ class TestGenericProvider < Minitest::Test
44
37
  if Dir.exist?(@existing_dir)
45
38
  FileUtils.remove_dir(@existing_dir, force: true)
46
39
  end
47
- restore_pristine_metadata_methods
48
- end
49
-
50
- # Undo the class_eval monkey-patching done by tests like test_post_screenshots and
51
- # test_non_app_automate so subsequent tests see the real implementations.
52
- def restore_pristine_metadata_methods
53
- pristine_session_id = PRISTINE_SESSION_ID
54
- pristine_get_system_bars = PRISTINE_GET_SYSTEM_BARS
55
- Percy::Metadata.class_eval do
56
- define_method(:session_id) { |*args, &blk| pristine_session_id.bind(self).call(*args, &blk) }
57
- end
58
- Percy::AndroidMetadata.class_eval do
59
- define_method(:get_system_bars) { |*args, &blk| pristine_get_system_bars.bind(self).call(*args, &blk) }
60
- end
61
40
  end
62
41
 
63
42
  def test_get_dir_without_env_variable
@@ -154,30 +133,6 @@ class TestGenericProvider < Minitest::Test
154
133
  File.delete(tile.filepath)
155
134
  end
156
135
 
157
- # Covers generic_provider.rb line 75: requesting a fullpage screenshot on the generic
158
- # provider logs the "only supported on App Automate" fallback message and still produces
159
- # a single page tile.
160
- def test_get_tiles_fullpage_logs_fallback
161
- status_bar_height = 11
162
- nav_bar_height = 22
163
- logged = []
164
- @generic_provider.stub(:log, ->(message, **_kwargs) { logged << message }) do
165
- tile = @generic_provider._get_tiles(
166
- fullpage: true,
167
- status_bar_height: status_bar_height,
168
- nav_bar_height: nav_bar_height
169
- )[0]
170
- dict_tile = tile.to_h
171
- assert_includes dict_tile, 'filepath'
172
- assert(File.exist?(dict_tile['filepath']))
173
- assert_equal status_bar_height, dict_tile['status_bar_height']
174
- assert_equal nav_bar_height, dict_tile['nav_bar_height']
175
- File.delete(tile.filepath)
176
- end
177
- assert_includes logged,
178
- 'Full page screenshot is only supported on App Automate. Falling back to single page screenshot.'
179
- end
180
-
181
136
  def test_get_tiles_kwargs
182
137
  status_bar_height = 135
183
138
  nav_bar_height = 246
@@ -301,7 +256,7 @@ class TestGenericProvider < Minitest::Test
301
256
  end
302
257
 
303
258
  @mock_webdriver.expect(:find_element, mock_element,
304
- [:xpath, '//path/to/element'])
259
+ [Appium::Core::Base::SearchContext::FINDERS[:xpath], '//path/to/element'])
305
260
 
306
261
  elements_array = []
307
262
  xpaths = ['//path/to/element']
@@ -316,7 +271,7 @@ class TestGenericProvider < Minitest::Test
316
271
  end
317
272
 
318
273
  def test_get_regions_by_xpath_with_non_existing_element
319
- @mock_webdriver.expect(:find_element, [:xpath, '//path/to/element']) do
274
+ @mock_webdriver.expect(:find_element, [Appium::Core::Base::SearchContext::FINDERS[:xpath], '//path/to/element']) do
320
275
  raise Appium::Core::Error::NoSuchElementError, 'Test error'
321
276
  end
322
277
  elements_array = []
@@ -354,7 +309,7 @@ class TestGenericProvider < Minitest::Test
354
309
  mock_element.expect(:size, Selenium::WebDriver::Dimension.new(100, 200))
355
310
  end
356
311
 
357
- @mock_webdriver.expect(:find_element, [:accessibility_id, 'id1']) do
312
+ @mock_webdriver.expect(:find_element, [Appium::Core::Base::SearchContext::FINDERS[:accessibility_id], 'id1']) do
358
313
  raise Appium::Core::Error::NoSuchElementError, 'Test error'
359
314
  end
360
315
 
@@ -69,25 +69,6 @@ class TestIOSMetadata < Minitest::Test
69
69
  assert_equal({ 'height' => 40 }, status_bar)
70
70
  end
71
71
 
72
- def test_status_bar_from_viewport_top
73
- # Seed a viewport with a non-zero `top` so status_bar uses the viewport
74
- # height (view_port['top']) instead of the static config scale_factor path.
75
- Percy::Cache.force_cleanup_cache
76
- session_id = 'session_id_viewport_top'
77
- Percy::Cache.set_cache(session_id, Percy::Cache::VIEWPORT, { 'top' => 30, 'height' => 200, 'width' => 100 })
78
-
79
- # status_bar -> viewport reads the seeded cache (1 session_id call); since
80
- # top != 0 it returns view_port['top'] directly.
81
- @mock_webdriver.expect(:session_id, session_id)
82
-
83
- status_bar = @ios_metadata.status_bar
84
- assert_equal({ 'height' => 30 }, status_bar)
85
- end
86
-
87
- def test_navigation_bar
88
- assert_equal({ 'height' => 0 }, @ios_metadata.navigation_bar)
89
- end
90
-
91
72
  def test_scale_factor_present_in_devices_json
92
73
  @mock_webdriver.expect(:capabilities, { 'deviceName' => 'iPhone 6' })
93
74
  assert_equal(2, @ios_metadata.scale_factor)
data/specs/metadata.rb CHANGED
@@ -108,16 +108,6 @@ class TestMetadata < Minitest::Test
108
108
  assert_equal('10', os_ver)
109
109
  end
110
110
 
111
- def test_metadata_os_version_returns_empty_string_on_error
112
- # os_version that does not respond to #to_f (a Hash) makes the
113
- # `os_version.to_f.to_i.to_s` conversion raise, exercising the rescue path.
114
- capabilities = { 'os_version' => {} }
115
- @mock_webdriver.expect(:capabilities, capabilities)
116
- @mock_webdriver.expect(:capabilities, capabilities)
117
- @mock_webdriver.expect(:capabilities, capabilities)
118
- assert_equal('', @metadata.os_version)
119
- end
120
-
121
111
  def test_metadata_value_from_devices_info_for_android
122
112
  android_device = 'google pixel 7'
123
113
  android_device_info = { '13' => { 'status_bar' => '118', 'nav_bar' => '63' } }
@@ -20,24 +20,6 @@ class MetadataResolverTestCase < Minitest::Test
20
20
  assert_instance_of(Percy::AndroidMetadata, resolved_metadata)
21
21
  @mock_webdriver.verify
22
22
  end
23
-
24
- def test_android_resolved_with_PLATFORM_NAME
25
- @mock_webdriver.expect(:capabilities, { 'PLATFORM_NAME' => 'Android' })
26
- @mock_webdriver.expect(:capabilities, { 'PLATFORM_NAME' => 'Android' })
27
- resolved_metadata = Percy::MetadataResolver.resolve(@mock_webdriver)
28
-
29
- assert_instance_of(Percy::AndroidMetadata, resolved_metadata)
30
- @mock_webdriver.verify
31
- end
32
-
33
- def test_android_resolved_with_platform_name
34
- @mock_webdriver.expect(:capabilities, { 'platform_name' => 'Android' })
35
- @mock_webdriver.expect(:capabilities, { 'platform_name' => 'Android' })
36
- resolved_metadata = Percy::MetadataResolver.resolve(@mock_webdriver)
37
-
38
- assert_instance_of(Percy::AndroidMetadata, resolved_metadata)
39
- @mock_webdriver.verify
40
- end
41
23
 
42
24
  def test_ios_resolved
43
25
  @mock_webdriver.expect(:capabilities, { 'platformName' => 'iOS' })
@@ -47,23 +29,6 @@ class MetadataResolverTestCase < Minitest::Test
47
29
  @mock_webdriver.verify
48
30
  end
49
31
 
50
- def test_ios_resolved_with_platform_name
51
- @mock_webdriver.expect(:capabilities, { 'platform_name' => 'iOS' })
52
- resolved_metadata = Percy::MetadataResolver.resolve(@mock_webdriver)
53
-
54
- assert_instance_of(Percy::IOSMetadata, resolved_metadata)
55
- @mock_webdriver.verify
56
- end
57
-
58
- def test_platformName_precedence_over_platform_name
59
- @mock_webdriver.expect(:capabilities, { 'platformName' => 'Android', 'platform_name' => 'iOS' })
60
- @mock_webdriver.expect(:capabilities, { 'platformName' => 'Android', 'platform_name' => 'iOS' })
61
- resolved_metadata = Percy::MetadataResolver.resolve(@mock_webdriver)
62
-
63
- assert_instance_of(Percy::AndroidMetadata, resolved_metadata)
64
- @mock_webdriver.verify
65
- end
66
-
67
32
  def test_unknown_platform_exception
68
33
  @mock_webdriver.expect(:capabilities, { 'platformName' => 'Something Random' })
69
34
 
@@ -73,14 +38,4 @@ class MetadataResolverTestCase < Minitest::Test
73
38
 
74
39
  @mock_webdriver.verify
75
40
  end
76
-
77
- def test_unknown_platform_exception_with_platform_name
78
- @mock_webdriver.expect(:capabilities, { 'platform_name' => 'Something Random' })
79
-
80
- assert_raises(PlatformNotSupported) do
81
- Percy::MetadataResolver.resolve(@mock_webdriver)
82
- end
83
-
84
- @mock_webdriver.verify
85
- end
86
41
  end
data/specs/screenshot.rb CHANGED
@@ -383,7 +383,7 @@ class TestPercyScreenshot < Minitest::Test
383
383
  })
384
384
  end
385
385
  driver.expect(:find_element, mock_element,
386
- [:xpath, '//path/to/element'])
386
+ [Appium::Core::Base::SearchContext::FINDERS[:xpath], '//path/to/element'])
387
387
 
388
388
  percy_screenshot(driver, 'screenshot 1', ignore_regions_xpaths: xpaths)
389
389
 
@@ -416,87 +416,4 @@ class TestPercyScreenshot < Minitest::Test
416
416
  mock_healthcheck(type: 'automate')
417
417
  assert_raises(Exception) { percy_screenshot(@mock_webdriver) }
418
418
  end
419
-
420
- def test_swallows_screenshot_error_when_ignore_errors_true
421
- mock_healthcheck
422
- # /percy/events is hit by post_failed_event in the rescue path.
423
- stub_request(:post, 'http://localhost:5338/percy/events')
424
- .to_return(status: 200, body: '{"success": true}')
425
-
426
- # Force the AppPercy (non-automate) provider so the rescue path in
427
- # percy-appium-app.rb is exercised. session_type is memoized across tests.
428
- Percy::Environment.session_type = nil
429
-
430
- # Android caps have percy:options.ignoreErrors => true, so the error is
431
- # swallowed and percy_screenshot returns nil. A non-String name makes
432
- # AppPercy#screenshot raise a TypeError after construction succeeds.
433
- driver = build_app_driver(get_android_capabilities)
434
-
435
- result = nil
436
- assert_output(/Could not take screenshot/) do
437
- result = percy_screenshot(driver, 12_345)
438
- end
439
- assert_nil result
440
- assert(@requests.any? { |r| r.uri.path == '/percy/events' })
441
- end
442
-
443
- def test_reraises_screenshot_error_when_ignore_errors_false
444
- mock_healthcheck
445
- stub_request(:post, 'http://localhost:5338/percy/events')
446
- .to_return(status: 200, body: '{"success": true}')
447
-
448
- Percy::Environment.session_type = nil
449
-
450
- # Build caps whose percy:options.ignoreErrors is false so the rescue path
451
- # re-raises after posting the failed event.
452
- caps = get_android_capabilities.dup
453
- caps['percy:options'] = { 'enabled' => true, 'ignoreErrors' => false }
454
- caps['percyOptions'] = { 'enabled' => true, 'ignoreErrors' => false }
455
- driver = build_app_driver(caps)
456
-
457
- assert_output(/Could not take screenshot/) do
458
- assert_raises(TypeError) { percy_screenshot(driver, 12_345) }
459
- end
460
- assert(@requests.any? { |r| r.uri.path == '/percy/events' })
461
- end
462
-
463
- private
464
-
465
- # Builds a Minitest::Mock driver that survives AppPercy construction
466
- # (is_a? Appium driver, capabilities, remote_url) for the rescue-path tests.
467
- def build_app_driver(caps)
468
- driver = Minitest::Mock.new
469
- bridge = Minitest::Mock.new
470
- http = Minitest::Mock.new
471
- server_url = Minitest::Mock.new
472
-
473
- driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
474
- 20.times { driver.expect(:capabilities, caps) }
475
- 3.times do
476
- driver.expect(:instance_variable_get, bridge, [:@bridge])
477
- http.expect(:instance_variable_get, server_url, [:@server_url])
478
- bridge.expect(:instance_variable_get, http, [:@http])
479
- server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
480
- end
481
- driver
482
- end
483
- end
484
-
485
- # Test suite for the `hashed` helper in percy/common/common.rb
486
- class TestHashedHelper < Minitest::Test
487
- # Stand-in object that responds to #as_json (core objects do not in this env).
488
- class JsonableDouble
489
- def as_json
490
- { 'converted' => true }
491
- end
492
- end
493
-
494
- def test_hashed_returns_hash_unchanged
495
- input = { 'a' => 1 }
496
- assert_same(input, hashed(input))
497
- end
498
-
499
- def test_hashed_converts_non_hash_via_as_json
500
- assert_equal({ 'converted' => true }, hashed(JsonableDouble.new))
501
- end
502
419
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: percy-appium-app
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BroswerStack
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2024-10-28 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: appium_lib
@@ -177,12 +178,8 @@ files:
177
178
  - specs/metadata.rb
178
179
  - specs/metadata_resolver.rb
179
180
  - specs/mocks/mock_methods.rb
180
- - specs/percy_automate.rb
181
181
  - specs/percy_options.rb
182
182
  - specs/screenshot.rb
183
- - specs/support/coverage_check.rb
184
- - specs/support/run_spec.rb
185
- - specs/support/spec_helper.rb
186
183
  - specs/tile.rb
187
184
  homepage: ''
188
185
  licenses:
@@ -190,6 +187,7 @@ licenses:
190
187
  metadata:
191
188
  bug_tracker_uri: https://github.com/percy/percy-appium-ruby/issues
192
189
  source_code_uri: https://github.com/percy/percy-appium-ruby
190
+ post_install_message:
193
191
  rdoc_options: []
194
192
  require_paths:
195
193
  - percy
@@ -204,7 +202,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
202
  - !ruby/object:Gem::Version
205
203
  version: '0'
206
204
  requirements: []
207
- rubygems_version: 3.6.9
205
+ rubygems_version: 3.1.4
206
+ signing_key:
208
207
  specification_version: 4
209
208
  summary: Percy visual testing for Ruby Appium Mobile Apps
210
209
  test_files: []
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/autorun'
4
- require 'json'
5
- require 'webmock/minitest'
6
- require 'appium_lib'
7
-
8
- require_relative '../percy/lib/percy_automate'
9
- require_relative '../percy/lib/percy_options'
10
- require_relative '../percy/lib/cli_wrapper'
11
- require_relative 'mocks/mock_methods'
12
-
13
- def mock_poa_screenshot(fail: false)
14
- stub_request(:post, 'http://localhost:5338/percy/automateScreenshot')
15
- .to_return(body: "{\"success\": #{fail ? 'false, "error": "test"' : 'true'}}", status: (fail ? 500 : 200))
16
- end
17
-
18
- # Test suite for the Percy::PercyOnAutomate class
19
- class TestPercyOnAutomate < Minitest::Test
20
- def setup
21
- @mock_driver = Minitest::Mock.new
22
- @bridge = Minitest::Mock.new
23
- @http = Minitest::Mock.new
24
- @server_url = Minitest::Mock.new
25
- WebMock.enable!
26
- end
27
-
28
- def teardown
29
- WebMock.disable!
30
- end
31
-
32
- # Targets percy_automate.rb line 17: an unsupported driver raises DriverNotSupported.
33
- def test_initialize_raises_for_unsupported_driver
34
- assert_raises(DriverNotSupported) do
35
- Percy::PercyOnAutomate.new(Object.new)
36
- end
37
- end
38
-
39
- # Targets percy_automate.rb lines 56-58: when the screenshot post fails, the error
40
- # is rescued and logged rather than propagated.
41
- def test_screenshot_rescues_and_logs_on_failure
42
- mock_poa_screenshot(fail: true)
43
-
44
- @mock_driver.expect(:is_a?, true, [Appium::Core::Base::Driver])
45
- @mock_driver.expect(:capabilities, { 'percy:options' => { 'enabled' => true } })
46
-
47
- 5.times do
48
- @mock_driver.expect(:session_id, 'Dummy_session_id')
49
- end
50
- @mock_driver.expect(:instance_variable_get, @bridge, [:@bridge])
51
- @http.expect(:instance_variable_get, @server_url, [:@server_url])
52
- @bridge.expect(:instance_variable_get, @http, [:@http])
53
- @server_url.expect(:to_s, 'https://hub-cloud.browserstack.com/wd/hub')
54
- @mock_driver.expect(:capabilities, { 'key' => 'value' })
55
- @mock_driver.expect(:desired_capabilities, { 'key' => 'value' })
56
-
57
- percy = Percy::PercyOnAutomate.new(@mock_driver)
58
-
59
- result = nil
60
- assert_output(/Could not take Screenshot 'Snapshot 1'/) do
61
- result = percy.screenshot('Snapshot 1', options: {})
62
- end
63
- assert_nil result
64
- end
65
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Collates the per-process SimpleCov results written during the spec loop and
4
- # enforces the coverage gate. Run after all specs:
5
- # bundle exec ruby specs/support/coverage_check.rb
6
- require 'simplecov'
7
-
8
- SimpleCov.collate Dir['coverage/.resultset.json'] do
9
- add_filter '/specs/'
10
- track_files 'percy/**/*.rb'
11
- minimum_coverage line: 100
12
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Per-spec runner used by CI: `bundle exec ruby specs/support/run_spec.rb specs/<file>.rb`.
4
- # Requires spec_helper first (starting SimpleCov within the bundler context, which
5
- # a command-line `-r` flag cannot do reliably) and then loads the target spec.
6
- require_relative 'spec_helper'
7
-
8
- spec_file = ARGV[0] or abort('usage: run_spec.rb <spec_file>')
9
- load File.expand_path(spec_file)
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Loaded (via `ruby -r ./specs/support/spec_helper`) before each spec so
4
- # SimpleCov can instrument the percy source. The suite runs one spec per
5
- # process, so each process records its own result under a unique command_name;
6
- # SimpleCov merges them into coverage/.resultset.json, and
7
- # specs/support/coverage_check.rb collates the merged result and enforces the gate.
8
- require 'simplecov'
9
-
10
- SimpleCov.start do
11
- add_filter '/specs/'
12
- track_files 'percy/**/*.rb'
13
- command_name "specs:#{File.basename(ARGV.first || $PROGRAM_NAME)}"
14
- merge_timeout 3600
15
- end