ruby_raider 3.0.0 → 3.1.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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yml +0 -17
  3. data/.github/workflows/system_tests.yml +0 -22
  4. data/README.md +8 -62
  5. data/lib/commands/scaffolding_commands.rb +22 -188
  6. data/lib/commands/utility_commands.rb +10 -44
  7. data/lib/generators/automation/templates/login.tt +1 -9
  8. data/lib/generators/automation/templates/page.tt +1 -7
  9. data/lib/generators/automation/templates/partials/initialize_selector.tt +1 -3
  10. data/lib/generators/automation/templates/partials/visit_method.tt +2 -6
  11. data/lib/generators/common_generator.rb +0 -6
  12. data/lib/generators/cucumber/cucumber_generator.rb +0 -24
  13. data/lib/generators/cucumber/templates/accessibility_steps.tt +2 -6
  14. data/lib/generators/cucumber/templates/cucumber.tt +0 -7
  15. data/lib/generators/cucumber/templates/env.tt +1 -3
  16. data/lib/generators/cucumber/templates/partials/appium_env.tt +1 -6
  17. data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -5
  18. data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -5
  19. data/lib/generators/cucumber/templates/partials/web_steps.tt +4 -4
  20. data/lib/generators/cucumber/templates/world.tt +1 -3
  21. data/lib/generators/generator.rb +2 -46
  22. data/lib/generators/helper_generator.rb +4 -42
  23. data/lib/generators/infrastructure/templates/github.tt +2 -2
  24. data/lib/generators/infrastructure/templates/github_appium.tt +2 -6
  25. data/lib/generators/invoke_generators.rb +4 -25
  26. data/lib/generators/menu_generator.rb +10 -100
  27. data/lib/generators/rspec/rspec_generator.rb +0 -11
  28. data/lib/generators/rspec/templates/accessibility_spec.tt +2 -6
  29. data/lib/generators/rspec/templates/spec.tt +6 -8
  30. data/lib/generators/templates/common/gemfile.tt +0 -26
  31. data/lib/generators/templates/common/git_ignore.tt +0 -2
  32. data/lib/generators/templates/common/partials/mobile_config.tt +0 -4
  33. data/lib/generators/templates/common/partials/web_config.tt +0 -4
  34. data/lib/generators/templates/common/rakefile.tt +0 -9
  35. data/lib/generators/templates/common/read_me.tt +4 -10
  36. data/lib/generators/templates/helpers/allure_helper.tt +0 -10
  37. data/lib/generators/templates/helpers/browser_helper.tt +1 -1
  38. data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +1 -3
  39. data/lib/generators/templates/helpers/partials/debug_start.tt +1 -3
  40. data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
  41. data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
  42. data/lib/generators/templates/helpers/partials/selenium_driver.tt +1 -1
  43. data/lib/generators/templates/helpers/spec_helper.tt +2 -39
  44. data/lib/ruby_raider.rb +2 -50
  45. data/lib/scaffolding/project_detector.rb +30 -13
  46. data/lib/scaffolding/scaffolding.rb +0 -109
  47. data/lib/scaffolding/templates/component.tt +1 -4
  48. data/lib/scaffolding/templates/page_object.tt +0 -10
  49. data/lib/scaffolding/templates/spec.tt +2 -6
  50. data/lib/scaffolding/templates/steps.tt +0 -2
  51. data/lib/utilities/utilities.rb +26 -24
  52. data/lib/version +1 -1
  53. data/sig/commands/scaffolding_commands.rbs +0 -12
  54. data/sig/commands/utility_commands.rbs +0 -6
  55. data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
  56. data/sig/generators/generator.rbs +0 -11
  57. data/sig/generators/helper_generator.rbs +1 -5
  58. data/sig/generators/invoke_generators.rbs +1 -2
  59. data/sig/generators/menu_generator.rbs +2 -6
  60. data/sig/generators/rspec/rspec_generator.rbs +0 -2
  61. data/sig/ruby_raider.rbs +1 -3
  62. data/sig/scaffolding/project_detector.rbs +0 -1
  63. data/sig/scaffolding/scaffolding.rbs +0 -23
  64. data/sig/utilities/utilities.rbs +0 -4
  65. data/spec/commands/raider_commands_spec.rb +0 -61
  66. data/spec/commands/scaffolding_commands_spec.rb +22 -0
  67. data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
  68. data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
  69. data/spec/integration/commands/utility_commands_spec.rb +9 -9
  70. data/spec/integration/content/ci_content_spec.rb +6 -61
  71. data/spec/integration/content/common_content_spec.rb +0 -64
  72. data/spec/integration/content/config_content_spec.rb +1 -51
  73. data/spec/integration/content/content_helper.rb +2 -2
  74. data/spec/integration/content/gemfile_content_spec.rb +0 -71
  75. data/spec/integration/content/helper_content_spec.rb +4 -240
  76. data/spec/integration/content/page_content_spec.rb +0 -89
  77. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  78. data/spec/integration/content/test_content_spec.rb +0 -119
  79. data/spec/integration/end_to_end_features_spec.rb +13 -411
  80. data/spec/integration/end_to_end_spec.rb +0 -96
  81. data/spec/integration/generators/axe_addon_spec.rb +2 -52
  82. data/spec/integration/generators/common_generator_spec.rb +1 -13
  83. data/spec/integration/generators/config_features_spec.rb +3 -81
  84. data/spec/integration/generators/debug_helper_spec.rb +0 -20
  85. data/spec/integration/generators/github_generator_spec.rb +5 -15
  86. data/spec/integration/generators/helpers_generator_spec.rb +0 -37
  87. data/spec/integration/scaffolding_e2e_spec.rb +540 -0
  88. data/spec/integration/settings_helper.rb +1 -3
  89. data/spec/integration/spec_helper.rb +6 -11
  90. data/spec/menus/menu_generator_spec.rb +4 -107
  91. data/spec/scaffolding/scaffold_project_detector_spec.rb +42 -26
  92. data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
  93. data/spec/system/selenium_spec.rb +1 -4
  94. data/spec/system/support/system_test_helper.rb +0 -1
  95. data/spec/system/watir_spec.rb +1 -4
  96. data/spec/utilities/headless_config_spec.rb +82 -0
  97. data/spec/utilities/utilities_spec.rb +105 -0
  98. metadata +7 -97
  99. data/lib/adopter/adopt_menu.rb +0 -146
  100. data/lib/adopter/converters/base_converter.rb +0 -84
  101. data/lib/adopter/converters/identity_converter.rb +0 -53
  102. data/lib/adopter/migration_plan.rb +0 -74
  103. data/lib/adopter/migrator.rb +0 -96
  104. data/lib/adopter/plan_builder.rb +0 -275
  105. data/lib/adopter/project_analyzer.rb +0 -252
  106. data/lib/adopter/project_detector.rb +0 -157
  107. data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
  108. data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
  109. data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
  110. data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
  111. data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
  112. data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
  113. data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
  114. data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
  115. data/lib/generators/minitest/minitest_generator.rb +0 -35
  116. data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
  117. data/lib/generators/minitest/templates/performance_test.tt +0 -18
  118. data/lib/generators/minitest/templates/test.tt +0 -64
  119. data/lib/generators/minitest/templates/visual_test.tt +0 -23
  120. data/lib/generators/rspec/templates/performance_spec.tt +0 -18
  121. data/lib/generators/rspec/templates/visual_spec.tt +0 -20
  122. data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
  123. data/lib/generators/templates/helpers/performance_helper.tt +0 -57
  124. data/lib/generators/templates/helpers/visual_helper.tt +0 -58
  125. data/lib/llm/client.rb +0 -79
  126. data/lib/llm/config.rb +0 -57
  127. data/lib/llm/prompts.rb +0 -84
  128. data/lib/llm/provider.rb +0 -27
  129. data/lib/llm/providers/anthropic_provider.rb +0 -43
  130. data/lib/llm/providers/ollama_provider.rb +0 -56
  131. data/lib/llm/providers/openai_provider.rb +0 -42
  132. data/lib/llm/response_parser.rb +0 -67
  133. data/lib/plugin/plugin.rb +0 -111
  134. data/lib/plugin/plugin_exposer.rb +0 -55
  135. data/lib/scaffolding/crud_generator.rb +0 -94
  136. data/lib/scaffolding/dry_run_presenter.rb +0 -16
  137. data/lib/scaffolding/page_introspector.rb +0 -45
  138. data/lib/scaffolding/scaffold_menu.rb +0 -103
  139. data/lib/scaffolding/templates/page_from_url.tt +0 -75
  140. data/lib/scaffolding/templates/spec_from_page.tt +0 -31
  141. data/lib/scaffolding/templates/spec_from_url.tt +0 -46
  142. data/lib/scaffolding/url_analyzer.rb +0 -179
  143. data/sig/adopter/adopt_menu.rbs +0 -25
  144. data/sig/adopter/converters/base_converter.rbs +0 -23
  145. data/sig/adopter/converters/identity_converter.rbs +0 -16
  146. data/sig/adopter/migration_plan.rbs +0 -34
  147. data/sig/adopter/migrator.rbs +0 -21
  148. data/sig/adopter/plan_builder.rbs +0 -38
  149. data/sig/adopter/project_analyzer.rbs +0 -39
  150. data/sig/adopter/project_detector.rbs +0 -26
  151. data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
  152. data/sig/generators/minitest/minitest_generator.rbs +0 -8
  153. data/sig/llm/client.rbs +0 -15
  154. data/sig/llm/config.rbs +0 -20
  155. data/sig/llm/prompts.rbs +0 -8
  156. data/sig/llm/provider.rbs +0 -12
  157. data/sig/llm/providers/anthropic_provider.rbs +0 -16
  158. data/sig/llm/providers/ollama_provider.rbs +0 -18
  159. data/sig/llm/providers/openai_provider.rbs +0 -16
  160. data/sig/llm/response_parser.rbs +0 -13
  161. data/sig/plugin/plugin.rbs +0 -24
  162. data/sig/plugin/plugin_exposer.rbs +0 -20
  163. data/sig/scaffolding/crud_generator.rbs +0 -16
  164. data/sig/scaffolding/dry_run_presenter.rbs +0 -4
  165. data/sig/scaffolding/page_introspector.rbs +0 -14
  166. data/sig/scaffolding/scaffold_menu.rbs +0 -18
  167. data/sig/scaffolding/url_analyzer.rbs +0 -28
  168. data/spec/adopter/adopt_menu_spec.rb +0 -176
  169. data/spec/adopter/converters/identity_converter_spec.rb +0 -145
  170. data/spec/adopter/migration_plan_spec.rb +0 -113
  171. data/spec/adopter/migrator_spec.rb +0 -277
  172. data/spec/adopter/plan_builder_spec.rb +0 -298
  173. data/spec/adopter/project_analyzer_spec.rb +0 -337
  174. data/spec/adopter/project_detector_spec.rb +0 -295
  175. data/spec/generators/generator_spec.rb +0 -23
  176. data/spec/integration/content/reporter_content_spec.rb +0 -236
  177. data/spec/integration/content/skip_flags_content_spec.rb +0 -206
  178. data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
  179. data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
  180. data/spec/integration/generators/minitest_generator_spec.rb +0 -64
  181. data/spec/integration/generators/reporter_spec.rb +0 -159
  182. data/spec/integration/generators/skip_flags_spec.rb +0 -134
  183. data/spec/integration/generators/visual_addon_spec.rb +0 -148
  184. data/spec/llm/client_spec.rb +0 -79
  185. data/spec/llm/config_spec.rb +0 -92
  186. data/spec/llm/prompts_spec.rb +0 -49
  187. data/spec/llm/response_parser_spec.rb +0 -92
  188. data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
  189. data/spec/scaffolding/page_introspector_spec.rb +0 -82
  190. data/spec/scaffolding/url_analyzer_spec.rb +0 -110
  191. data/spec/system/adopt_matrix_spec.rb +0 -537
  192. data/spec/system/adopt_spec.rb +0 -225
  193. data/spec/system/capybara_spec.rb +0 -42
@@ -0,0 +1,540 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require_relative '../../lib/commands/scaffolding_commands'
6
+ require_relative '../../lib/scaffolding/scaffolding'
7
+
8
+ # Custom matchers (defined inline to avoid pulling in integration spec_helper)
9
+ RSpec::Matchers.define :have_frozen_string_literal do
10
+ match { |content| content.include?('# frozen_string_literal: true') }
11
+ failure_message { 'expected file to contain frozen_string_literal magic comment' }
12
+ end
13
+
14
+ RSpec::Matchers.define :have_valid_ruby_syntax do
15
+ match do |content|
16
+ RubyVM::InstructionSequence.compile(content)
17
+ true
18
+ rescue SyntaxError
19
+ false
20
+ end
21
+ failure_message { |_content| "expected valid Ruby syntax but got SyntaxError: #{$ERROR_INFO&.message}" }
22
+ end
23
+
24
+ # End-to-end scaffolding tests that verify the scaffold command creates the right
25
+ # files with the right content across all automation × framework combinations.
26
+ describe 'Scaffolding E2E' do # rubocop:disable RSpec/DescribeClass
27
+
28
+ WEB_AUTOMATIONS = %w[selenium watir].freeze
29
+ TEST_FRAMEWORKS = %w[rspec cucumber].freeze
30
+
31
+ def self.scaffold_style(framework)
32
+ case framework
33
+ when 'rspec' then :spec
34
+ when 'cucumber' then :feature
35
+ end
36
+ end
37
+
38
+ # Build a minimal mock project that the scaffolding system can detect.
39
+ def self.build_mock_project(automation, framework) # rubocop:disable Metrics/MethodLength
40
+ dir = File.expand_path("tmp_scaffold_e2e_#{framework}_#{automation}")
41
+ FileUtils.rm_rf(dir)
42
+ FileUtils.mkdir_p("#{dir}/page_objects/pages")
43
+ FileUtils.mkdir_p("#{dir}/page_objects/abstract")
44
+ FileUtils.mkdir_p("#{dir}/page_objects/components")
45
+ FileUtils.mkdir_p("#{dir}/helpers")
46
+ FileUtils.mkdir_p("#{dir}/config")
47
+ FileUtils.mkdir_p("#{dir}/models/data")
48
+
49
+ case framework
50
+ when 'rspec' then FileUtils.mkdir_p("#{dir}/spec")
51
+ when 'cucumber' then FileUtils.mkdir_p("#{dir}/features/step_definitions")
52
+ FileUtils.mkdir_p("#{dir}/features/support")
53
+ end
54
+
55
+ gems = []
56
+ case automation
57
+ when 'selenium' then gems << "gem 'selenium-webdriver'"
58
+ when 'watir' then gems << "gem 'watir'"
59
+ end
60
+ case framework
61
+ when 'rspec' then gems << "gem 'rspec'"
62
+ when 'cucumber' then gems << "gem 'cucumber'"
63
+ end
64
+ File.write("#{dir}/Gemfile", gems.join("\n") + "\n")
65
+ File.write("#{dir}/config/config.yml", "browser: chrome\nurl: http://localhost:3000\n")
66
+ File.write("#{dir}/page_objects/abstract/page.rb", "class Page; end\n")
67
+ File.write("#{dir}/page_objects/abstract/component.rb", "class Component; end\n")
68
+
69
+ dir
70
+ end
71
+
72
+ # ──────────────────────────────────────────────
73
+ # Shared examples: Page object content
74
+ # ──────────────────────────────────────────────
75
+
76
+ shared_examples 'valid scaffolded page' do
77
+ it 'has frozen_string_literal' do
78
+ expect(page_content).to have_frozen_string_literal
79
+ end
80
+
81
+ it 'has valid Ruby syntax' do
82
+ expect(page_content).to have_valid_ruby_syntax
83
+ end
84
+
85
+ it 'inherits from Page' do
86
+ expect(page_content).to include('class CheckoutPage < Page')
87
+ end
88
+
89
+ it 'requires abstract page' do
90
+ expect(page_content).to include("require_relative '../abstract/page'")
91
+ end
92
+
93
+ it 'includes url method (config has url)' do
94
+ expect(page_content).to include("def url(_page)")
95
+ expect(page_content).to include("'checkout'")
96
+ end
97
+ end
98
+
99
+ shared_examples 'selenium scaffolded page' do
100
+ it 'has driver.find_element example' do
101
+ expect(page_content).to include('driver.find_element')
102
+ end
103
+
104
+ it 'has private section' do
105
+ expect(page_content).to include('private')
106
+ end
107
+
108
+ it 'does not have capybara fill_in' do
109
+ expect(page_content).not_to include('fill_in')
110
+ end
111
+
112
+ it 'does not have watir browser.text_field' do
113
+ expect(page_content).not_to include('browser.text_field')
114
+ end
115
+ end
116
+
117
+ shared_examples 'watir scaffolded page' do
118
+ it 'has browser.text_field example' do
119
+ expect(page_content).to include('browser.text_field')
120
+ end
121
+
122
+ it 'has private section' do
123
+ expect(page_content).to include('private')
124
+ end
125
+
126
+ it 'does not have selenium find_element' do
127
+ expect(page_content).not_to include('driver.find_element')
128
+ end
129
+
130
+ it 'does not have capybara fill_in' do
131
+ expect(page_content).not_to include('fill_in')
132
+ end
133
+ end
134
+
135
+ # ──────────────────────────────────────────────
136
+ # Shared examples: Spec content (RSpec)
137
+ # ──────────────────────────────────────────────
138
+
139
+ shared_examples 'valid scaffolded spec' do
140
+ it 'has frozen_string_literal' do
141
+ expect(spec_content).to have_frozen_string_literal
142
+ end
143
+
144
+ it 'has valid Ruby syntax' do
145
+ expect(spec_content).to have_valid_ruby_syntax
146
+ end
147
+
148
+ it 'describes the page class' do
149
+ expect(spec_content).to include("describe 'CheckoutPage'")
150
+ end
151
+
152
+ it 'requires the page object' do
153
+ expect(spec_content).to include("require_relative '../page_objects/pages/checkout'")
154
+ end
155
+
156
+ it 'has pending test' do
157
+ expect(spec_content).to include("pending 'implement test'")
158
+ end
159
+
160
+ it 'has before block with navigation' do
161
+ expect(spec_content).to include('before do')
162
+ end
163
+ end
164
+
165
+ shared_examples 'selenium scaffolded spec' do
166
+ it 'instantiates page with driver' do
167
+ expect(spec_content).to include('CheckoutPage.new(driver)')
168
+ end
169
+
170
+ it 'navigates with driver' do
171
+ expect(spec_content).to include('driver.navigate.to')
172
+ end
173
+
174
+ it 'does not use capybara visit' do
175
+ expect(spec_content).not_to match(/^\s+visit '/)
176
+ end
177
+ end
178
+
179
+ shared_examples 'watir scaffolded spec' do
180
+ it 'instantiates page with browser' do
181
+ expect(spec_content).to include('CheckoutPage.new(browser)')
182
+ end
183
+
184
+ it 'navigates with browser.goto' do
185
+ expect(spec_content).to include('browser.goto')
186
+ end
187
+ end
188
+
189
+ # ──────────────────────────────────────────────
190
+ # Shared examples: Feature content (Cucumber)
191
+ # ──────────────────────────────────────────────
192
+
193
+ shared_examples 'valid scaffolded feature' do
194
+ it 'has Feature keyword' do
195
+ expect(feature_content).to include('Feature: Checkout')
196
+ end
197
+
198
+ it 'has Scenario keyword' do
199
+ expect(feature_content).to include('Scenario: Scenario name')
200
+ end
201
+
202
+ it 'has Given/When/Then steps' do
203
+ expect(feature_content).to include('Given I am on the checkout page')
204
+ expect(feature_content).to include('When I perform an action')
205
+ expect(feature_content).to include('Then I see the expected result')
206
+ end
207
+ end
208
+
209
+ # ──────────────────────────────────────────────
210
+ # Shared examples: Steps content (Cucumber)
211
+ # ──────────────────────────────────────────────
212
+
213
+ shared_examples 'valid scaffolded steps' do
214
+ it 'has frozen_string_literal' do
215
+ expect(steps_content).to have_frozen_string_literal
216
+ end
217
+
218
+ it 'has valid Ruby syntax' do
219
+ expect(steps_content).to have_valid_ruby_syntax
220
+ end
221
+
222
+ it 'requires the page object' do
223
+ expect(steps_content).to include("require_relative '../../page_objects/pages/checkout'")
224
+ end
225
+
226
+ it 'has Given/When/Then blocks' do
227
+ expect(steps_content).to include("Given('I am on the checkout page')")
228
+ expect(steps_content).to include("When('I perform an action')")
229
+ expect(steps_content).to include("Then('I see the expected result')")
230
+ end
231
+
232
+ it 'has pending steps' do
233
+ expect(steps_content).to include("pending 'implement step'")
234
+ end
235
+ end
236
+
237
+ shared_examples 'selenium scaffolded steps' do
238
+ it 'instantiates page with driver' do
239
+ expect(steps_content).to include('CheckoutPage.new(driver)')
240
+ end
241
+
242
+ it 'does not use capybara visit' do
243
+ expect(steps_content).not_to match(/^\s+visit '/)
244
+ end
245
+ end
246
+
247
+ shared_examples 'watir scaffolded steps' do
248
+ it 'instantiates page with browser' do
249
+ expect(steps_content).to include('CheckoutPage.new(browser)')
250
+ end
251
+ end
252
+
253
+ # ──────────────────────────────────────────────
254
+ # Shared examples: Helper content
255
+ # ──────────────────────────────────────────────
256
+
257
+ shared_examples 'valid scaffolded helper' do
258
+ it 'has frozen_string_literal' do
259
+ expect(helper_content).to have_frozen_string_literal
260
+ end
261
+
262
+ it 'has valid Ruby syntax' do
263
+ expect(helper_content).to have_valid_ruby_syntax
264
+ end
265
+
266
+ it 'defines helper module' do
267
+ expect(helper_content).to include('module CheckoutHelper')
268
+ end
269
+
270
+ it 'has helper placeholder comment' do
271
+ expect(helper_content).to include('# Add your helper code here')
272
+ end
273
+ end
274
+
275
+ # ──────────────────────────────────────────────
276
+ # Shared examples: Component content
277
+ # ──────────────────────────────────────────────
278
+
279
+ shared_examples 'valid scaffolded component' do
280
+ it 'has frozen_string_literal' do
281
+ expect(component_content).to have_frozen_string_literal
282
+ end
283
+
284
+ it 'has valid Ruby syntax' do
285
+ expect(component_content).to have_valid_ruby_syntax
286
+ end
287
+
288
+ it 'inherits from Component' do
289
+ expect(component_content).to include('class Sidebar < Component')
290
+ end
291
+
292
+ it 'requires abstract component' do
293
+ expect(component_content).to include("require_relative '../abstract/component'")
294
+ end
295
+ end
296
+
297
+ shared_examples 'selenium scaffolded component' do
298
+ it 'has driver.find_element example' do
299
+ expect(component_content).to include('driver.find_element')
300
+ end
301
+
302
+ it 'has private section' do
303
+ expect(component_content).to include('private')
304
+ end
305
+
306
+ it 'has content method' do
307
+ expect(component_content).to include('def content')
308
+ end
309
+ end
310
+
311
+ shared_examples 'watir scaffolded component' do
312
+ it 'has browser.element example' do
313
+ expect(component_content).to include('browser.element')
314
+ end
315
+
316
+ it 'has private section' do
317
+ expect(component_content).to include('private')
318
+ end
319
+
320
+ it 'has content method' do
321
+ expect(component_content).to include('def content')
322
+ end
323
+ end
324
+
325
+ # ══════════════════════════════════════════════
326
+ # Test matrix: automation × framework
327
+ # ══════════════════════════════════════════════
328
+
329
+ WEB_AUTOMATIONS.each do |automation|
330
+ TEST_FRAMEWORKS.each do |framework|
331
+ style = scaffold_style(framework)
332
+
333
+ context "with #{framework} and #{automation}" do # rubocop:disable RSpec/ContextWording
334
+ let(:scaffold) { ScaffoldingCommands }
335
+
336
+ # Use absolute path so Dir.chdir is safe regardless of current CWD
337
+ project_dir = build_mock_project(automation, framework)
338
+
339
+ before do
340
+ Dir.chdir(project_dir)
341
+ end
342
+
343
+ after do
344
+ Dir.chdir(File.dirname(project_dir))
345
+ end
346
+
347
+ after(:all) do # rubocop:disable RSpec/BeforeAfterAll
348
+ FileUtils.rm_rf(project_dir)
349
+ end
350
+
351
+ # ── Default scaffold ───────────────────
352
+
353
+ describe 'default scaffold' do
354
+ before do
355
+ scaffold.new.invoke(:scaffold, nil, %w[checkout])
356
+ end
357
+
358
+ after do
359
+ FileUtils.rm_f('page_objects/pages/checkout.rb')
360
+ FileUtils.rm_f('spec/checkout_page_spec.rb')
361
+ if framework == 'cucumber'
362
+ FileUtils.rm_f('features/checkout.feature')
363
+ FileUtils.rm_f('features/step_definitions/checkout_steps.rb')
364
+ else
365
+ FileUtils.rm_rf('features')
366
+ FileUtils.rm_rf('spec') unless framework == 'rspec'
367
+ end
368
+ end
369
+
370
+ it 'creates page object' do
371
+ expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
372
+ end
373
+
374
+ if style == :spec
375
+ it 'creates spec file' do
376
+ expect(Pathname.new('spec/checkout_page_spec.rb')).to be_file
377
+ end
378
+
379
+ it 'does not create feature file' do
380
+ expect(Pathname.new('features/checkout.feature')).not_to be_file
381
+ end
382
+ else # :feature
383
+ it 'creates feature file' do
384
+ expect(Pathname.new('features/checkout.feature')).to be_file
385
+ end
386
+
387
+ it 'creates steps file' do
388
+ expect(Pathname.new('features/step_definitions/checkout_steps.rb')).to be_file
389
+ end
390
+ end
391
+
392
+ # Page content validation
393
+ let(:page_content) { File.read('page_objects/pages/checkout.rb') }
394
+
395
+ include_examples 'valid scaffolded page'
396
+ include_examples "#{automation} scaffolded page"
397
+
398
+ # Test content validation (depends on scaffold style)
399
+ if style == :spec
400
+ let(:spec_content) { File.read('spec/checkout_page_spec.rb') }
401
+ include_examples 'valid scaffolded spec'
402
+ include_examples "#{automation} scaffolded spec"
403
+ else # :feature
404
+ let(:feature_content) { File.read('features/checkout.feature') }
405
+ include_examples 'valid scaffolded feature'
406
+
407
+ let(:steps_content) { File.read('features/step_definitions/checkout_steps.rb') }
408
+ include_examples 'valid scaffolded steps'
409
+ include_examples "#{automation} scaffolded steps"
410
+ end
411
+ end
412
+
413
+ # ── Component scaffold ─────────────────
414
+
415
+ describe 'component scaffold' do
416
+ before do
417
+ scaffold.new.invoke(:component, nil, %w[sidebar])
418
+ end
419
+
420
+ after do
421
+ FileUtils.rm_f('page_objects/components/sidebar.rb')
422
+ end
423
+
424
+ let(:component_content) { File.read('page_objects/components/sidebar.rb') }
425
+
426
+ include_examples 'valid scaffolded component'
427
+ include_examples "#{automation} scaffolded component"
428
+ end
429
+
430
+ # ── Helper scaffold ────────────────────
431
+
432
+ describe 'helper scaffold' do
433
+ before do
434
+ scaffold.new.invoke(:helper, nil, %w[checkout])
435
+ end
436
+
437
+ after do
438
+ FileUtils.rm_f('helpers/checkout_helper.rb')
439
+ end
440
+
441
+ let(:helper_content) { File.read('helpers/checkout_helper.rb') }
442
+
443
+ include_examples 'valid scaffolded helper'
444
+ end
445
+
446
+ # ── Batch scaffold ─────────────────────
447
+
448
+ describe 'batch scaffold' do
449
+ after do
450
+ %w[search filter].each do |name|
451
+ FileUtils.rm_f("page_objects/pages/#{name}.rb")
452
+ FileUtils.rm_f("spec/#{name}_page_spec.rb")
453
+ FileUtils.rm_f("features/#{name}.feature")
454
+ FileUtils.rm_f("features/step_definitions/#{name}_steps.rb")
455
+ end
456
+ FileUtils.rm_rf('features') unless framework == 'cucumber'
457
+ FileUtils.rm_rf('spec') unless framework == 'rspec'
458
+ end
459
+
460
+ it 'generates multiple scaffolds at once' do
461
+ scaffold.new.invoke(:scaffold, nil, %w[search filter])
462
+ expect(Pathname.new('page_objects/pages/search.rb')).to be_file
463
+ expect(Pathname.new('page_objects/pages/filter.rb')).to be_file
464
+
465
+ if style == :spec
466
+ expect(Pathname.new('spec/search_page_spec.rb')).to be_file
467
+ expect(Pathname.new('spec/filter_page_spec.rb')).to be_file
468
+ else
469
+ expect(Pathname.new('features/search.feature')).to be_file
470
+ expect(Pathname.new('features/filter.feature')).to be_file
471
+ end
472
+ end
473
+ end
474
+
475
+ # ── Name normalization ─────────────────
476
+
477
+ describe 'name normalization' do
478
+ after do
479
+ %w[cart shopping_cart checkout].each do |name|
480
+ FileUtils.rm_f("page_objects/pages/#{name}.rb")
481
+ end
482
+ end
483
+
484
+ it 'strips _page suffix' do
485
+ scaffold.new.invoke(:page, nil, %w[cart_page])
486
+ expect(Pathname.new('page_objects/pages/cart.rb')).to be_file
487
+ end
488
+
489
+ it 'converts CamelCase to snake_case' do
490
+ scaffold.new.invoke(:page, nil, %w[ShoppingCart])
491
+ expect(Pathname.new('page_objects/pages/shopping_cart.rb')).to be_file
492
+ end
493
+
494
+ it 'handles CamelCase with Page suffix' do
495
+ scaffold.new.invoke(:page, nil, %w[CheckoutPage])
496
+ expect(Pathname.new('page_objects/pages/checkout.rb')).to be_file
497
+ end
498
+ end
499
+
500
+ # ── Relationships ──────────────────────
501
+
502
+ describe 'relationships' do
503
+ before do
504
+ File.write('page_objects/pages/login.rb', "class LoginPage < Page; end\n")
505
+ end
506
+
507
+ after do
508
+ FileUtils.rm_f('page_objects/pages/dashboard.rb')
509
+ FileUtils.rm_f('spec/dashboard_page_spec.rb')
510
+ FileUtils.rm_f('features/dashboard.feature')
511
+ FileUtils.rm_f('features/step_definitions/dashboard_steps.rb')
512
+ FileUtils.rm_f('page_objects/pages/login.rb')
513
+ FileUtils.rm_rf('features') unless framework == 'cucumber'
514
+ FileUtils.rm_rf('spec') unless framework == 'rspec'
515
+ end
516
+
517
+ it 'adds require_relative for dependent page' do
518
+ scaffold.new.invoke(:page, nil, %w[dashboard --uses login])
519
+ content = File.read('page_objects/pages/dashboard.rb')
520
+ expect(content).to include("require_relative 'login'")
521
+ end
522
+
523
+ if style == :spec
524
+ it 'adds dependency to spec let declarations' do
525
+ scaffold.new.invoke(:spec, nil, %w[dashboard --uses login])
526
+ content = File.read('spec/dashboard_page_spec.rb')
527
+ expect(content).to include('LoginPage')
528
+
529
+ case automation
530
+ when 'selenium' then expect(content).to include('LoginPage.new(driver)')
531
+ when 'watir' then expect(content).to include('LoginPage.new(browser)')
532
+ end
533
+ end
534
+ end
535
+ end
536
+
537
+ end
538
+ end
539
+ end
540
+ end
@@ -2,12 +2,10 @@ module SettingsHelper
2
2
  def create_settings(options)
3
3
  automation = options[:automation]
4
4
  framework = options[:framework]
5
- ci_platform = options[:ci_platform]
6
5
  {
7
6
  automation:,
8
7
  framework:,
9
- ci_platform:,
10
- name: ci_platform ? "#{framework}_#{automation}_#{ci_platform}" : "#{framework}_#{automation}"
8
+ name: "#{framework}_#{automation}"
11
9
  }
12
10
  end
13
11
  end
@@ -5,15 +5,13 @@ require 'rspec'
5
5
  require_relative '../../lib/generators/invoke_generators'
6
6
  require_relative 'settings_helper'
7
7
 
8
- AUTOMATION_TYPES = %w[android ios selenium watir cross_platform capybara].freeze
9
- FRAMEWORKS = %w[cucumber rspec minitest].freeze
10
- CI_PLATFORMS = [nil, 'github', 'gitlab'].freeze
8
+ AUTOMATION_TYPES = %w[android ios selenium watir cross_platform].freeze
9
+ FRAMEWORKS = %w[cucumber rspec].freeze
11
10
 
12
11
  # Named constants for readable spec references — no more fragile numeric indices
13
12
  module FrameworkIndex
14
13
  CUCUMBER = FRAMEWORKS[0]
15
14
  RSPEC = FRAMEWORKS[1]
16
- MINITEST = FRAMEWORKS[2]
17
15
  end
18
16
 
19
17
  module AutomationIndex
@@ -22,17 +20,16 @@ module AutomationIndex
22
20
  SELENIUM = AUTOMATION_TYPES[2]
23
21
  WATIR = AUTOMATION_TYPES[3]
24
22
  CROSS_PLATFORM = AUTOMATION_TYPES[4]
25
- CAPYBARA = AUTOMATION_TYPES[5]
26
23
  end
27
24
 
28
25
  # Lazy project generation — only generates a project the first time it's needed.
29
- # Replaces the old before(:all) that generated all 54 combinations upfront.
26
+ # Replaces the old before(:all) that generated all combinations upfront.
30
27
  module LazyProjectGenerator
31
28
  GENERATED = Set.new
32
29
  MUTEX = Mutex.new
33
30
 
34
- def ensure_project(framework:, automation:, ci_platform: nil)
35
- settings = create_settings(framework:, automation:, ci_platform:)
31
+ def ensure_project(framework:, automation:)
32
+ settings = create_settings(framework:, automation:)
36
33
  project_name = settings[:name]
37
34
 
38
35
  MUTEX.synchronize do
@@ -57,9 +54,7 @@ RSpec.configure do |config|
57
54
  # Individual specs can also call ensure_project() for on-demand generation.
58
55
  FRAMEWORKS.each do |framework|
59
56
  AUTOMATION_TYPES.each do |automation|
60
- CI_PLATFORMS.each do |ci_platform|
61
- ensure_project(framework:, automation:, ci_platform:)
62
- end
57
+ ensure_project(framework:, automation:)
63
58
  end
64
59
  end
65
60
  end