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
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'content_helper'
4
-
5
- describe 'Cucumber raider_reporter wiring' do
6
- screenshot_calls = {
7
- 'selenium' => 'driver.save_screenshot(screenshot_path)',
8
- 'watir' => 'browser.screenshot.save(screenshot_path)'
9
- }
10
-
11
- AUTOMATION_TYPES.each do |automation|
12
- context "with cucumber and #{automation}" do
13
- let(:project_name) { "#{FrameworkIndex::CUCUMBER}_#{automation}" }
14
- let(:env) { read_generated(project_name, 'features/support/env.rb') }
15
- let(:screenshot_call) { screenshot_calls.fetch(automation, 'driver.screenshot(screenshot_path)') }
16
- let(:hook) { env[/^\s*After do.*\z/m] }
17
- let(:quit_call) { automation == 'watir' ? 'browser.quit' : 'driver.quit' }
18
-
19
- it 'activates the reporter by requiring raider_reporter/cucumber' do
20
- expect(env).to include("require 'raider_reporter/cucumber'")
21
- end
22
-
23
- it 'does not reference the allure helper' do
24
- expect(env).not_to match(/AllureHelper|allure_helper/)
25
- end
26
-
27
- it 'takes the screenshot only for failed scenarios, inside the guard block' do
28
- guarded = /if scenario\.failed\?\n\s+Dir\.mktmpdir do \|temp_folder\|\n(?<body>.*?)\n\s+end\n\s+end\nensure/m
29
- expect(hook.match(guarded)[:body]).to include(screenshot_call)
30
- end
31
-
32
- it 'uses a fixed file name in the tmpdir, never the scenario name' do
33
- expect(env).to include("screenshot_path = File.join(temp_folder, 'screenshot.png')")
34
- expect(hook).not_to include('scenario.name')
35
- end
36
-
37
- it 'quits the driver in an ensure of the After block, without a redundant begin' do
38
- quit = %w[android ios cross_platform].include?(automation) ? 'driver.quit_driver' : quit_call
39
- expect(hook).to match(/^\s*After do \|scenario\|\n.*\n^ensure\n.*\n\s+#{Regexp.escape(quit)}\nend/m)
40
- expect(hook).not_to match(/^\s+begin$/)
41
- end
42
-
43
- it 'attaches the screenshot path before quitting the driver' do
44
- attach = "RaiderReporter.attach('screenshot', screenshot_path, type: :png)"
45
- expect(env).to include(attach)
46
- expect(env.index(screenshot_call)).to be < env.index(attach)
47
- expect(env.index(attach)).to be < env.index(/driver\.quit|browser\.quit/, env.index(attach))
48
- end
49
-
50
- context 'with video recording' do
51
- target = automation == 'watir' ? 'browser' : 'driver'
52
- video_type = %w[selenium watir].include?(automation) ? ':webm' : ':mp4'
53
-
54
- it 'requires video_helper' do
55
- expect(env).to include("require_relative '../../helpers/video_helper'")
56
- end
57
-
58
- it 'starts the recorder in Before' do
59
- expect(env[/^Before do.*?^end$/m]).to include("@video_recorder = VideoHelper.recorder_for(#{target})", '@video_recorder.start')
60
- end
61
-
62
- it 'stops with keep: scenario.failed? before quitting, attaches the path and cleans up in the ensure' do
63
- expect(hook).to include('video_path = @video_recorder&.stop(keep: scenario.failed?)')
64
- expect(hook).to include("RaiderReporter.attach('video', video_path, type: #{video_type}) if video_path")
65
- expect(hook.index('@video_recorder&.stop')).to be < hook.index("RaiderReporter.attach('video'")
66
- expect(hook.index("RaiderReporter.attach('video'")).to be < hook.index(/^ensure/)
67
- expect(hook.index(/^ensure/)).to be < hook.index('@video_recorder&.cleanup')
68
- end
69
- end
70
-
71
- if automation == 'selenium'
72
- context 'with trace recording' do
73
- it 'requires trace_helper' do
74
- expect(env).to include("require_relative '../../helpers/trace_helper'")
75
- end
76
-
77
- it 'counts attempts per scenario location and starts the trace recorder after the video recorder' do
78
- before_hook = env[/^Before do.*?^end$/m]
79
- expect(env).to match(/^TRACE_ATTEMPTS = Hash\.new\(0\) unless defined\?\(TRACE_ATTEMPTS\)$/)
80
- expect(before_hook).to include('TRACE_ATTEMPTS[scenario.location.to_s] += 1', '@trace_recorder = TraceHelper.recorder_for(driver)')
81
- expect(before_hook).to match(/^Before do \|scenario\|$/)
82
- expect(before_hook.index('@video_recorder.start')).to be < before_hook.index('@trace_recorder = TraceHelper')
83
- expect(before_hook).to match(/if defined\?\(TraceHelper\)\n(.*\n)*.*@trace_recorder = TraceHelper/)
84
- end
85
-
86
- it 'stops with keep: TraceHelper.keep_all? || scenario.failed? before the video, attaches it and cleans up before quitting' do
87
- expect(hook).to include('TraceHelper.keep_all? || scenario.failed?', 'error: scenario.exception', 'attempt: @trace_attempt')
88
- expect(hook).to include("RaiderReporter.attach('trace', trace_path, type: :trace, attempt: @trace_attempt) if trace_path")
89
- order = [hook.index('@trace_recorder&.stop'), hook.index('@video_recorder&.stop'), hook.index("RaiderReporter.attach('trace'"),
90
- hook.index(/^ensure/), hook.index('@trace_recorder&.cleanup'), hook.index('@video_recorder&.cleanup'), hook.index('driver.quit')]
91
- expect(order).to all(be_an(Integer)).and eq(order.compact.sort)
92
- end
93
-
94
- it 'survives a Before that raised before the trace init, and a failing plugin setup or handler' do
95
- expect(hook).to include('@trace_test&.merge(')
96
- expect(hook).not_to include('@trace_test.merge(')
97
- plugin = env[/^InstallPlugin do.*?^end$/m]
98
- expect(plugin.scan('rescue StandardError => e').size).to eq(3)
99
- expect(plugin.index('AstLookup.new')).to be > plugin.index("begin\n")
100
- expect(plugin).to include('TraceHelper.warn_once("Gherkin step groups unavailable')
101
- end
102
-
103
- it 'turns tracing off with TraceHelper.disable! when a step group handler raises' do
104
- plugin = env[/^InstallPlugin do.*?^end$/m]
105
- expect(plugin.scan(/TraceHelper\.disable!\("step groups failed: \#\{e\.message\}"\) if defined\?\(TraceHelper\)/).size).to eq(2)
106
- expect(plugin).not_to include('tracing disabled')
107
- end
108
-
109
- it 'records each Gherkin step as a group through the InstallPlugin step events, skipping hooks' do
110
- plugin = env[/^InstallPlugin do.*?^end$/m]
111
- expect(plugin).to include('config.on_event(:test_step_started)', 'config.on_event(:test_step_finished)', 'next if event.test_step.hook?')
112
- expect(plugin).to include('TraceHelper.active_recorder&.step_started', 'TraceHelper.active_recorder&.step_finished')
113
- expect(plugin).to include('defined?(TraceHelper)')
114
- end
115
- end
116
- else
117
- it 'has no trace wiring' do
118
- expect(env).not_to match(/trace/i)
119
- end
120
- end
121
-
122
- it 'does not generate the allure helper' do
123
- expect(File).not_to exist(File.join(project_name, 'helpers/allure_helper.rb'))
124
- end
125
-
126
- it 'does not mention Allure in any generated file' do
127
- files = Dir.glob("#{project_name}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) }
128
- expect(files.select { |f| File.read(f).match?(/allure/i) }).to be_empty
129
- end
130
-
131
- it 'does not create an allure-results folder' do
132
- expect(File).not_to exist(File.join(project_name, 'allure-results'))
133
- end
134
- end
135
- end
136
- end
137
-
138
- describe 'Cucumber login reuse wiring' do
139
- { 'selenium' => 'driver', 'watir' => 'browser.driver' }.each do |automation, target|
140
- context "with cucumber and #{automation}" do
141
- let(:env) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'features/support/env.rb') }
142
-
143
- it 'requires session_state' do
144
- expect(env).to include("require_relative '../../helpers/session_state'")
145
- end
146
-
147
- it "reuses the saved login in Before('@logged_in'), defined after the plain Before" do
148
- expect(env).to include("Before('@logged_in') do", "SessionState.new(#{target}).reuse(",
149
- "UserFactory.for('registered user')")
150
- expect(env.index("Before('@logged_in')")).to be > env.index(/^Before do/)
151
- end
152
- end
153
- end
154
-
155
- %w[android ios cross_platform].each do |automation|
156
- it "does not mention session_state with cucumber and #{automation}" do
157
- env = read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'features/support/env.rb')
158
- expect(env).not_to include('session_state')
159
- end
160
- end
161
- end
@@ -1,100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../../lib/generators/cucumber/cucumber_generator'
4
- require_relative '../spec_helper'
5
-
6
- describe CucumberGenerator do
7
- shared_examples 'creates cucumber files' do |project_name, file_name|
8
- it 'creates a feature file' do
9
- expect(File).to exist("#{project_name}/features/#{file_name}.feature")
10
- end
11
-
12
- it 'creates a step definitions file' do
13
- expect(File).to exist("#{project_name}/features/step_definitions/#{file_name}_steps.rb")
14
- end
15
-
16
- it 'creates an env file' do
17
- expect(File).to exist("#{project_name}/features/support/env.rb")
18
- end
19
-
20
- it 'creates an cucumber.yml file' do
21
- expect(File).to exist("#{project_name}/cucumber.yml")
22
- end
23
-
24
- it 'does not create a spec file' do
25
- expect(File).not_to exist("#{project_name}/spec/home_spec.rb")
26
- end
27
- end
28
-
29
- shared_examples 'creates factories for web projects' do |project_name|
30
- it 'creates an data file' do
31
- expect(File).to exist("#{project_name}/models/data/users.yml")
32
- end
33
-
34
- it 'creates a model factory' do
35
- expect(File).to exist("#{project_name}/models/model_factory.rb")
36
- end
37
-
38
- it 'creates a user factory' do
39
- expect(File).to exist("#{project_name}/models/user_factory.rb")
40
- end
41
- end
42
-
43
- context 'with cucumber and appium android' do
44
- include_examples 'creates cucumber files', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::ANDROID}", 'home'
45
- end
46
-
47
- context 'with cucumber and appium ios' do
48
- include_examples 'creates cucumber files', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::IOS}", 'home'
49
- end
50
-
51
- context 'with cucumber and appium cross platform' do
52
- include_examples 'creates cucumber files', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::CROSS_PLATFORM}", 'home'
53
- end
54
-
55
- context 'with cucumber and selenium' do
56
- include_examples 'creates cucumber files', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}", 'login'
57
- include_examples 'creates factories for web projects', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}", 'login'
58
- end
59
-
60
- context 'with cucumber and watir' do
61
- include_examples 'creates cucumber files', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::WATIR}", 'login'
62
- include_examples 'creates factories for web projects', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::WATIR}", 'login'
63
- end
64
-
65
- describe 'without example tests' do
66
- %w[selenium ios cross_platform].each do |automation|
67
- context "with cucumber and #{automation}" do
68
- let(:name) { ensure_project(framework: 'cucumber', automation:, examples: false) }
69
-
70
- it 'creates no feature files' do
71
- expect(Dir.glob("#{name}/features/**/*.feature")).to be_empty
72
- end
73
-
74
- it 'keeps an empty step_definitions folder' do
75
- expect(Dir.children("#{name}/features/step_definitions")).to eq(['.keep'])
76
- end
77
-
78
- it 'creates the support files' do
79
- expect(Dir.children("#{name}/features/support")).to contain_exactly('env.rb', 'world.rb')
80
- end
81
-
82
- it 'creates the cucumber.yml file' do
83
- expect(File).to exist("#{name}/cucumber.yml")
84
- end
85
-
86
- it 'creates no user factory' do
87
- expect(File).not_to exist("#{name}/models/user_factory.rb")
88
- end
89
- end
90
- end
91
-
92
- it 'creates no accessibility example' do
93
- InvokeGenerators.generate_framework(framework: 'cucumber', automation: 'selenium', name: 'skeleton_axe_cucumber',
94
- examples: false, accessibility: true)
95
- expect(Dir.glob('skeleton_axe_cucumber/features/**/accessibility*')).to be_empty
96
- ensure
97
- FileUtils.rm_rf('skeleton_axe_cucumber')
98
- end
99
- end
100
- end
@@ -1,281 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'wait_helper_support'
4
-
5
- # The mobile render loads into App, so its WaitHelper and RaiderElement never meet the web
6
- # render's top-level ones when both spec files run in one process.
7
- module WaitHelperMobileSpec
8
- module App; end
9
- WaitHelperSpec.load_templates(mobile: true, into: App)
10
-
11
- class Page
12
- include App::WaitHelper
13
-
14
- attr_reader :driver
15
-
16
- def initialize(driver)
17
- @driver = driver
18
- end
19
- end
20
-
21
- RSpec.describe 'WaitHelper on mobile' do
22
- let(:driver) { WaitHelperSpec::FakeDriver.new(device: :ios) }
23
- let(:android) { WaitHelperSpec::FakeDriver.new(device: :android) }
24
- let(:page) { Page.new(driver) }
25
- let(:per_os) { { ios: { accessibility_id: 'ios-card' }, android: { accessibility_id: 'android-card' } } }
26
-
27
- def element(**state) = WaitHelperSpec::FakeElement.new(**state)
28
- def clock = Process.clock_gettime(Process::CLOCK_MONOTONIC)
29
- def screen_area = { left: 0, top: 20, width: 390, height: 800 }
30
-
31
- describe '#tap and #click' do
32
- it 'click once the element is visible, enabled and stable' do
33
- %i[tap click].each do |name|
34
- target = element(rects: [[0, 0, 10, 10], [5, 5, 10, 10], [5, 5, 10, 10]])
35
- driver.script({ accessibility_id: name.to_s }, nil, element(displayed: false), element(enabled: false), target)
36
- expect(page.public_send(name, { accessibility_id: name.to_s }, timeout: 2)).to eq(target)
37
- expect(target.log).to eq([[:click]])
38
- end
39
- end
40
- end
41
-
42
- describe 'tap with a block' do
43
- it 'is Object#tap on a RaiderElement: yields and returns the wrapper without clicking' do
44
- card = element
45
- driver.script({ accessibility_id: 'card' }, card)
46
- wrapper = page.find_element(accessibility_id: 'card')
47
- seen = nil
48
- expect(wrapper.tap { |yielded| seen = yielded }).to be(wrapper)
49
- expect([seen, card.log]).to eq([wrapper, []])
50
- end
51
-
52
- it 'is Object#tap on the mixin: yields self without looking anything up' do
53
- seen = nil
54
- expect(page.tap { |yielded| seen = yielded }).to be(page)
55
- expect(page.tap(&:driver)).to be(page)
56
- expect([seen, driver.finds, driver.scripts]).to eq([page, [], []])
57
- end
58
-
59
- it 'still clicks without a block' do
60
- card = element
61
- driver.script({ id: 'x' }, card)
62
- page.tap({ id: 'x' }, timeout: 1)
63
- page.tap(id: 'x')
64
- page.find_element(id: 'x').tap
65
- expect(card.log).to eq([[:click]] * 3)
66
- end
67
- end
68
-
69
- describe '#swipe' do
70
- it 'uses mobile: swipe on iOS' do
71
- card = element
72
- driver.script({ accessibility_id: 'card' }, card)
73
- expect(page.swipe('left', { accessibility_id: 'card' }, timeout: 1)).to eq(card)
74
- expect(driver.scripts).to eq([['mobile: swipe', { direction: 'left', element: card.id }]])
75
- end
76
-
77
- it 'uses mobile: swipeGesture on Android' do
78
- card = element
79
- android.script({ accessibility_id: 'card' }, card)
80
- Page.new(android).swipe('left', { accessibility_id: 'card' }, timeout: 1)
81
- expect(android.scripts)
82
- .to eq([['mobile: swipeGesture', { elementId: card.id, direction: 'left', percent: 0.75 }]])
83
- end
84
-
85
- it 'waits for the element to be visible first' do
86
- card = element
87
- driver.script({ accessibility_id: 'card' }, nil, element(displayed: false), card)
88
- page.swipe('up', { accessibility_id: 'card' }, timeout: 1)
89
- expect([driver.finds.size, driver.scripts.size]).to eq([3, 1])
90
- end
91
-
92
- it 'swipes the whole screen without a locator' do
93
- expect(page.swipe('up')).to be_nil
94
- Page.new(android).swipe('up')
95
- expect(driver.scripts).to eq([['mobile: swipe', { direction: 'up' }]])
96
- expect(android.scripts).to eq([['mobile: swipeGesture', { **screen_area, direction: 'up', percent: 0.75 }]])
97
- end
98
-
99
- it 'rejects a direction other than up, down, left or right' do
100
- expect { page.swipe('sideways') }.to raise_error(ArgumentError, /up, down, left or right/)
101
- expect(driver.scripts).to be_empty
102
- end
103
- end
104
-
105
- describe '#scroll' do
106
- it 'scrolls the screen without a locator' do
107
- page.scroll('down')
108
- Page.new(android).scroll('down')
109
- expect(driver.scripts).to eq([['mobile: scroll', { direction: 'down' }]])
110
- expect(android.scripts).to eq([['mobile: scrollGesture', { **screen_area, direction: 'down', percent: 0.75 }]])
111
- end
112
-
113
- it 'scrolls a container' do
114
- list = element
115
- android.script({ id: 'list' }, list)
116
- Page.new(android).scroll('up', { id: 'list' }, timeout: 1)
117
- expect(android.scripts).to eq([['mobile: scrollGesture', { elementId: list.id, direction: 'up', percent: 0.75 }]])
118
- end
119
- end
120
-
121
- describe 'an unknown device' do
122
- it 'raises NotImplementedError naming it, before waiting' do
123
- windows = WaitHelperSpec::FakeDriver.new(device: :windows)
124
- started = clock
125
- expect { Page.new(windows).swipe('left', { id: 'never' }, timeout: 2) }
126
- .to raise_error(NotImplementedError, /windows/)
127
- expect { Page.new(windows).scroll('down') }.to raise_error(NotImplementedError, /windows/)
128
- expect(clock - started).to be < 0.5
129
- end
130
- end
131
-
132
- describe '#scroll_to' do
133
- it 'returns a RaiderElement once the element turns visible after 2 scrolls' do
134
- footer = element
135
- driver.script({ accessibility_id: 'footer' }, nil, element(displayed: false), footer)
136
- found = page.scroll_to({ accessibility_id: 'footer' }, timeout: 2)
137
- expect(found).to be_a(App::RaiderElement).and eq(footer)
138
- expect(driver.scripts).to eq([['mobile: scroll', { direction: 'down' }]] * 2)
139
- end
140
-
141
- it 'scrolls in the given direction' do
142
- driver.script({ accessibility_id: 'header' }, nil, element)
143
- page.scroll_to({ accessibility_id: 'header' }, direction: 'up', timeout: 2)
144
- expect(driver.scripts).to eq([['mobile: scroll', { direction: 'up' }]])
145
- end
146
-
147
- it 'stops after max_scrolls scrolls and says so' do
148
- started = clock
149
- expect { page.scroll_to({ accessibility_id: 'footer' }, max_scrolls: 3, timeout: 5) }
150
- .to raise_error(App::WaitHelper::TimeoutError) { |error|
151
- expect(error.message).to include('to be visible').and include('after 3 scrolls')
152
- }
153
- expect(driver.scripts.size).to eq(3)
154
- expect(driver.finds.size).to eq(4)
155
- expect(clock - started).to be < 2
156
- end
157
-
158
- it 'also stops at the timeout, naming the scrolls so far' do
159
- expect { page.scroll_to({ accessibility_id: 'footer' }, timeout: 0) }
160
- .to raise_error(App::WaitHelper::TimeoutError, /after 0 scrolls/)
161
- expect(driver.scripts).to be_empty
162
- end
163
- end
164
-
165
- describe 'per-OS locators' do
166
- it 'resolve on tap and swipe' do
167
- card = element
168
- android.script({ accessibility_id: 'android-card' }, card)
169
- mobile = Page.new(android)
170
- mobile.tap(per_os, timeout: 1)
171
- mobile.swipe('right', per_os, timeout: 1)
172
- expect(android.finds.uniq).to eq([{ accessibility_id: 'android-card' }])
173
- expect([card.log, android.scripts.size]).to eq([[[:click]], 1])
174
- end
175
-
176
- it 'resolve on a nested find_element' do
177
- row = element
178
- list = element.script({ accessibility_id: 'ios-row' }, row)
179
- driver.script({ accessibility_id: 'ios-card' }, list)
180
- found = page.find_element(per_os).find_element({ ios: { accessibility_id: 'ios-row' }, android: { id: 'row' } })
181
- expect(found).to eq(row)
182
- end
183
- end
184
-
185
- describe 'RaiderElement' do
186
- it 'has the mobile set without the locator' do
187
- card = element
188
- driver.script({ accessibility_id: 'card' }, card)
189
- wrapper = page.find_element(accessibility_id: 'card')
190
- expect(wrapper.tap.swipe('left').scroll('down').scroll_to).to be(wrapper)
191
- expect(card.log).to eq([[:click]])
192
- expect(driver.scripts).to eq([['mobile: swipe', { direction: 'left', element: card.id }],
193
- ['mobile: scroll', { direction: 'down', element: card.id }]])
194
- end
195
-
196
- it 'adds exactly the planned public methods' do
197
- expect(App::RaiderElement.public_instance_methods(false).sort)
198
- .to eq(%i[== attribute clear click displayed? enabled? eql? find_element find_elements hash native scroll
199
- scroll_to selected? send_keys swipe tap text])
200
- end
201
- end
202
-
203
- describe 'the choke point' do
204
- let(:actions) { [] }
205
-
206
- before do
207
- driver.script({ id: 'x' }, element.script({ css: 'y' }, element).script_many({ css: 'y' }, [element]))
208
- allow_any_instance_of(App::WaitHelper::Waiter).to receive(:perform_action).and_wrap_original do |original, *args, **options, &block|
209
- actions << args.first
210
- original.call(*args, **options, &block)
211
- end
212
- end
213
-
214
- def calls(locator)
215
- { find_element: [locator], find_elements: [locator], click: [locator], tap: [locator], clear: [locator],
216
- text: [locator], displayed?: [locator], enabled?: [locator], selected?: [locator],
217
- send_keys: [locator, 'a'], attribute: [locator, 'name'], swipe: ['left', locator],
218
- scroll: ['down', locator], scroll_to: [locator] }
219
- end
220
-
221
- it 'is called once per mixin method, with the method name' do
222
- calls({ id: 'x' }).each do |name, args|
223
- actions.clear
224
- page.public_send(name, *args, timeout: 1)
225
- expect(actions).to eq([name])
226
- end
227
- end
228
-
229
- it 'is called once for a whole-screen gesture' do
230
- %i[swipe scroll].each do |name|
231
- actions.clear
232
- page.public_send(name, 'up')
233
- expect(actions).to eq([name])
234
- end
235
- end
236
-
237
- it 'is called once per wrapper method, with the method name' do
238
- wrapper = page.find_element(id: 'x')
239
- calls({ css: 'y' }).each do |name, args|
240
- args = args.reject { |arg| arg.is_a?(Hash) } unless %i[find_element find_elements].include?(name)
241
- actions.clear
242
- wrapper.public_send(name, *args, timeout: 1)
243
- expect(actions).to eq([name])
244
- end
245
- end
246
- end
247
-
248
- describe 'footprint' do
249
- it 'adds exactly the mobile API, with no private methods and nothing named wait' do
250
- expect(App::WaitHelper.public_instance_methods(false).sort)
251
- .to eq(%i[attribute clear click displayed? enabled? eventually find_element find_elements scroll scroll_to
252
- selected? send_keys swipe tap text])
253
- expect(App::WaitHelper.private_instance_methods(false)).to be_empty
254
- expect(page.respond_to?(:wait, true)).to be(false)
255
- end
256
- end
257
-
258
- describe '#eventually' do
259
- it 'retries the block as on web' do
260
- tries = 0
261
- expect(page.eventually(timeout: 1) { expect(tries += 1).to eq(2) }).to be(true)
262
- end
263
-
264
- it 'retries a nested timeout: until its own deadline, as on web' do
265
- started = clock
266
- expect { page.eventually(timeout: 0.6) { page.text({ accessibility_id: 'never' }, timeout: 0.2) } }
267
- .to raise_error(App::WaitHelper::TimeoutError, /\A\(still failing after 0.6s\)/)
268
- expect(clock - started).to be_between(0.5, 1.0)
269
- end
270
-
271
- it 'raises at once for a stale find_elements member used as a parent' do
272
- row = element.script({ accessibility_id: 'cell' }, element)
273
- driver.script_many({ accessibility_id: 'row' }, [row])
274
- member = page.find_elements(accessibility_id: 'row').first
275
- row.go_stale
276
- expect { member.find_element(accessibility_id: 'cell', timeout: 2) }
277
- .to raise_error(Selenium::WebDriver::Error::StaleElementReferenceError, /find_elements again/)
278
- end
279
- end
280
- end
281
- end