ruby_raider 3.13.0 → 4.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.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/runtime.yml +38 -0
  3. data/.github/workflows/system_tests.yml +1 -23
  4. data/.reek.yml +1 -0
  5. data/.rspec +1 -0
  6. data/Gemfile +2 -0
  7. data/README.md +16 -49
  8. data/RELEASE.md +22 -0
  9. data/bin/raider +5 -0
  10. data/lib/commands/scaffolding_commands.rb +2 -25
  11. data/lib/commands/utility_commands.rb +23 -3
  12. data/lib/generators/automation/automation_generator.rb +0 -5
  13. data/lib/generators/automation/templates/component.tt +1 -0
  14. data/lib/generators/automation/templates/home.tt +2 -2
  15. data/lib/generators/automation/templates/login.tt +6 -26
  16. data/lib/generators/automation/templates/page.tt +23 -18
  17. data/lib/generators/automation/templates/partials/home_page_selector.tt +1 -2
  18. data/lib/generators/automation/templates/partials/initialize_selector.tt +0 -8
  19. data/lib/generators/automation/templates/partials/pdp_page_selector.tt +1 -2
  20. data/lib/generators/automation/templates/partials/url_methods.tt +8 -2
  21. data/lib/generators/automation/templates/partials/visit_method.tt +0 -5
  22. data/lib/generators/automation/templates/pdp.tt +1 -9
  23. data/lib/generators/common_generator.rb +0 -2
  24. data/lib/generators/generator.rb +12 -19
  25. data/lib/generators/helper_generator.rb +21 -19
  26. data/lib/generators/infrastructure/templates/azure_pipelines.tt +1 -1
  27. data/lib/generators/infrastructure/templates/github.tt +2 -3
  28. data/lib/generators/infrastructure/templates/github_appium.tt +0 -8
  29. data/lib/generators/invoke_generators.rb +4 -23
  30. data/lib/generators/menu_generator.rb +1 -9
  31. data/lib/generators/rspec/templates/accessibility_spec.tt +1 -9
  32. data/lib/generators/rspec/templates/spec.tt +8 -24
  33. data/lib/generators/template_renderer/partial_cache.rb +3 -1
  34. data/lib/generators/template_renderer.rb +1 -1
  35. data/lib/generators/templates/common/gemfile.tt +3 -10
  36. data/lib/generators/templates/common/git_ignore.tt +0 -5
  37. data/lib/generators/templates/common/partials/mobile_config.tt +3 -1
  38. data/lib/generators/templates/common/partials/web_config.tt +10 -1
  39. data/lib/generators/templates/common/rakefile.tt +0 -15
  40. data/lib/generators/templates/common/read_me.tt +357 -250
  41. data/lib/generators/templates/common/rubocop.tt +0 -2
  42. data/lib/generators/templates/helpers/driver_helper.tt +8 -0
  43. data/lib/generators/templates/helpers/gesture_helper.tt +78 -0
  44. data/lib/generators/templates/helpers/network_helper.tt +521 -0
  45. data/lib/generators/templates/helpers/partials/quit_driver.tt +0 -2
  46. data/lib/generators/templates/helpers/partials/screenshot.tt +0 -2
  47. data/lib/generators/templates/helpers/partials/selenium_driver.tt +5 -2
  48. data/lib/generators/templates/helpers/screenshot_helper.tt +490 -0
  49. data/lib/generators/templates/helpers/spec_helper.tt +39 -29
  50. data/lib/generators/templates/helpers/trace_helper.tt +240 -95
  51. data/lib/generators/templates/helpers/video_helper.tt +3 -8
  52. data/lib/ruby_raider/runtime/actionability.rb +129 -0
  53. data/lib/ruby_raider/runtime/actionable.rb +49 -0
  54. data/lib/ruby_raider/runtime/by_role.rb +55 -0
  55. data/lib/ruby_raider/runtime/config.rb +79 -0
  56. data/lib/ruby_raider/runtime/context.rb +38 -0
  57. data/lib/ruby_raider/runtime/errors.rb +69 -0
  58. data/lib/ruby_raider/runtime/finders.rb +73 -0
  59. data/lib/ruby_raider/runtime/js/actionability.js +134 -0
  60. data/lib/ruby_raider/runtime/js/role_engine.js +315 -0
  61. data/lib/ruby_raider/runtime/locator_converter.rb +72 -0
  62. data/lib/ruby_raider/runtime/matchers/base.rb +222 -0
  63. data/lib/ruby_raider/runtime/matchers/element.rb +183 -0
  64. data/lib/ruby_raider/runtime/matchers/page.rb +71 -0
  65. data/lib/ruby_raider/runtime/matchers/presence.rb +41 -0
  66. data/lib/ruby_raider/runtime/matchers/retrying_block.rb +104 -0
  67. data/lib/ruby_raider/runtime/observer.rb +170 -0
  68. data/lib/ruby_raider/runtime/role_finder.rb +83 -0
  69. data/lib/ruby_raider/runtime/rspec.rb +78 -0
  70. data/lib/ruby_raider/runtime/selectors.rb +60 -0
  71. data/lib/ruby_raider/runtime/strict.rb +142 -0
  72. data/lib/ruby_raider/runtime/wait.rb +84 -0
  73. data/lib/ruby_raider/runtime.rb +20 -0
  74. data/lib/scaffolding/name_normalizer.rb +1 -1
  75. data/lib/scaffolding/project_detector.rb +2 -28
  76. data/lib/scaffolding/scaffolding.rb +0 -20
  77. data/lib/scaffolding/templates/component.tt +0 -14
  78. data/lib/scaffolding/templates/page_object.tt +2 -14
  79. data/lib/scaffolding/templates/spec.tt +1 -9
  80. data/lib/utilities/utilities.rb +10 -8
  81. data/lib/version +1 -1
  82. data/ruby_raider.gemspec +2 -1
  83. data/sig/commands/scaffolding_commands.rbs +0 -2
  84. data/sig/commands/utility_commands.rbs +6 -0
  85. data/sig/generators/generator.rbs +0 -4
  86. data/sig/generators/helper_generator.rbs +0 -1
  87. data/sig/generators/menu_generator.rbs +0 -1
  88. data/sig/ruby_raider/runtime/actionability.rbs +25 -0
  89. data/sig/ruby_raider/runtime/actionable.rbs +13 -0
  90. data/sig/ruby_raider/runtime/by_role.rbs +15 -0
  91. data/sig/ruby_raider/runtime/config.rbs +26 -0
  92. data/sig/ruby_raider/runtime/context.rbs +12 -0
  93. data/sig/ruby_raider/runtime/errors.rbs +36 -0
  94. data/sig/ruby_raider/runtime/finders.rbs +26 -0
  95. data/sig/ruby_raider/runtime/locator_converter.rbs +21 -0
  96. data/sig/ruby_raider/runtime/matchers/base.rbs +64 -0
  97. data/sig/ruby_raider/runtime/matchers/element.rbs +78 -0
  98. data/sig/ruby_raider/runtime/matchers/page.rbs +38 -0
  99. data/sig/ruby_raider/runtime/matchers/presence.rbs +21 -0
  100. data/sig/ruby_raider/runtime/matchers/retrying_block.rbs +27 -0
  101. data/sig/ruby_raider/runtime/observer.rbs +46 -0
  102. data/sig/ruby_raider/runtime/role_finder.rbs +18 -0
  103. data/sig/ruby_raider/runtime/selectors.rbs +21 -0
  104. data/sig/ruby_raider/runtime/strict.rbs +35 -0
  105. data/sig/ruby_raider/runtime/wait.rbs +17 -0
  106. data/sig/ruby_raider/runtime.rbs +2 -0
  107. data/sig/scaffolding/project_detector.rbs +0 -4
  108. data/sig/scaffolding/scaffolding.rbs +0 -5
  109. data/sig/utilities/utilities.rbs +2 -1
  110. data/sig/vendor/selenium_webdriver.rbs +71 -0
  111. data/spec/commands/raider_commands_spec.rb +38 -3
  112. data/spec/commands/scaffolding_commands_spec.rb +27 -0
  113. data/spec/commands/utility_commands_spec.rb +53 -0
  114. data/spec/generators/gesture_helper_spec.rb +123 -0
  115. data/spec/generators/network_helper_spec.rb +634 -0
  116. data/spec/generators/screenshot_helper_spec.rb +776 -0
  117. data/spec/generators/template_renderer_spec.rb +0 -4
  118. data/spec/generators/trace_helper_spec.rb +489 -97
  119. data/spec/generators/video_helper_spec.rb +0 -9
  120. data/spec/integration/commands/browser_update_after_creation_spec.rb +0 -8
  121. data/spec/integration/commands/scaffolding_commands_spec.rb +12 -70
  122. data/spec/integration/commands/utility_commands_spec.rb +19 -41
  123. data/spec/integration/content/azure_content_spec.rb +2 -6
  124. data/spec/integration/content/ci_content_spec.rb +1 -55
  125. data/spec/integration/content/common_content_spec.rb +1 -118
  126. data/spec/integration/content/config_content_spec.rb +34 -31
  127. data/spec/integration/content/gemfile_content_spec.rb +7 -72
  128. data/spec/integration/content/helper_content_spec.rb +105 -99
  129. data/spec/integration/content/page_content_spec.rb +85 -87
  130. data/spec/integration/content/readme_content_spec.rb +134 -75
  131. data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
  132. data/spec/integration/content/rubocop_content_spec.rb +1 -1
  133. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  134. data/spec/integration/content/test_content_spec.rb +23 -147
  135. data/spec/integration/end_to_end_features_spec.rb +1 -43
  136. data/spec/integration/end_to_end_spec.rb +319 -326
  137. data/spec/integration/generators/automation_generator_spec.rb +3 -37
  138. data/spec/integration/generators/axe_addon_spec.rb +2 -35
  139. data/spec/integration/generators/azure_generator_spec.rb +1 -1
  140. data/spec/integration/generators/common_generator_spec.rb +1 -40
  141. data/spec/integration/generators/config_features_spec.rb +1 -24
  142. data/spec/integration/generators/github_generator_spec.rb +0 -12
  143. data/spec/integration/generators/helpers_generator_spec.rb +74 -80
  144. data/spec/integration/generators/invoke_generators_spec.rb +54 -45
  145. data/spec/integration/generators/rspec_generator_spec.rb +0 -6
  146. data/spec/integration/scaffolding_e2e_spec.rb +17 -179
  147. data/spec/integration/spec_helper.rb +4 -6
  148. data/spec/integration/trace_format_checker.rb +11 -2
  149. data/spec/integration/trace_page_server.rb +72 -5
  150. data/spec/menus/menu_generator_spec.rb +14 -39
  151. data/spec/runtime/actionability_spec.rb +207 -0
  152. data/spec/runtime/actionable_spec.rb +125 -0
  153. data/spec/runtime/browser/actionability_script_browser_spec.rb +150 -0
  154. data/spec/runtime/browser/actionable_browser_spec.rb +98 -0
  155. data/spec/runtime/browser/finders_browser_spec.rb +148 -0
  156. data/spec/runtime/browser/matchers_browser_spec.rb +67 -0
  157. data/spec/runtime/browser/retrying_block_browser_spec.rb +37 -0
  158. data/spec/runtime/browser/role_engine_browser_spec.rb +184 -0
  159. data/spec/runtime/browser/strict_browser_spec.rb +38 -0
  160. data/spec/runtime/by_role_spec.rb +52 -0
  161. data/spec/runtime/config_spec.rb +79 -0
  162. data/spec/runtime/context_spec.rb +73 -0
  163. data/spec/runtime/errors_spec.rb +89 -0
  164. data/spec/runtime/finders_spec.rb +165 -0
  165. data/spec/runtime/fixtures/actionability.html +70 -0
  166. data/spec/runtime/fixtures/locators.html +46 -0
  167. data/spec/runtime/fixtures/matchers.html +33 -0
  168. data/spec/runtime/fixtures/roles.html +162 -0
  169. data/spec/runtime/matchers/base_spec.rb +165 -0
  170. data/spec/runtime/matchers/element_spec.rb +277 -0
  171. data/spec/runtime/matchers/page_spec.rb +102 -0
  172. data/spec/runtime/matchers/presence_spec.rb +59 -0
  173. data/spec/runtime/matchers/retrying_block_spec.rb +165 -0
  174. data/spec/runtime/observer_spec.rb +341 -0
  175. data/spec/runtime/role_finder_spec.rb +156 -0
  176. data/spec/runtime/role_query_spec.rb +75 -0
  177. data/spec/runtime/selectors_spec.rb +135 -0
  178. data/spec/runtime/strict_spec.rb +257 -0
  179. data/spec/runtime/support/browser.rb +48 -0
  180. data/spec/runtime/support/fake_bridge.rb +117 -0
  181. data/spec/runtime/wait_spec.rb +71 -0
  182. data/spec/scaffolding/name_normalizer_spec.rb +3 -2
  183. data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -43
  184. data/spec/system/selenium_spec.rb +1 -5
  185. data/spec/system/support/system_test_helper.rb +2 -5
  186. data/spec/utilities/headless_config_spec.rb +0 -7
  187. data/spec/utilities/utilities_spec.rb +24 -0
  188. metadata +113 -48
  189. data/lib/generators/automation/templates/header.tt +0 -9
  190. data/lib/generators/cucumber/cucumber_generator.rb +0 -58
  191. data/lib/generators/cucumber/templates/accessibility_feature.tt +0 -5
  192. data/lib/generators/cucumber/templates/accessibility_steps.tt +0 -17
  193. data/lib/generators/cucumber/templates/cucumber.tt +0 -1
  194. data/lib/generators/cucumber/templates/env.tt +0 -7
  195. data/lib/generators/cucumber/templates/feature.tt +0 -21
  196. data/lib/generators/cucumber/templates/partials/appium_env.tt +0 -26
  197. data/lib/generators/cucumber/templates/partials/driver_world.tt +0 -18
  198. data/lib/generators/cucumber/templates/partials/mobile_steps.tt +0 -20
  199. data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -120
  200. data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -48
  201. data/lib/generators/cucumber/templates/partials/watir_world.tt +0 -4
  202. data/lib/generators/cucumber/templates/partials/web_steps.tt +0 -28
  203. data/lib/generators/cucumber/templates/steps.tt +0 -5
  204. data/lib/generators/cucumber/templates/user_factory.tt +0 -15
  205. data/lib/generators/cucumber/templates/world.tt +0 -7
  206. data/lib/generators/templates/helpers/browser_helper.tt +0 -31
  207. data/lib/generators/templates/helpers/raider_element.tt +0 -159
  208. data/lib/generators/templates/helpers/wait_helper.tt +0 -611
  209. data/lib/scaffolding/templates/feature.tt +0 -6
  210. data/lib/scaffolding/templates/steps.tt +0 -19
  211. data/sig/generators/cucumber/cucumber_generator.rbs +0 -12
  212. data/spec/fixtures/watir_master/cucumber_watir/Readme.md.snapshot +0 -175
  213. data/spec/fixtures/watir_master/cucumber_watir/features/step_definitions/login_steps.rb.snapshot +0 -24
  214. data/spec/fixtures/watir_master/cucumber_watir/page_objects/abstract/page.rb.snapshot +0 -37
  215. data/spec/fixtures/watir_master/cucumber_watir/page_objects/pages/login.rb.snapshot +0 -33
  216. data/spec/fixtures/watir_master/rspec_watir/Readme.md.snapshot +0 -174
  217. data/spec/fixtures/watir_master/rspec_watir/page_objects/abstract/page.rb.snapshot +0 -37
  218. data/spec/fixtures/watir_master/rspec_watir/page_objects/pages/login.rb.snapshot +0 -33
  219. data/spec/fixtures/watir_master/rspec_watir/spec/login_page_spec.rb.snapshot +0 -49
  220. data/spec/integration/content/cucumber_reporter_content_spec.rb +0 -161
  221. data/spec/integration/generators/cucumber_generator_spec.rb +0 -100
  222. data/spec/integration/helpers/wait_helper_mobile_spec.rb +0 -281
  223. data/spec/integration/helpers/wait_helper_spec.rb +0 -679
  224. data/spec/integration/helpers/wait_helper_support.rb +0 -183
  225. data/spec/system/watir_spec.rb +0 -38
@@ -0,0 +1,71 @@
1
+ # Minimal Selenium WebDriver stubs for type checking (covers only what Raider's runtime touches)
2
+ module Selenium
3
+ module WebDriver
4
+ VERSION: String
5
+
6
+ module Error
7
+ class WebDriverError < StandardError
8
+ end
9
+ class ElementNotInteractableError < WebDriverError
10
+ end
11
+ class UnsupportedOperationError < WebDriverError
12
+ end
13
+ class TimeoutError < WebDriverError
14
+ end
15
+ class NoSuchElementError < WebDriverError
16
+ end
17
+ class StaleElementReferenceError < WebDriverError
18
+ end
19
+ end
20
+
21
+ module SearchContext
22
+ FINDERS: Hash[Symbol, String]
23
+
24
+ def self.extra_finders: () -> Hash[Symbol, String]?
25
+ def self.extra_finders=: (Hash[Symbol, String]?) -> Hash[Symbol, String]?
26
+ def self.finders: () -> Hash[Symbol, String]
27
+
28
+ def bridge: () -> untyped
29
+ def find_element: (*untyped) -> Element
30
+ def find_elements: (*untyped) -> Array[Element]
31
+ end
32
+
33
+ class Element
34
+ include SearchContext
35
+
36
+ def initialize: (untyped bridge, String id) -> void
37
+ def tag_name: () -> String
38
+ def attribute: (String) -> String?
39
+ def text: () -> String
40
+ def displayed?: () -> bool
41
+ def enabled?: () -> bool
42
+ def ref: () -> [Symbol, String?]
43
+ def click: () -> void
44
+ def send_keys: (*untyped) -> void
45
+ def clear: () -> void
46
+ def submit: () -> void
47
+ end
48
+
49
+ class Driver
50
+ include SearchContext
51
+
52
+ def ref: () -> [Symbol, String?]
53
+ end
54
+
55
+ module Remote
56
+ class Bridge
57
+ class LocatorConverter
58
+ def convert: (String | Symbol, untyped) -> [String, untyped]
59
+ end
60
+
61
+ def self.locator_converter: () -> untyped
62
+ def self.locator_converter=: (untyped) -> untyped
63
+
64
+ def find_element_by: (*untyped) -> untyped
65
+ def find_elements_by: (*untyped) -> untyped
66
+ def execute_script: (String, *untyped) -> untyped
67
+ def current_context: () -> String
68
+ end
69
+ end
70
+ end
71
+ end
@@ -121,8 +121,8 @@ RSpec.describe RubyRaider::Raider do # rubocop:disable RSpec/FilePath
121
121
 
122
122
  let(:raider_bin) { File.expand_path('../../bin/raider', __dir__) }
123
123
 
124
- def run_raider(*args, chdir: Dir.pwd)
125
- Open3.capture3(RbConfig.ruby, raider_bin, *args, chdir:)
124
+ def run_raider(*args, chdir: Dir.pwd, env: {})
125
+ Open3.capture3(env, RbConfig.ruby, raider_bin, *args, chdir:)
126
126
  end
127
127
 
128
128
  it 'exits non-zero for an unknown command' do
@@ -143,7 +143,8 @@ RSpec.describe RubyRaider::Raider do # rubocop:disable RSpec/FilePath
143
143
  out, err, status = run_raider('new', 'x', '-p', 'framework:minitest', 'automation:selenium', chdir: dir)
144
144
 
145
145
  expect(status.exitstatus).not_to eq(0)
146
- expect(out + err).to include('framework', 'rspec', 'cucumber')
146
+ expect(out + err).to include('framework', 'rspec')
147
+ expect(out + err).not_to include('cucumber')
147
148
  expect(File.exist?(File.join(dir, 'x'))).to be(false)
148
149
  end
149
150
  end
@@ -167,5 +168,39 @@ RSpec.describe RubyRaider::Raider do # rubocop:disable RSpec/FilePath
167
168
  expect(File.exist?(File.join(dir, 'x'))).to be(false)
168
169
  end
169
170
  end
171
+
172
+ it 'exits 1 with the Invalid automation message for automation:watir and creates nothing' do
173
+ Dir.mktmpdir do |dir|
174
+ out, err, status = run_raider('new', 'x', '-p', 'framework:rspec', 'automation:watir', chdir: dir)
175
+
176
+ expect(status.exitstatus).to eq(1)
177
+ expect(out + err).to include('Invalid automation "watir"')
178
+ expect(File.exist?(File.join(dir, 'x'))).to be(false)
179
+ end
180
+ end
181
+
182
+ describe 'raider new under a US-ASCII locale' do
183
+ # macOS gives GUI apps no LANG, so a shell spawned by the desktop runs Ruby with a US-ASCII
184
+ # default_external; the templates hold UTF-8 bytes and must still render. The generated README holds
185
+ # an ellipsis, so the rendered file proves the bytes came through. RUBYOPT is unset so a
186
+ # -E or -K flag from the outer environment cannot hide the locale.
187
+ { 'LC_ALL=C' => { 'LANG' => nil, 'LC_CTYPE' => nil, 'LC_ALL' => 'C', 'RUBYOPT' => nil },
188
+ 'no locale variables' => { 'LANG' => nil, 'LC_CTYPE' => nil, 'LC_ALL' => nil, 'RUBYOPT' => nil } }
189
+ .each do |label, env|
190
+ it "renders the config under #{label}" do
191
+ Dir.mktmpdir do |dir|
192
+ _out, err, status = run_raider('new', 'x', '-p', 'framework:rspec', 'automation:selenium',
193
+ chdir: dir, env:)
194
+
195
+ expect(err).not_to include('invalid multibyte')
196
+ expect(status.exitstatus).to eq(0)
197
+ readme = File.read(File.join(dir, 'x/Readme.md'), encoding: 'UTF-8')
198
+ expect(readme).to include("`expect { account_page.welcome_message }.to have_text(\u2026)`")
199
+ config = File.read(File.join(dir, 'x/config/config.yml'), encoding: 'UTF-8')
200
+ expect(YAML.safe_load(config, permitted_classes: [Symbol])['url']).to include('http')
201
+ end
202
+ end
203
+ end
204
+ end
170
205
  end
171
206
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
+ require 'tmpdir'
5
+ require 'rbconfig'
6
+ require_relative '../../lib/commands/scaffolding_commands'
4
7
 
5
8
  RSpec.describe 'ScaffoldingCommands' do
6
9
  describe 'dependencies' do
@@ -19,4 +22,28 @@ RSpec.describe 'ScaffoldingCommands' do
19
22
  expect(stdout.strip).to eq('OK')
20
23
  end
21
24
  end
25
+
26
+ describe 'commands' do
27
+ it 'has no feature or steps command' do
28
+ expect(ScaffoldingCommands.all_commands.keys).not_to include('feature', 'steps')
29
+ end
30
+ end
31
+
32
+ describe 'scaffold in a directory that looks like a Cucumber project' do
33
+ let(:raider_bin) { File.expand_path('../../bin/raider', __dir__) }
34
+
35
+ it 'writes the spec and the page and nothing under features' do
36
+ Dir.mktmpdir do |dir|
37
+ File.write(File.join(dir, 'Gemfile'), "gem 'cucumber'\n")
38
+ FileUtils.mkdir_p(File.join(dir, 'features'))
39
+
40
+ _out, err, status = Open3.capture3(RbConfig.ruby, raider_bin, 'g', 'scaffold', 'login', chdir: dir)
41
+
42
+ expect(status.success?).to be(true), err
43
+ expect(File).to exist(File.join(dir, 'spec/login_page_spec.rb'))
44
+ expect(File).to exist(File.join(dir, 'page_objects/pages/login.rb'))
45
+ expect(Dir.glob(File.join(dir, 'features/**/*')).select { |f| File.file?(f) }).to be_empty
46
+ end
47
+ end
48
+ end
22
49
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require_relative '../../lib/ruby_raider'
6
+
7
+ RSpec.describe UtilityCommands do
8
+ let(:tmp_dir) { 'tmp_utility_commands_test' }
9
+
10
+ before do
11
+ FileUtils.mkdir_p("#{tmp_dir}/config")
12
+ Dir.chdir(tmp_dir)
13
+ File.write('config/config.yml', "browser: chrome\ntimeout: 10\n")
14
+ Utilities.instance_variable_set(:@config, nil)
15
+ end
16
+
17
+ after do
18
+ Dir.chdir('..')
19
+ FileUtils.rm_rf(tmp_dir)
20
+ Utilities.instance_variable_set(:@config, nil)
21
+ end
22
+
23
+ describe 'strict' do
24
+ it 'writes strict: false for off' do
25
+ described_class.new.invoke(:strict, nil, %w[off])
26
+ expect(YAML.load_file('config/config.yml')['strict']).to be false
27
+ end
28
+
29
+ it 'writes strict: true for on' do
30
+ described_class.new.invoke(:strict, nil, %w[on])
31
+ expect(YAML.load_file('config/config.yml')['strict']).to be true
32
+ end
33
+ end
34
+
35
+ describe 'actionability' do
36
+ it 'writes actionability: true for on' do
37
+ described_class.new.invoke(:actionability, nil, %w[on])
38
+ expect(YAML.load_file('config/config.yml')['actionability']).to be true
39
+ end
40
+
41
+ it 'writes actionability: false for off' do
42
+ described_class.new.invoke(:actionability, nil, %w[off])
43
+ expect(YAML.load_file('config/config.yml')['actionability']).to be false
44
+ end
45
+ end
46
+
47
+ describe 'help' do
48
+ it 'lists both toggles' do
49
+ expect { described_class.start(%w[help]) }
50
+ .to output(%r{actionability \[on/off\].*strict \[on/off\]}m).to_stdout
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'tmpdir'
5
+ require_relative '../../lib/generators/helper_generator'
6
+ require_relative '../../lib/ruby_raider/runtime'
7
+
8
+ # Stands in for the Appium driver: only the calls the gestures make.
9
+ class GestureDriver
10
+ Rect = Struct.new(:x, :y, :width, :height)
11
+ Element = Struct.new(:id)
12
+
13
+ attr_reader :appium_device
14
+
15
+ def initialize(device)
16
+ @appium_device = device
17
+ end
18
+
19
+ def execute_script(*); end
20
+
21
+ def find_element(*); end
22
+
23
+ def window_rect
24
+ Rect.new(0, 10, 400, 800)
25
+ end
26
+ end
27
+
28
+ class GesturePage
29
+ attr_reader :driver
30
+
31
+ def initialize(driver)
32
+ @driver = driver
33
+ end
34
+
35
+ def self.rendered
36
+ generator = HelpersGenerator.new(%w[ios rspec proj])
37
+ template = File.expand_path('../../lib/generators/templates/helpers/gesture_helper.tt', __dir__)
38
+ ERB.new(File.read(template), trim_mode: '-').result(generator.instance_eval { binding })
39
+ end
40
+ end
41
+
42
+ Dir.mktmpdir do |dir|
43
+ path = File.join(dir, 'gesture_helper.rb')
44
+ File.write(path, GesturePage.rendered)
45
+ load path
46
+ end
47
+ GesturePage.include(GestureHelper)
48
+
49
+ describe 'gesture_helper template' do
50
+ let(:element) { GestureDriver::Element.new('el-1') }
51
+ let(:driver) { GestureDriver.new(:android) }
52
+ let(:page) { GesturePage.new(driver) }
53
+
54
+ context 'on iOS' do
55
+ let(:driver) { GestureDriver.new(:ios) }
56
+
57
+ it 'swipes the screen with mobile: swipe' do
58
+ expect(driver).to receive(:execute_script).with('mobile: swipe', { direction: 'left' })
59
+ page.swipe('left')
60
+ end
61
+
62
+ it 'scrolls an element with mobile: scroll and its id' do
63
+ expect(driver).to receive(:execute_script).with('mobile: scroll', { direction: 'down', element: 'el-1' })
64
+ page.scroll(:down, element)
65
+ end
66
+ end
67
+
68
+ context 'on Android' do
69
+ let(:driver) { GestureDriver.new(:android) }
70
+
71
+ it 'swipes the screen with mobile: swipeGesture over the window rect' do
72
+ expect(driver).to receive(:execute_script)
73
+ .with('mobile: swipeGesture', { left: 0, top: 10, width: 400, height: 800, direction: 'up', percent: 0.75 })
74
+ page.swipe('up')
75
+ end
76
+
77
+ it 'scrolls an element with mobile: scrollGesture and its element id' do
78
+ expect(driver).to receive(:execute_script)
79
+ .with('mobile: scrollGesture', { elementId: 'el-1', direction: 'down', percent: 0.75 })
80
+ page.scroll('down', element)
81
+ end
82
+ end
83
+
84
+ it 'rejects a direction other than up, down, left or right' do
85
+ driver = GestureDriver.new(:ios)
86
+ page = GesturePage.new(driver)
87
+ expect { page.swipe('diagonal') }.to raise_error(ArgumentError, /direction must be up, down, left or right/)
88
+ end
89
+
90
+ describe '#scroll_to' do
91
+ let(:locator) { { accessibility_id: 'footer' } }
92
+
93
+ let(:driver) { GestureDriver.new(:android) }
94
+
95
+ it 'returns the element without scrolling when it is already there' do
96
+ allow(driver).to receive(:find_element).with(locator).and_return(element)
97
+ expect(driver).not_to receive(:execute_script)
98
+ expect(page.scroll_to(locator)).to eq(element)
99
+ end
100
+
101
+ it 'scrolls between tries until the element appears' do
102
+ attempts = 0
103
+ immediate = []
104
+ allow(driver).to receive(:find_element).with(locator) do
105
+ immediate << Raider::Wait.immediate?
106
+ attempts += 1
107
+ raise Selenium::WebDriver::Error::NoSuchElementError, 'gone' if attempts < 3
108
+
109
+ element
110
+ end
111
+ expect(driver).to receive(:execute_script).twice
112
+ expect(page.scroll_to(locator, direction: 'up')).to eq(element)
113
+ expect(immediate).to eq([true, true, true])
114
+ end
115
+
116
+ it 'gives up after max_scrolls and names the locator' do
117
+ allow(driver).to receive(:find_element).and_raise(Selenium::WebDriver::Error::NoSuchElementError, 'gone')
118
+ expect(driver).to receive(:execute_script).exactly(3).times
119
+ expect { page.scroll_to(locator, max_scrolls: 3) }
120
+ .to raise_error(Selenium::WebDriver::Error::NoSuchElementError, /accessibility_id.*footer.*after 3 scrolls/)
121
+ end
122
+ end
123
+ end