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,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'yaml'
3
4
  require_relative 'content_helper'
4
5
 
5
6
  describe 'Page object content' do
@@ -36,16 +37,15 @@ describe 'Page object content' do
36
37
  shared_examples 'selenium login page' do |project_name|
37
38
  subject(:login) { read_generated(project_name, 'page_objects/pages/login.rb') }
38
39
 
39
- it 'returns locator hashes from the element methods' do
40
- expect(login).to include("{ id: 'loginFrm_loginname' }", "{ id: 'loginFrm_password' }",
41
- %q({ xpath: "//button[@title='Login']" }))
42
- expect(login).not_to include('driver.find_element')
40
+ it 'returns Selenium elements from the element methods' do
41
+ expect(login).to include("driver.find_element(id: 'loginFrm_loginname')", "driver.find_element(id: 'loginFrm_password')",
42
+ "driver.find_element(by_role('button', name: 'Login'))")
43
+ expect(login).not_to include('browser.')
43
44
  end
44
45
 
45
- it 'logs in through the wait helper' do
46
- expect(login).to include('send_keys(username_field, username)', 'send_keys(password_field, password)',
47
- 'click(login_button)')
48
- expect(login).not_to include('.send_keys', '.click')
46
+ it 'logs in by acting on the elements' do
47
+ expect(login).to include('username_field.send_keys(username)', 'password_field.send_keys(password)', 'login_button.click')
48
+ expect(login).to include('alias log_as login')
49
49
  end
50
50
 
51
51
  it 'has private element methods' do
@@ -59,28 +59,6 @@ describe 'Page object content' do
59
59
  expect(login).not_to include('Capybara::DSL')
60
60
  expect(login).not_to include('fill_in')
61
61
  end
62
-
63
- it 'does not include Watir methods' do
64
- expect(login).not_to include('browser.text_field')
65
- end
66
- end
67
-
68
- shared_examples 'watir login page' do |project_name|
69
- subject(:login) { read_generated(project_name, 'page_objects/pages/login.rb') }
70
-
71
- it 'uses browser methods for elements' do
72
- expect(login).to include('browser.text_field')
73
- expect(login).to include('browser.button')
74
- end
75
-
76
- it 'uses set for input' do
77
- expect(login).to include('.set ')
78
- end
79
-
80
- it 'does not include Selenium methods' do
81
- expect(login).not_to include('find_element')
82
- expect(login).not_to include('send_keys')
83
- end
84
62
  end
85
63
 
86
64
  # --- Abstract page ---
@@ -99,6 +77,39 @@ describe 'Page object content' do
99
77
  it 'defines to_s method' do
100
78
  expect(page).to match(/def to_s/)
101
79
  end
80
+
81
+ describe 'full_url' do
82
+ # The generated full_url and base_url, loaded without the helpers the abstract page requires.
83
+ let(:page_class) do
84
+ methods = page[/^ def full_url.*?^ end\n\n(?: #.*\n)* def base_url.*?^ end\n/m]
85
+ expect(methods).not_to be_nil
86
+ Class.new { def url(_page) = 'index.php?rt=account/login' }.tap { |klass| klass.class_eval(methods) }
87
+ end
88
+
89
+ def full_url_with(base_url, path = nil)
90
+ allow(YAML).to receive(:load_file).with('config/config.yml').and_return('url' => base_url)
91
+ klass = page_class
92
+ klass.define_method(:url) { |_page| path } if path
93
+ klass.new.full_url(nil)
94
+ end
95
+
96
+ it 'adds the missing slash between a base url with a path and the page path' do
97
+ expect(full_url_with('https://example.org/login')).to eq('https://example.org/login/index.php?rt=account/login')
98
+ end
99
+
100
+ it 'keeps a single slash when the base url already ends with one' do
101
+ expect(full_url_with('https://example.org/login/')).to eq('https://example.org/login/index.php?rt=account/login')
102
+ end
103
+
104
+ it 'does not double the slash when the page path starts with one' do
105
+ expect(full_url_with('https://example.org', '/index.php')).to eq('https://example.org/index.php')
106
+ end
107
+
108
+ it 'returns the page path alone when the config has no url' do
109
+ expect(full_url_with(nil, 'https://other.example/index.php')).to eq('https://other.example/index.php')
110
+ expect(full_url_with(nil, '/index.php')).to eq('/index.php')
111
+ end
112
+ end
102
113
  end
103
114
 
104
115
  shared_examples 'selenium abstract page' do |project_name|
@@ -121,22 +132,6 @@ describe 'Page object content' do
121
132
  end
122
133
  end
123
134
 
124
- shared_examples 'watir abstract page' do |project_name|
125
- subject(:page) { read_generated(project_name, 'page_objects/abstract/page.rb') }
126
-
127
- it 'has attr_reader :browser' do
128
- expect(page).to include('attr_reader :browser')
129
- end
130
-
131
- it 'has initialize accepting browser' do
132
- expect(page).to match(/def initialize\(browser\)/)
133
- end
134
-
135
- it 'has visit with browser.goto' do
136
- expect(page).to include('browser.goto')
137
- end
138
- end
139
-
140
135
  # --- Contexts ---
141
136
 
142
137
  context 'with rspec and selenium' do
@@ -146,49 +141,29 @@ describe 'Page object content' do
146
141
  include_examples 'valid abstract page', name
147
142
  include_examples 'selenium abstract page', name
148
143
  end
149
-
150
- context 'with rspec and watir' do
151
- name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::WATIR}"
152
- include_examples 'valid login page', name
153
- include_examples 'watir login page', name
154
- include_examples 'valid abstract page', name
155
- include_examples 'watir abstract page', name
156
- end
157
-
158
- context 'with cucumber and selenium' do
159
- name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
160
- include_examples 'valid login page', name
161
- include_examples 'selenium login page', name
162
- include_examples 'valid abstract page', name
163
- include_examples 'selenium abstract page', name
164
- end
165
-
166
- context 'with cucumber and watir' do
167
- name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::WATIR}"
168
- include_examples 'valid login page', name
169
- include_examples 'watir login page', name
170
- include_examples 'valid abstract page', name
171
- include_examples 'watir abstract page', name
172
- end
173
144
  end
174
145
 
175
- describe 'Page WaitHelper wiring' do
176
- %w[rspec cucumber].each do |framework|
177
- %w[selenium ios android cross_platform].each do |automation|
178
- it "includes WaitHelper in #{framework} #{automation}" do
146
+ describe 'Page helper wiring' do
147
+ FRAMEWORKS.each do |framework|
148
+ it "includes NetworkHelper only in #{framework} selenium" do
149
+ %w[selenium ios android cross_platform].each do |automation|
179
150
  page = read_generated("#{framework}_#{automation}", 'page_objects/abstract/page.rb')
180
- expect(page).to include("require_relative '../../helpers/wait_helper'", 'include WaitHelper')
151
+ expect(page.include?('include NetworkHelper')).to eq(automation == 'selenium')
181
152
  end
182
153
  end
183
154
 
184
- it "does not include WaitHelper in #{framework} watir" do
185
- expect(read_generated("#{framework}_watir", 'page_objects/abstract/page.rb')).not_to include('WaitHelper')
155
+ it "includes ScreenshotHelper only in #{framework} selenium" do
156
+ %w[selenium ios android cross_platform].each do |automation|
157
+ page = read_generated("#{framework}_#{automation}", 'page_objects/abstract/page.rb')
158
+ expect(page.include?('include ScreenshotHelper')).to eq(automation == 'selenium')
159
+ expect(page.include?("require_relative '../../helpers/screenshot_helper'")).to eq(automation == 'selenium')
160
+ end
186
161
  end
187
162
  end
188
163
  end
189
164
 
190
165
  describe 'Abstract page layout' do
191
- %w[rspec cucumber].each do |framework|
166
+ FRAMEWORKS.each do |framework|
192
167
  %w[selenium ios android cross_platform].each do |automation|
193
168
  [true, false].each do |examples|
194
169
  it "has no blank line before the class end in #{framework} #{automation}#{' (skeleton)' unless examples}" do
@@ -200,12 +175,12 @@ describe 'Abstract page layout' do
200
175
  end
201
176
  end
202
177
 
203
- describe 'Example pages on the wait helper' do
204
- %w[rspec cucumber].each do |framework|
205
- it "finds the header through the wait helper in #{framework} selenium" do
178
+ describe 'Example pages' do
179
+ FRAMEWORKS.each do |framework|
180
+ it "defines welcome_message on the abstract page in #{framework} selenium" do
206
181
  page = read_generated("#{framework}_selenium", 'page_objects/abstract/page.rb')
207
- expect(page).to include("Header.new(find_element(css: '.welcome-msg'))")
208
- expect(page).not_to include('driver.find_element')
182
+ expect(page).to include("def welcome_message\n driver.find_element(css: '.welcome-msg')")
183
+ expect(page).not_to include('Header', 'components/header')
209
184
  end
210
185
 
211
186
  %w[ios android cross_platform].each do |automation|
@@ -213,10 +188,12 @@ describe 'Example pages on the wait helper' do
213
188
  let(:home) { read_generated("#{framework}_#{automation}", 'page_objects/pages/home.rb') }
214
189
  let(:pdp) { read_generated("#{framework}_#{automation}", 'page_objects/pages/pdp.rb') }
215
190
 
216
- it 'taps the first product and reads the add to cart text through the wait helper' do
217
- expect(home).to include('tap(first_product)')
218
- expect(pdp).to include('text(add_to_cart_button)')
219
- expect(home + pdp).not_to include('driver.wait', 'driver.find_element')
191
+ it 'clicks the first product and exposes the add to cart button as plain Appium' do
192
+ finder = automation == 'cross_platform' ? 'element(' : 'driver.find_element('
193
+ expect(home).to include('first_product.click', " private\n", "def first_product\n #{finder}")
194
+ expect(pdp).to include('def add_to_cart_button')
195
+ expect(pdp).not_to include('add_to_cart_text', 'private')
196
+ expect(home + pdp).not_to include('tap(', 'text(', 'driver.wait')
220
197
  end
221
198
 
222
199
  it 'returns locator hashes from the element methods' do
@@ -238,3 +215,24 @@ describe 'Example pages on the wait helper' do
238
215
  end
239
216
  end
240
217
  end
218
+
219
+ describe 'Page runtime wiring' do
220
+ it 'requires the appium helper that the cross platform page includes' do
221
+ page = read_generated("#{FrameworkIndex::RSPEC}_cross_platform", 'page_objects/abstract/page.rb')
222
+ expect(page).to include("require_relative '../../helpers/appium_helper'", 'include AppiumHelper')
223
+ end
224
+
225
+ it 'leaves the appium helper out of the other pages' do
226
+ %w[selenium ios android].each do |automation|
227
+ page = read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'page_objects/abstract/page.rb')
228
+ expect(page).not_to include('appium_helper')
229
+ end
230
+ end
231
+
232
+ AUTOMATION_TYPES.each do |automation|
233
+ it "requires the runtime and includes Raider::Helpers in #{automation}" do
234
+ page = read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'page_objects/abstract/page.rb')
235
+ expect(page).to include("require 'ruby_raider/runtime'", 'include Raider::Helpers')
236
+ end
237
+ end
238
+ end
@@ -8,7 +8,7 @@ describe 'Generated Readme content' do
8
8
  subject(:section) { readme[/^### Video replay\n.*?(?=^##)/m]&.gsub(/\s+/, ' ') }
9
9
 
10
10
  let(:readme) { read_generated("#{framework}_#{automation}", 'Readme.md') }
11
- let(:web) { %w[selenium watir].include?(automation) }
11
+ let(:web) { automation == 'selenium' }
12
12
 
13
13
  it 'exists and covers the defaults, VIDEO=off, the install commands and the cache' do
14
14
  expect(section).not_to be_nil
@@ -86,59 +86,84 @@ describe 'Generated Readme content' do
86
86
  expect(section).to include('Failure messages can contain typed text too, because an assertion message is kept as it is')
87
87
  end
88
88
 
89
- it 'says how to remove the trace helper and notes the wait helper link' do
90
- expect(readme).to include('Removing the trace helper', 'require_relative',
91
- 'without WaitHelper a trace records only navigation and the failure')
89
+ it 'says how to remove the trace helper' do
90
+ expect(readme).to include('Removing the trace helper', 'require_relative')
92
91
  expect(readme).not_to match(/allure/i)
93
92
  expect(readme).not_to include('—')
94
93
  end
94
+
95
+ it 'says which element actions the trace records' do
96
+ expect(section).to include('records `click`, `send_keys`, `clear` and `submit` with their locators',
97
+ '`by_role` as `button, name: Save`', 'not steps')
98
+ end
95
99
  else
96
100
  it 'does not mention trace replay' do
97
101
  expect(readme).not_to include('Trace replay', 'TRACE=', 'trace_helper')
98
102
  end
99
103
  end
100
104
  end
101
- end
102
105
 
103
- AUTOMATION_TYPES.each do |automation|
104
- context "with rspec and #{automation}" do
105
- subject(:readme) { read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'Readme.md') }
106
+ context "with #{framework} and #{automation}, the network mocking section" do
107
+ subject(:readme) { read_generated("#{framework}_#{automation}", 'Readme.md') }
106
108
 
107
- it 'has a Reporting section' do
108
- expect(readme).to include('## Reporting')
109
- end
109
+ if automation == 'selenium'
110
+ it 'documents the API, the examples, BIDI=off and how to remove the helper' do
111
+ expect(readme).to include('## Mocking network requests', "mock_request('**/api/users', json: { users: [] })",
112
+ 'BIDI=off', '### Removing the network helper', 'status: 500',
113
+ "block_request('**/*.png')", 'NetworkHelper::MockError')
114
+ expect(readme).to include('expect { users.called? }.to be(true)',
115
+ "expect { users.calls.first&.method }.to eq('GET')")
116
+ end
110
117
 
111
- it 'documents local results, upload settings and the raider_reporter README' do
112
- expect(readme).to include('raider-results/', 'RAIDER_TOKEN', 'RAIDER_ENDPOINT',
113
- 'https://github.com/RaiderHQ/raider_reporter')
114
- end
118
+ it 'names the files to edit for this framework' do
119
+ section = readme[/^### Removing the network helper\n.*?(?=^##)/m]
120
+ expect(section).to include('helpers/network_helper.rb', 'NetworkHelper.finish', 'raise mock_error')
121
+ expect(section).to include(framework == 'rspec' ? 'config.include(NetworkHelper)' : 'World(NetworkHelper)')
122
+ end
115
123
 
116
- it 'explains quarantine in CI' do
117
- expect(readme).to include('### Quarantine', 'RAIDER_QUARANTINE', 'raider-reporter exec')
124
+ it 'says that TRACE=off keeps BiDi on' do
125
+ expect(readme).to include('BiDi stays on')
126
+ end
127
+ else
128
+ it 'does not mention network mocking' do
129
+ expect(readme).not_to include('Mocking network requests', 'NetworkHelper')
130
+ end
118
131
  end
132
+ end
119
133
 
120
- it 'explains the quality gate' do
121
- expect(readme).to include('### Quality gate', 'RAIDER_GATE=1', 'Gate page', 'raider/gate',
122
- 'raider-reporter gate', 'raider-gate', 'exits 1 when the gate fails',
123
- '2 when it gets no verdict', 'RAIDER_GATE_TIMEOUT', 'RAIDER_SHARD_TOTAL',
124
- 'RAIDER_SHARD_INDEX')
125
- end
134
+ context "with #{framework} and #{automation}, the screenshot comparison section" do
135
+ subject(:readme) { read_generated("#{framework}_#{automation}", 'Readme.md') }
126
136
 
127
- it 'points to the local HTML report' do
128
- expect(readme).to include('raider-results/index.html')
129
- expect(readme).not_to match(/allure/i)
137
+ if automation == 'selenium'
138
+ it 'documents the call, SCREENSHOTS=update, tolerances, CI baselines and removal' do
139
+ expect(readme).to include('## Screenshot comparison', "match_screenshot('home')", 'SCREENSHOTS=update',
140
+ 'screenshots/baselines/', 'max_diff_pixels', 'Retina', 'Generate baselines where CI runs',
141
+ '### Removing screenshot comparison')
142
+ end
143
+
144
+ it 'names the files to edit for this framework' do
145
+ section = readme[/^### Removing screenshot comparison\n.*?(?=^##)/m]
146
+ expect(section).to include('helpers/screenshot_helper.rb', 'ScreenshotHelper.reset', "gem 'chunky_png'")
147
+ expect(section).to include(framework == 'rspec' ? 'config.include(ScreenshotHelper)' : 'World(ScreenshotHelper)')
148
+ end
149
+ else
150
+ it 'does not mention screenshot comparison' do
151
+ expect(readme).not_to include('Screenshot comparison', 'ScreenshotHelper', 'match_screenshot')
152
+ end
130
153
  end
131
154
  end
155
+ end
132
156
 
133
- context "with cucumber and #{automation}" do
134
- subject(:readme) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Readme.md') }
157
+ AUTOMATION_TYPES.each do |automation|
158
+ context "with rspec and #{automation}" do
159
+ subject(:readme) { read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'Readme.md') }
135
160
 
136
161
  it 'has a Reporting section' do
137
162
  expect(readme).to include('## Reporting')
138
163
  end
139
164
 
140
- it 'documents local results, upload settings, plugin mode and the raider_reporter README' do
141
- expect(readme).to include('raider-results/', 'RAIDER_TOKEN', 'RAIDER_ENDPOINT', 'features/support/env.rb',
165
+ it 'documents local results, upload settings and the raider_reporter README' do
166
+ expect(readme).to include('raider-results/', 'RAIDER_TOKEN', 'RAIDER_ENDPOINT',
142
167
  'https://github.com/RaiderHQ/raider_reporter')
143
168
  end
144
169
 
@@ -153,7 +178,8 @@ describe 'Generated Readme content' do
153
178
  'RAIDER_SHARD_INDEX')
154
179
  end
155
180
 
156
- it 'does not mention Allure' do
181
+ it 'points to the local HTML report' do
182
+ expect(readme).to include('raider-results/index.html')
157
183
  expect(readme).not_to match(/allure/i)
158
184
  end
159
185
  end
@@ -163,11 +189,11 @@ describe 'Generated Readme content' do
163
189
  context "with #{framework} and #{automation}, the login reuse section" do
164
190
  subject(:readme) { read_generated("#{framework}_#{automation}", 'Readme.md') }
165
191
 
166
- if %w[selenium watir].include?(automation)
192
+ if automation == 'selenium'
167
193
  it 'documents the tags, other roles, max_age, the env switch and the secrets warning' do
168
194
  section = readme[/^## Reusing the login\n.*?(?=^##)/m].gsub(/\s+/, ' ')
169
- expect(section).to include('`:logged_in`', '`@logged_in`', "SessionState.new(driver, name: 'admin')",
170
- 'browser.driver', 'login_state', 'max_age', 'RAIDER_LOGIN_STATE=off',
195
+ expect(section).to include('`:logged_in`', "SessionState.new(driver, name: 'admin')",
196
+ 'login_state', 'max_age', 'RAIDER_LOGIN_STATE=off',
171
197
  'RAIDER_LOGIN_STATE=refresh', '.raider/auth/', 'git-ignored',
172
198
  'live session tokens', 'must not be committed, cached or uploaded')
173
199
  end
@@ -204,36 +230,55 @@ describe 'Generated Readme: How the framework works' do
204
230
  expect(readme.index("\n## Reusing the login\n")).to be > works
205
231
  end
206
232
 
207
- it 'covers the web wait helper, chaining, .native, absence checks and how to remove it' do
233
+ it 'covers the folders, the settings, running the tests and the helpers without a wait helper' do
208
234
  section = section_of(readme)
209
- expect(section).to include('### Removing the wait helper', '.native',
210
- "find_element(css: '.form').find_element(id: 'email')",
211
- 'select_by', 'hover', 'drag_and_drop', 'eventually', 'timeout: 0',
212
- 'find_elements', 'config/config.yml', '[Reporting](#reporting)',
213
- '[Reusing the login](#reusing-the-login)', 'parallel_tests', 'HEADLESS',
235
+ expect(section).to include('config/config.yml', '[Reporting](#reporting)', '[Reusing the login](#reusing-the-login)',
236
+ 'parallel_tests', 'HEADLESS', 'requires the Raider runtime',
214
237
  framework == 'rspec' ? 'RETRY_COUNT' : '--retry 2')
215
- expect(section).not_to include('scroll_to', 'swipe')
238
+ expect(section).to include('`timeout`', '`strict`', '`actionability`')
239
+ expect(section).not_to include('scroll_to', 'swipe', 'gesture_helper')
240
+ end
241
+
242
+ it 'has no wait helper, RaiderElement, eventually or implicit wait advice' do
243
+ expect(readme).not_to match(/WaitHelper|RaiderElement|wait_helper|raider_element|eventually|wait helper/i)
244
+ expect(readme).not_to include('Removing the wait helper', 'implicit_wait = ')
245
+ end
246
+
247
+ it 'documents the locators, with by_role, the exact form, strictness and find_elements' do
248
+ locators = section_of(readme)[/^### Locators\n.*?(?=^### )/m].gsub(/\s+/, ' ')
249
+ expect(locators).to include('id:', 'css:', 'xpath:', 'name:', 'class:', 'link_text:', 'label:', 'placeholder:',
250
+ 'alt:', 'title:', 'text:', 'test_id:', 'data-testid', 'RAIDER_TEST_ID_ATTRIBUTE',
251
+ "by_role('button', name: 'Login')", 'exact:', 'include_hidden:', 'level:',
252
+ "label: ['Email', { exact: true }]", 'matches more than one element',
253
+ 'AmbiguousMatch', 'find_elements')
216
254
  end
217
255
 
218
- it 'explains that the waits inside eventually stretch to its time and are retried' do
219
- eventually = section_of(readme)[/^### eventually\n.*?(?=^### )/m]
220
- expect(eventually).to include('a wait inside it that timed out', 'whatever is left of the block',
221
- '`timeout: 2` gets 2 seconds per try', '(still failing after 10s)')
256
+ it 'documents the waiting finds, the per-action checks, force and the settings' do
257
+ waiting = section_of(readme)[/^### Waiting and actionability\n.*?(?=^### )/m].gsub(/\s+/, ' ')
258
+ expect(waiting).to include('`find_element` and `find_elements` wait for a match', '`find_elements` returns `[]`',
259
+ '| `click` | visible, enabled, not inert, stable, receives events |',
260
+ '| `send_keys` | visible, enabled, editable, not inert |',
261
+ '| `clear` | visible, enabled, editable, not inert |',
262
+ '| `submit` | visible, enabled, not inert |',
263
+ 'force: true', 'Raider.actionability = false', 'RAIDER_STRICT', 'RAIDER_ACTIONABILITY',
264
+ 'RAIDER_TIMEOUT', 'RAIDER_QUIET', 'raider u strict', 'raider u actionability',
265
+ 'no such element: { id: "x" } (waited 10s)', 'NotActionable', 'select_by',
266
+ 'implicit wait')
267
+ expect(waiting).not_to include('driver.manage.timeouts.implicit_wait')
222
268
  end
223
269
 
224
- it 'replaces eventually with a true/false Wait block and points at the generators that check' do
225
- removal = section_of(readme)[/^### Removing the wait helper\n.*/m]
226
- expect(removal).to include('a plain `expect`, or `Selenium::WebDriver::Wait.new(timeout: 10).until { condition }`',
227
- 'a block that returns true or false', '`raider g page` and `raider g component`')
228
- expect(removal).not_to include('raider g spec')
270
+ it 'documents the matchers, wait:, the block form and its limits' do
271
+ assertions = section_of(readme)[/^### Assertions\n.*?(?=^### )/m].gsub(/\s+/, ' ')
272
+ expect(assertions).to include('have_text', 'have_value', 'have_attribute', 'be_visible', 'be_hidden', 'be_enabled',
273
+ 'be_disabled', 'be_checked', 'have_count', 'have_title', 'have_url', 'have_element',
274
+ 'have_css', 'have_xpath', 'wait: 5', 'expect { account_page.welcome_message }',
275
+ 'expect { value }.to eq(', 'raise_error', 'does not retry',
276
+ 'once per poll', 'aggregate_failures')
229
277
  end
230
278
 
231
- it 'lists the five steps to remove the wait helper' do
232
- removal = section_of(readme)[/^### Removing the wait helper\n.*/m]
233
- expect(removal).to include('helpers/wait_helper.rb', 'helpers/raider_element.rb', 'include WaitHelper',
234
- 'driver.find_element(**', 'Selenium::WebDriver::Wait.new', 'implicit_wait',
235
- 'raider g')
236
- expect(removal.scan(/^\d\. /).size).to eq(5)
279
+ it 'lists the web helpers and the page base class without a wait helper' do
280
+ section = section_of(readme)
281
+ expect(section).to include('`driver_helper.rb` starts the browser', '`video_helper.rb`', '`session_state.rb`')
237
282
  end
238
283
  end
239
284
 
@@ -248,15 +293,40 @@ describe 'Generated Readme: How the framework works' do
248
293
  expect(readme.index("\n## Reporting\n")).to be > works
249
294
  end
250
295
 
251
- it 'covers the mobile method set, per-OS locators, Appium setup and how to remove the wait helper' do
296
+ it 'covers the folders, per-OS locators, Appium setup and the gesture helper' do
252
297
  section = section_of(readme)
253
- expect(section).to include('swipe', 'scroll_to', 'tap', 'capabilities.yml', 'raider u start_appium',
254
- '### Removing the wait helper', '.native', '[Reporting](#reporting)',
255
- 'timeout: 15', 'http://127.0.0.1:4723')
256
- expect(section).not_to include('select_by', 'hover', 'drag_and_drop', 'Reusing the login', '/wd/hub')
298
+ expect(section).to include('capabilities.yml', 'raider u start_appium', '[Reporting](#reporting)',
299
+ 'timeout: 15', 'http://127.0.0.1:4723', '`gesture_helper.rb`',
300
+ '`swipe`', '`scroll_to`', 'requires the Raider runtime')
301
+ expect(section).not_to include('select_by', 'hover', 'drag_and_drop', 'Reusing the login', '/wd/hub',
302
+ 'by_role', 'wait_helper', 'raider_element')
257
303
  expect(section).to include('ios: {') if automation == 'cross_platform'
258
304
  end
259
305
 
306
+ it 'has no wait helper, RaiderElement or eventually' do
307
+ expect(readme).not_to match(/WaitHelper|RaiderElement|wait_helper|raider_element|eventually|wait helper/i)
308
+ end
309
+
310
+ it 'documents the Appium locators and the waiting finds and actions' do
311
+ section = section_of(readme)
312
+ locators = section[/^### Locators\n.*?(?=^### )/m].gsub(/\s+/, ' ')
313
+ expect(locators).to include('accessibility_id:', 'xpath:', 'id:', 'class_name:', 'find_elements')
314
+ expect(section).to include('element(ios: { … }, android: { … })') if automation == 'cross_platform'
315
+ waiting = section[/^### Waiting\n.*?(?=^### )/m].gsub(/\s+/, ' ')
316
+ expect(waiting).to include('`find_element` and `find_elements` wait for a match',
317
+ '`click`, `send_keys` and `clear` wait until the element is displayed and enabled',
318
+ '`submit` is not checked', 'strict is off', 'force: true', 'Raider.actionability = false',
319
+ 'NotActionable', 'XCUITest', 'RAIDER_TIMEOUT', 'RAIDER_STRICT', 'RAIDER_ACTIONABILITY')
320
+ expect(section[/^### Settings\n.*?(?=^### )/m]).to include('no `config/config.yml`') unless automation == 'cross_platform'
321
+ end
322
+
323
+ it 'documents the gestures and the assertions' do
324
+ section = section_of(readme)
325
+ gestures = section[/^### Gestures\n.*?(?=^### )/m].gsub(/\s+/, ' ')
326
+ expect(gestures).to include("swipe('left', element)", "scroll('down')", 'scroll_to({ accessibility_id: ')
327
+ expect(section[/^### Assertions\n.*?(?=^### )/m]).to include('have_text', 'expect { pdp_page.add_to_cart_button }')
328
+ end
329
+
260
330
  it 'says the device capabilities sit under appium:options' do
261
331
  settings = section_of(readme)[/^### Settings\n.*?(?=^### )/m]
262
332
  expect(settings).to include('under `appium:options`')
@@ -264,26 +334,15 @@ describe 'Generated Readme: How the framework works' do
264
334
  end
265
335
  end
266
336
  end
267
-
268
- it "marks the #{framework} watir Readme as deprecated and not the selenium one" do
269
- watir = read_generated("#{framework}_watir", 'Readme.md')
270
- expect(watir).to include('> **Deprecated:** Watir is deprecated', 'only available through `-p automation:watir`',
271
- 'https://github.com/RaiderHQ/ruby_raider#migrating-from-watir')
272
- expect(read_generated("#{framework}_selenium", 'Readme.md')).not_to include('**Deprecated:**')
273
- end
274
-
275
- it "has no such section for #{framework} and watir" do
276
- expect(read_generated("#{framework}_watir", 'Readme.md')).not_to include('How the framework works')
277
- end
278
337
  end
279
338
 
280
339
  describe 'without example tests' do
281
- %w[rspec cucumber].product(%w[selenium ios cross_platform]).each do |framework, automation|
340
+ FRAMEWORKS.product(%w[selenium ios cross_platform]).each do |framework, automation|
282
341
  context "with #{framework} and #{automation}" do
283
342
  subject(:readme) { read_generated(ensure_project(framework:, automation:, examples: false), 'Readme.md') }
284
343
 
285
344
  it 'still explains how the framework works' do
286
- expect(readme).to include('## How the framework works', '### The wait helper and RaiderElement')
345
+ expect(readme).to include('## How the framework works', '### Locators', '### Assertions')
287
346
  end
288
347
 
289
348
  it 'points at no example file' do
@@ -298,7 +357,7 @@ describe 'Generated Readme: How the framework works' do
298
357
  end
299
358
  end
300
359
 
301
- %w[rspec cucumber].each do |framework|
360
+ FRAMEWORKS.each do |framework|
302
361
  it "shows how to add the login hook for the project's own login page in #{framework}" do
303
362
  readme = read_generated(ensure_project(framework:, automation: 'selenium', examples: false), 'Readme.md')
304
363
  section = readme[/^## Reusing the login\n.*?(?=^## )/m]
@@ -18,8 +18,8 @@ describe 'RSpec projects without Allure' do
18
18
  expect(File).not_to exist(File.join(project_name, 'allure-results'))
19
19
  end
20
20
 
21
- it 'pins raider_reporter 0.12' do
22
- expect(read_generated(project_name, 'Gemfile')).to include("gem 'raider_reporter', '~> 0.12'")
21
+ it 'pins raider_reporter 1.0' do
22
+ expect(read_generated(project_name, 'Gemfile')).to include("gem 'raider_reporter', '~> 1.0'")
23
23
  end
24
24
  end
25
25
  end
@@ -6,7 +6,7 @@ require 'stringio'
6
6
  require 'tmpdir'
7
7
  require_relative 'content_helper'
8
8
 
9
- RUBOCOP_WEB_AUTOMATIONS = %w[selenium watir].freeze
9
+ RUBOCOP_WEB_AUTOMATIONS = %w[selenium].freeze
10
10
 
11
11
  # Every generated project must pass the RuboCop it ships: its own .rubocop.yml, run in-process with
12
12
  # this gem's bundle. The project is linted from a copy outside this repo, so RuboCop resolves the
@@ -38,8 +38,8 @@ describe 'Ruby syntax validation' do
38
38
  end
39
39
 
40
40
  SYNTAX_MOBILE_AUTOMATIONS.each do |automation|
41
- it "#{framework}_#{automation} wait_helper has the mobile gestures and no web-only methods" do
42
- content = File.read(File.join(ensure_project(framework:, automation:), 'helpers/wait_helper.rb'))
41
+ it "#{framework}_#{automation} gesture_helper has the mobile gestures and no web-only methods" do
42
+ content = File.read(File.join(ensure_project(framework:, automation:), 'helpers/gesture_helper.rb'))
43
43
  expect(content).to include("'mobile: swipeGesture'").and include('def scroll_to(')
44
44
  expect(content).not_to include('def drag_and_drop(')
45
45
  end