ruby_raider 2.0.0 → 3.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 (224) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yml +58 -0
  3. data/.github/workflows/steep.yml +21 -0
  4. data/.gitignore +1 -1
  5. data/.reek.yml +46 -4
  6. data/.ruby-version +1 -1
  7. data/README.md +138 -77
  8. data/Steepfile +22 -0
  9. data/assets/ruby_raider_logo.svg +51 -0
  10. data/lib/adopter/adopt_menu.rb +11 -15
  11. data/lib/adopter/converters/base_converter.rb +1 -2
  12. data/lib/adopter/converters/identity_converter.rb +3 -6
  13. data/lib/adopter/migration_plan.rb +0 -1
  14. data/lib/adopter/plan_builder.rb +2 -5
  15. data/lib/adopter/project_analyzer.rb +1 -5
  16. data/lib/adopter/project_detector.rb +3 -5
  17. data/lib/commands/adopt_commands.rb +0 -1
  18. data/lib/commands/plugin_commands.rb +0 -2
  19. data/lib/commands/scaffolding_commands.rb +220 -37
  20. data/lib/commands/utility_commands.rb +82 -2
  21. data/lib/generators/automation/automation_generator.rb +0 -7
  22. data/lib/generators/automation/templates/partials/element.tt +1 -1
  23. data/lib/generators/automation/templates/partials/initialize_selector.tt +0 -7
  24. data/lib/generators/automation/templates/partials/url_methods.tt +0 -1
  25. data/lib/generators/common_generator.rb +12 -0
  26. data/lib/generators/cucumber/cucumber_generator.rb +36 -0
  27. data/lib/generators/cucumber/templates/accessibility_feature.tt +5 -0
  28. data/lib/generators/cucumber/templates/accessibility_steps.tt +21 -0
  29. data/lib/generators/cucumber/templates/cucumber.tt +8 -1
  30. data/lib/generators/cucumber/templates/feature.tt +0 -4
  31. data/lib/generators/cucumber/templates/partials/appium_env.tt +5 -0
  32. data/lib/generators/cucumber/templates/partials/capybara_env.tt +19 -1
  33. data/lib/generators/cucumber/templates/partials/driver_world.tt +1 -4
  34. data/lib/generators/cucumber/templates/partials/selenium_env.tt +22 -35
  35. data/lib/generators/cucumber/templates/partials/watir_env.tt +20 -1
  36. data/lib/generators/cucumber/templates/partials/web_steps.tt +6 -12
  37. data/lib/generators/cucumber/templates/performance_feature.tt +5 -0
  38. data/lib/generators/cucumber/templates/performance_steps.tt +17 -0
  39. data/lib/generators/cucumber/templates/visual_feature.tt +5 -0
  40. data/lib/generators/cucumber/templates/visual_steps.tt +19 -0
  41. data/lib/generators/generator.rb +38 -7
  42. data/lib/generators/helper_generator.rb +24 -7
  43. data/lib/generators/infrastructure/templates/github.tt +1 -1
  44. data/lib/generators/infrastructure/templates/github_appium.tt +2 -2
  45. data/lib/generators/infrastructure/templates/gitlab.tt +1 -1
  46. data/lib/generators/invoke_generators.rb +42 -9
  47. data/lib/generators/menu_generator.rb +120 -11
  48. data/lib/generators/minitest/minitest_generator.rb +16 -4
  49. data/lib/generators/minitest/templates/accessibility_test.tt +26 -0
  50. data/lib/generators/minitest/templates/performance_test.tt +18 -0
  51. data/lib/generators/minitest/templates/test.tt +5 -34
  52. data/lib/generators/minitest/templates/visual_test.tt +23 -0
  53. data/lib/generators/rspec/rspec_generator.rb +16 -4
  54. data/lib/generators/rspec/templates/accessibility_spec.tt +25 -0
  55. data/lib/generators/rspec/templates/performance_spec.tt +18 -0
  56. data/lib/generators/rspec/templates/spec.tt +5 -35
  57. data/lib/generators/rspec/templates/visual_spec.tt +20 -0
  58. data/lib/generators/template_renderer/partial_cache.rb +11 -1
  59. data/lib/generators/template_renderer/partial_resolver.rb +17 -10
  60. data/lib/generators/template_renderer.rb +17 -1
  61. data/lib/generators/templates/common/gemfile.tt +21 -6
  62. data/lib/generators/templates/common/git_ignore.tt +6 -1
  63. data/lib/generators/templates/common/partials/mobile_config.tt +5 -1
  64. data/lib/generators/templates/common/partials/web_config.tt +16 -7
  65. data/lib/generators/templates/common/rakefile.tt +36 -0
  66. data/lib/generators/templates/common/read_me.tt +41 -91
  67. data/lib/generators/templates/common/rspec.tt +3 -0
  68. data/lib/generators/templates/common/ruby_version.tt +1 -0
  69. data/lib/generators/templates/helpers/allure_helper.tt +11 -0
  70. data/lib/generators/templates/helpers/browser_helper.tt +12 -2
  71. data/lib/generators/templates/helpers/capybara_helper.tt +5 -1
  72. data/lib/generators/templates/helpers/debug_helper.tt +190 -0
  73. data/lib/generators/templates/helpers/driver_helper.tt +2 -10
  74. data/lib/generators/templates/helpers/partials/appium_driver.tt +0 -2
  75. data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +7 -0
  76. data/lib/generators/templates/helpers/partials/debug_start.tt +7 -0
  77. data/lib/generators/templates/helpers/partials/driver_and_options.tt +1 -3
  78. data/lib/generators/templates/helpers/partials/selenium_driver.tt +8 -7
  79. data/lib/generators/templates/helpers/partials/video_start.tt +9 -0
  80. data/lib/generators/templates/helpers/partials/video_stop.tt +4 -0
  81. data/lib/generators/templates/helpers/performance_helper.tt +57 -0
  82. data/lib/generators/templates/helpers/spec_helper.tt +57 -8
  83. data/lib/generators/templates/helpers/test_helper.tt +69 -1
  84. data/lib/generators/templates/helpers/video_helper.tt +270 -0
  85. data/lib/generators/templates/helpers/visual_helper.tt +39 -46
  86. data/lib/llm/client.rb +79 -0
  87. data/lib/llm/config.rb +57 -0
  88. data/lib/llm/prompts.rb +84 -0
  89. data/lib/llm/provider.rb +27 -0
  90. data/lib/llm/providers/anthropic_provider.rb +43 -0
  91. data/lib/llm/providers/ollama_provider.rb +56 -0
  92. data/lib/llm/providers/openai_provider.rb +42 -0
  93. data/lib/llm/response_parser.rb +67 -0
  94. data/lib/plugin/plugin.rb +22 -20
  95. data/lib/plugin/plugin_exposer.rb +16 -38
  96. data/lib/ruby_raider.rb +47 -12
  97. data/lib/scaffolding/crud_generator.rb +94 -0
  98. data/lib/scaffolding/dry_run_presenter.rb +16 -0
  99. data/lib/scaffolding/name_normalizer.rb +63 -0
  100. data/lib/scaffolding/page_introspector.rb +45 -0
  101. data/lib/scaffolding/project_detector.rb +72 -0
  102. data/lib/scaffolding/scaffold_menu.rb +103 -0
  103. data/lib/scaffolding/scaffolding.rb +158 -11
  104. data/lib/scaffolding/templates/component.tt +30 -0
  105. data/lib/scaffolding/templates/feature.tt +4 -4
  106. data/lib/scaffolding/templates/helper.tt +15 -1
  107. data/lib/scaffolding/templates/page_from_url.tt +75 -0
  108. data/lib/scaffolding/templates/page_object.tt +50 -1
  109. data/lib/scaffolding/templates/spec.tt +33 -2
  110. data/lib/scaffolding/templates/spec_from_page.tt +31 -0
  111. data/lib/scaffolding/templates/spec_from_url.tt +46 -0
  112. data/lib/scaffolding/templates/steps.tt +17 -5
  113. data/lib/scaffolding/url_analyzer.rb +179 -0
  114. data/lib/utilities/desktop_downloader.rb +177 -0
  115. data/lib/utilities/logo.rb +83 -0
  116. data/lib/utilities/utilities.rb +53 -20
  117. data/lib/version +1 -1
  118. data/ruby_raider.gemspec +1 -0
  119. data/sig/adopter/adopt_menu.rbs +25 -0
  120. data/sig/adopter/converters/base_converter.rbs +23 -0
  121. data/sig/adopter/converters/identity_converter.rbs +16 -0
  122. data/sig/adopter/migration_plan.rbs +34 -0
  123. data/sig/adopter/migrator.rbs +21 -0
  124. data/sig/adopter/plan_builder.rbs +38 -0
  125. data/sig/adopter/project_analyzer.rbs +39 -0
  126. data/sig/adopter/project_detector.rbs +26 -0
  127. data/sig/commands/adopt_commands.rbs +8 -0
  128. data/sig/commands/loaded_commands.rbs +5 -0
  129. data/sig/commands/plugin_commands.rbs +9 -0
  130. data/sig/commands/scaffolding_commands.rbs +28 -0
  131. data/sig/commands/utility_commands.rbs +21 -0
  132. data/sig/generators/automation/automation_generator.rbs +20 -0
  133. data/sig/generators/common_generator.rbs +12 -0
  134. data/sig/generators/cucumber/cucumber_generator.rbs +16 -0
  135. data/sig/generators/generator.rbs +40 -0
  136. data/sig/generators/helper_generator.rbs +18 -0
  137. data/sig/generators/infrastructure/github_generator.rbs +5 -0
  138. data/sig/generators/infrastructure/gitlab_generator.rbs +4 -0
  139. data/sig/generators/invoke_generators.rbs +10 -0
  140. data/sig/generators/menu_generator.rbs +29 -0
  141. data/sig/generators/minitest/minitest_generator.rbs +8 -0
  142. data/sig/generators/rspec/rspec_generator.rbs +8 -0
  143. data/sig/generators/template_renderer/partial_cache.rbs +20 -0
  144. data/sig/generators/template_renderer/partial_resolver.rbs +20 -0
  145. data/sig/generators/template_renderer/template_error.rbs +19 -0
  146. data/sig/generators/template_renderer.rbs +10 -0
  147. data/sig/llm/client.rbs +15 -0
  148. data/sig/llm/config.rbs +20 -0
  149. data/sig/llm/prompts.rbs +8 -0
  150. data/sig/llm/provider.rbs +12 -0
  151. data/sig/llm/providers/anthropic_provider.rbs +16 -0
  152. data/sig/llm/providers/ollama_provider.rbs +18 -0
  153. data/sig/llm/providers/openai_provider.rbs +16 -0
  154. data/sig/llm/response_parser.rbs +13 -0
  155. data/sig/plugin/plugin.rbs +24 -0
  156. data/sig/plugin/plugin_exposer.rbs +20 -0
  157. data/sig/ruby_raider.rbs +15 -0
  158. data/sig/scaffolding/crud_generator.rbs +16 -0
  159. data/sig/scaffolding/dry_run_presenter.rbs +4 -0
  160. data/sig/scaffolding/name_normalizer.rbs +17 -0
  161. data/sig/scaffolding/page_introspector.rbs +14 -0
  162. data/sig/scaffolding/project_detector.rbs +14 -0
  163. data/sig/scaffolding/scaffold_menu.rbs +18 -0
  164. data/sig/scaffolding/scaffolding.rbs +55 -0
  165. data/sig/scaffolding/url_analyzer.rbs +28 -0
  166. data/sig/utilities/desktop_downloader.rbs +23 -0
  167. data/sig/utilities/logger.rbs +13 -0
  168. data/sig/utilities/logo.rbs +16 -0
  169. data/sig/utilities/utilities.rbs +30 -0
  170. data/sig/vendor/thor.rbs +34 -0
  171. data/sig/vendor/tty_prompt.rbs +15 -0
  172. data/spec/adopter/adopt_menu_spec.rb +12 -12
  173. data/spec/adopter/migration_plan_spec.rb +1 -1
  174. data/spec/adopter/migrator_spec.rb +2 -2
  175. data/spec/adopter/project_detector_spec.rb +1 -1
  176. data/spec/commands/raider_commands_spec.rb +129 -0
  177. data/spec/generators/generator_spec.rb +23 -0
  178. data/spec/integration/commands/scaffolding_commands_spec.rb +1 -1
  179. data/spec/integration/commands/utility_commands_spec.rb +23 -3
  180. data/spec/integration/content/ci_content_spec.rb +119 -0
  181. data/spec/integration/content/common_content_spec.rb +288 -0
  182. data/spec/integration/content/config_content_spec.rb +175 -0
  183. data/spec/integration/content/content_helper.rb +32 -0
  184. data/spec/integration/content/gemfile_content_spec.rb +209 -0
  185. data/spec/integration/content/helper_content_spec.rb +485 -0
  186. data/spec/integration/content/page_content_spec.rb +259 -0
  187. data/spec/integration/content/reporter_content_spec.rb +236 -0
  188. data/spec/integration/content/skip_flags_content_spec.rb +206 -0
  189. data/spec/integration/content/syntax_validation_spec.rb +30 -0
  190. data/spec/integration/content/test_content_spec.rb +266 -0
  191. data/spec/integration/end_to_end_features_spec.rb +690 -0
  192. data/spec/integration/end_to_end_spec.rb +52 -16
  193. data/spec/integration/generators/automation_generator_spec.rb +0 -12
  194. data/spec/integration/generators/axe_addon_spec.rb +150 -0
  195. data/spec/integration/generators/common_generator_spec.rb +12 -13
  196. data/spec/integration/generators/config_features_spec.rb +155 -0
  197. data/spec/integration/generators/debug_helper_spec.rb +68 -0
  198. data/spec/integration/generators/helpers_generator_spec.rb +0 -12
  199. data/spec/integration/generators/lighthouse_addon_spec.rb +132 -0
  200. data/spec/integration/generators/minitest_generator_spec.rb +0 -6
  201. data/spec/integration/generators/reporter_spec.rb +159 -0
  202. data/spec/integration/generators/skip_flags_spec.rb +134 -0
  203. data/spec/integration/generators/visual_addon_spec.rb +148 -0
  204. data/spec/integration/settings_helper.rb +0 -3
  205. data/spec/integration/spec_helper.rb +30 -13
  206. data/spec/llm/client_spec.rb +79 -0
  207. data/spec/llm/config_spec.rb +92 -0
  208. data/spec/llm/prompts_spec.rb +49 -0
  209. data/spec/llm/response_parser_spec.rb +92 -0
  210. data/spec/menus/adopter_adopt_menu_spec.rb +97 -0
  211. data/spec/menus/menu_generator_spec.rb +263 -0
  212. data/spec/scaffolding/name_normalizer_spec.rb +113 -0
  213. data/spec/scaffolding/page_introspector_spec.rb +82 -0
  214. data/spec/scaffolding/scaffold_project_detector_spec.rb +104 -0
  215. data/spec/scaffolding/scaffolding_features_spec.rb +311 -0
  216. data/spec/scaffolding/url_analyzer_spec.rb +110 -0
  217. data/spec/system/adopt_matrix_spec.rb +537 -0
  218. data/spec/system/adopt_spec.rb +225 -0
  219. data/spec/system/support/system_test_helper.rb +0 -2
  220. data/spec/utilities/desktop_downloader_spec.rb +92 -0
  221. metadata +150 -5
  222. data/lib/generators/automation/templates/visual_options.tt +0 -16
  223. data/lib/generators/templates/helpers/partials/axe_driver.tt +0 -10
  224. data/lib/generators/templates/helpers/visual_spec_helper.tt +0 -35
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/scaffolding/url_analyzer'
4
+
5
+ RSpec.describe UrlAnalyzer do
6
+ subject(:analyzer) { described_class.new('https://example.com/login') }
7
+
8
+ let(:sample_html) do
9
+ <<~HTML
10
+ <html>
11
+ <body>
12
+ <form>
13
+ <input type="text" id="username" name="username" />
14
+ <input type="password" id="password" name="password" />
15
+ <input type="hidden" name="csrf" value="token123" />
16
+ <select id="role" name="role"><option>Admin</option></select>
17
+ <textarea id="bio" name="bio"></textarea>
18
+ <button id="login-btn">Log In</button>
19
+ <input type="submit" id="submit-btn" value="Submit" />
20
+ <a href="/forgot">Forgot Password</a>
21
+ </form>
22
+ </body>
23
+ </html>
24
+ HTML
25
+ end
26
+
27
+ before do
28
+ stub_request = instance_double(Net::HTTPSuccess, body: sample_html)
29
+ allow(Net::HTTP).to receive(:get_response).and_return(stub_request)
30
+ end
31
+
32
+ describe '#analyze' do
33
+ before { analyzer.analyze }
34
+
35
+ it 'parses input elements (excluding hidden)' do
36
+ inputs = analyzer.elements.select { |e| e[:type] == :input }
37
+ expect(inputs.length).to eq(2)
38
+ names = inputs.map { |e| e[:name] }
39
+ expect(names).to include('username', 'password')
40
+ end
41
+
42
+ it 'parses select elements' do
43
+ selects = analyzer.elements.select { |e| e[:type] == :select }
44
+ expect(selects.length).to eq(1)
45
+ expect(selects.first[:name]).to eq('role')
46
+ end
47
+
48
+ it 'parses textarea elements' do
49
+ textareas = analyzer.elements.select { |e| e[:type] == :textarea }
50
+ expect(textareas.length).to eq(1)
51
+ end
52
+
53
+ it 'parses button elements' do
54
+ buttons = analyzer.elements.select { |e| e[:type] == :button }
55
+ expect(buttons.length).to eq(1)
56
+ expect(buttons.first[:text]).to eq('Log In')
57
+ end
58
+
59
+ it 'parses submit inputs' do
60
+ submits = analyzer.elements.select { |e| e[:type] == :submit }
61
+ expect(submits.length).to eq(1)
62
+ end
63
+
64
+ it 'parses link elements' do
65
+ links = analyzer.elements.select { |e| e[:type] == :link }
66
+ expect(links.length).to eq(1)
67
+ expect(links.first[:name]).to eq('forgot_password')
68
+ end
69
+
70
+ it 'prefers id locators' do
71
+ username = analyzer.elements.find { |e| e[:name] == 'username' }
72
+ expect(username[:locator]).to eq(type: :id, value: 'username')
73
+ end
74
+ end
75
+
76
+ describe '#page_name' do
77
+ it 'derives name from URL path' do
78
+ expect(analyzer.page_name).to eq('login')
79
+ end
80
+
81
+ it 'uses name override when provided' do
82
+ custom = described_class.new('https://example.com/login', name_override: 'auth')
83
+ expect(custom.page_name).to eq('auth')
84
+ end
85
+
86
+ it 'defaults to home for root URL' do
87
+ root = described_class.new('https://example.com/')
88
+ expect(root.page_name).to eq('home')
89
+ end
90
+ end
91
+
92
+ describe '#to_h' do
93
+ it 'returns structured hash' do
94
+ analyzer.analyze
95
+ result = analyzer.to_h
96
+ expect(result).to include(:page_name, :url, :url_path, :elements)
97
+ expect(result[:url_path]).to eq('/login')
98
+ end
99
+ end
100
+
101
+ describe 'AI mode' do
102
+ let(:ai_analyzer) { described_class.new('https://example.com/login', ai: true) }
103
+
104
+ it 'falls back to regex parsing when LLM is unavailable' do
105
+ allow_any_instance_of(described_class).to receive(:analyze_with_llm).and_return(nil)
106
+ ai_analyzer.analyze
107
+ expect(ai_analyzer.elements).not_to be_empty
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,537 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require_relative '../../lib/adopter/adopt_menu'
6
+
7
+ describe 'Adopt matrix: all target combinations' do
8
+ source_dir = 'tmp_adopt_matrix_source'
9
+
10
+ before(:all) do # rubocop:disable RSpec/BeforeAfterAll
11
+ FileUtils.mkdir_p("#{source_dir}/pages")
12
+ FileUtils.mkdir_p("#{source_dir}/spec")
13
+ FileUtils.mkdir_p("#{source_dir}/features/step_definitions")
14
+
15
+ File.write("#{source_dir}/Gemfile", <<~GEMFILE)
16
+ gem 'rspec'
17
+ gem 'selenium-webdriver'
18
+ gem 'faker'
19
+ gem 'dotenv'
20
+ GEMFILE
21
+
22
+ File.write("#{source_dir}/pages/login_page.rb", <<~RUBY)
23
+ class LoginPage < BasePage
24
+ def login(user, pass)
25
+ driver.find_element(id: 'user').send_keys user
26
+ driver.find_element(id: 'pass').send_keys pass
27
+ driver.find_element(id: 'submit').click
28
+ end
29
+ end
30
+ RUBY
31
+
32
+ File.write("#{source_dir}/pages/dashboard_page.rb", <<~RUBY)
33
+ class DashboardPage < BasePage
34
+ def welcome_message
35
+ driver.find_element(css: '.welcome').text
36
+ end
37
+ end
38
+ RUBY
39
+
40
+ File.write("#{source_dir}/spec/login_spec.rb", <<~RUBY)
41
+ describe 'Login' do
42
+ it 'can log in' do
43
+ page = LoginPage.new(driver)
44
+ page.login('admin', 'secret')
45
+ expect(page).to be_truthy
46
+ end
47
+ end
48
+ RUBY
49
+
50
+ File.write("#{source_dir}/spec/dashboard_spec.rb", <<~RUBY)
51
+ describe 'Dashboard' do
52
+ it 'shows welcome' do
53
+ expect(true).to eq true
54
+ end
55
+ end
56
+ RUBY
57
+
58
+ File.write("#{source_dir}/features/login.feature", <<~GHERKIN)
59
+ Feature: Login
60
+ Scenario: Valid login
61
+ Given I am on the login page
62
+ When I log in with valid credentials
63
+ Then I see the dashboard
64
+ GHERKIN
65
+
66
+ File.write("#{source_dir}/features/step_definitions/login_steps.rb", <<~RUBY)
67
+ Given('I am on the login page') do
68
+ @page = LoginPage.new(driver)
69
+ end
70
+
71
+ When('I log in with valid credentials') do
72
+ @page.login('admin', 'secret')
73
+ end
74
+
75
+ Then('I see the dashboard') do
76
+ expect(true).to eq true
77
+ end
78
+ RUBY
79
+
80
+ # Generate all 9 combinations
81
+ Adopter::AdoptMenu::WEB_AUTOMATIONS.each do |automation|
82
+ Adopter::AdoptMenu::TEST_FRAMEWORKS.each do |framework|
83
+ output = "tmp_adopt_matrix_#{automation}_#{framework}"
84
+ Adopter::AdoptMenu.adopt(
85
+ source_path: source_dir,
86
+ output_path: output,
87
+ target_automation: automation,
88
+ target_framework: framework
89
+ )
90
+ end
91
+ end
92
+ end
93
+
94
+ after(:all) do # rubocop:disable RSpec/BeforeAfterAll
95
+ FileUtils.rm_rf(source_dir)
96
+ Adopter::AdoptMenu::WEB_AUTOMATIONS.each do |automation|
97
+ Adopter::AdoptMenu::TEST_FRAMEWORKS.each do |framework|
98
+ FileUtils.rm_rf("tmp_adopt_matrix_#{automation}_#{framework}")
99
+ end
100
+ end
101
+ end
102
+
103
+ # --- Shared examples ---
104
+
105
+ shared_examples 'valid adopted project structure' do |output_dir|
106
+ it 'creates Gemfile' do
107
+ expect(File).to exist("#{output_dir}/Gemfile")
108
+ end
109
+
110
+ it 'creates Rakefile' do
111
+ expect(File).to exist("#{output_dir}/Rakefile")
112
+ end
113
+
114
+ it 'creates Readme.md' do
115
+ expect(File).to exist("#{output_dir}/Readme.md")
116
+ end
117
+
118
+ it 'creates .rubocop.yml' do
119
+ expect(File).to exist("#{output_dir}/.rubocop.yml")
120
+ end
121
+
122
+ it 'creates config/config.yml' do
123
+ expect(File).to exist("#{output_dir}/config/config.yml")
124
+ end
125
+
126
+ it 'creates page_objects directory' do
127
+ expect(File).to exist("#{output_dir}/page_objects/pages")
128
+ end
129
+
130
+ it 'creates abstract page' do
131
+ expect(File).to exist("#{output_dir}/page_objects/abstract/page.rb")
132
+ end
133
+ end
134
+
135
+ shared_examples 'converted pages' do |output_dir|
136
+ it 'converts login page into raider path' do
137
+ expect(File).to exist("#{output_dir}/page_objects/pages/login.rb")
138
+ end
139
+
140
+ it 'converts dashboard page into raider path' do
141
+ expect(File).to exist("#{output_dir}/page_objects/pages/dashboard.rb")
142
+ end
143
+
144
+ it 'adds frozen_string_literal to login page' do
145
+ page = File.read("#{output_dir}/page_objects/pages/login.rb")
146
+ expect(page).to include('# frozen_string_literal: true')
147
+ end
148
+
149
+ it 'updates login page base class to Page' do
150
+ page = File.read("#{output_dir}/page_objects/pages/login.rb")
151
+ expect(page).to include('class LoginPage < Page')
152
+ end
153
+
154
+ it 'updates dashboard page base class to Page' do
155
+ page = File.read("#{output_dir}/page_objects/pages/dashboard.rb")
156
+ expect(page).to include('class DashboardPage < Page')
157
+ end
158
+ end
159
+
160
+ shared_examples 'merged gems' do |output_dir|
161
+ it 'includes custom gems from source' do
162
+ gemfile = File.read("#{output_dir}/Gemfile")
163
+ expect(gemfile).to include("gem 'faker'")
164
+ expect(gemfile).to include("gem 'dotenv'")
165
+ end
166
+
167
+ it 'does not duplicate framework gems' do
168
+ gemfile = File.read("#{output_dir}/Gemfile")
169
+ occurrences = gemfile.scan("gem 'rspec'").length + gemfile.scan("gem 'selenium-webdriver'").length
170
+ expect(occurrences).to be <= 2
171
+ end
172
+ end
173
+
174
+ shared_examples 'allure helper' do |output_dir|
175
+ it 'creates allure_helper.rb' do
176
+ expect(File).to exist("#{output_dir}/helpers/allure_helper.rb")
177
+ end
178
+
179
+ it 'allure_helper defines AllureHelper module' do
180
+ content = File.read("#{output_dir}/helpers/allure_helper.rb")
181
+ expect(content).to include('module AllureHelper')
182
+ end
183
+ end
184
+
185
+ # --- Automation-specific helpers ---
186
+
187
+ shared_examples 'selenium helpers' do |output_dir|
188
+ it 'creates driver_helper.rb' do
189
+ expect(File).to exist("#{output_dir}/helpers/driver_helper.rb")
190
+ end
191
+
192
+ it 'driver_helper defines DriverHelper module' do
193
+ content = File.read("#{output_dir}/helpers/driver_helper.rb")
194
+ expect(content).to include('module DriverHelper')
195
+ end
196
+
197
+ it 'driver_helper requires selenium-webdriver' do
198
+ content = File.read("#{output_dir}/helpers/driver_helper.rb")
199
+ expect(content).to include("require 'selenium-webdriver'")
200
+ end
201
+
202
+ it 'does not create browser_helper.rb' do
203
+ expect(File).not_to exist("#{output_dir}/helpers/browser_helper.rb")
204
+ end
205
+
206
+ it 'does not create capybara_helper.rb' do
207
+ expect(File).not_to exist("#{output_dir}/helpers/capybara_helper.rb")
208
+ end
209
+ end
210
+
211
+ shared_examples 'watir helpers' do |output_dir|
212
+ it 'creates browser_helper.rb' do
213
+ expect(File).to exist("#{output_dir}/helpers/browser_helper.rb")
214
+ end
215
+
216
+ it 'browser_helper defines BrowserHelper module' do
217
+ content = File.read("#{output_dir}/helpers/browser_helper.rb")
218
+ expect(content).to include('module BrowserHelper')
219
+ end
220
+
221
+ it 'browser_helper requires watir' do
222
+ content = File.read("#{output_dir}/helpers/browser_helper.rb")
223
+ expect(content).to include("require 'watir'")
224
+ end
225
+
226
+ it 'does not create driver_helper.rb' do
227
+ expect(File).not_to exist("#{output_dir}/helpers/driver_helper.rb")
228
+ end
229
+
230
+ it 'does not create capybara_helper.rb' do
231
+ expect(File).not_to exist("#{output_dir}/helpers/capybara_helper.rb")
232
+ end
233
+ end
234
+
235
+ shared_examples 'capybara helpers' do |output_dir|
236
+ it 'creates capybara_helper.rb' do
237
+ expect(File).to exist("#{output_dir}/helpers/capybara_helper.rb")
238
+ end
239
+
240
+ it 'capybara_helper defines CapybaraHelper module' do
241
+ content = File.read("#{output_dir}/helpers/capybara_helper.rb")
242
+ expect(content).to include('module CapybaraHelper')
243
+ end
244
+
245
+ it 'capybara_helper requires capybara' do
246
+ content = File.read("#{output_dir}/helpers/capybara_helper.rb")
247
+ expect(content).to include("require 'capybara'")
248
+ end
249
+
250
+ it 'does not create driver_helper.rb' do
251
+ expect(File).not_to exist("#{output_dir}/helpers/driver_helper.rb")
252
+ end
253
+
254
+ it 'does not create browser_helper.rb' do
255
+ expect(File).not_to exist("#{output_dir}/helpers/browser_helper.rb")
256
+ end
257
+ end
258
+
259
+ # --- Framework-specific structure ---
260
+
261
+ shared_examples 'rspec structure' do |output_dir|
262
+ it 'creates spec directory' do
263
+ expect(File).to exist("#{output_dir}/spec")
264
+ end
265
+
266
+ it 'creates spec_helper.rb' do
267
+ expect(File).to exist("#{output_dir}/helpers/spec_helper.rb")
268
+ end
269
+
270
+ it 'spec_helper defines SpecHelper module' do
271
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
272
+ expect(content).to include('module SpecHelper')
273
+ end
274
+
275
+ it 'converts source tests into spec directory' do
276
+ expect(File).to exist("#{output_dir}/spec/login_spec.rb")
277
+ end
278
+
279
+ it 'converts dashboard test into spec directory' do
280
+ expect(File).to exist("#{output_dir}/spec/dashboard_spec.rb")
281
+ end
282
+
283
+ it 'does not create test directory' do
284
+ expect(Dir).not_to exist("#{output_dir}/test")
285
+ end
286
+
287
+ it 'does not create features directory with step definitions' do
288
+ expect(File).not_to exist("#{output_dir}/features/step_definitions/login_steps.rb")
289
+ end
290
+ end
291
+
292
+ shared_examples 'cucumber structure' do |output_dir|
293
+ it 'creates features directory' do
294
+ expect(File).to exist("#{output_dir}/features")
295
+ end
296
+
297
+ it 'creates features/support/env.rb' do
298
+ expect(File).to exist("#{output_dir}/features/support/env.rb")
299
+ end
300
+
301
+ it 'creates cucumber.yml' do
302
+ expect(File).to exist("#{output_dir}/cucumber.yml")
303
+ end
304
+
305
+ it 'copies feature files' do
306
+ expect(File).to exist("#{output_dir}/features/login.feature")
307
+ end
308
+
309
+ it 'feature files contain Gherkin keywords' do
310
+ content = File.read("#{output_dir}/features/login.feature")
311
+ expect(content).to include('Feature:')
312
+ expect(content).to include('Scenario:')
313
+ end
314
+
315
+ it 'copies step definitions' do
316
+ expect(File).to exist("#{output_dir}/features/step_definitions/login_steps.rb")
317
+ end
318
+
319
+ it 'does not create spec directory with converted tests' do
320
+ expect(File).not_to exist("#{output_dir}/spec/login_spec.rb")
321
+ end
322
+
323
+ it 'does not create test directory' do
324
+ expect(Dir).not_to exist("#{output_dir}/test")
325
+ end
326
+ end
327
+
328
+ shared_examples 'minitest structure' do |output_dir|
329
+ it 'creates test directory' do
330
+ expect(File).to exist("#{output_dir}/test")
331
+ end
332
+
333
+ it 'creates test_helper.rb' do
334
+ expect(File).to exist("#{output_dir}/helpers/test_helper.rb")
335
+ end
336
+
337
+ it 'test_helper defines TestHelper module' do
338
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
339
+ expect(content).to include('module TestHelper')
340
+ end
341
+
342
+ it 'test_helper requires minitest/autorun' do
343
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
344
+ expect(content).to include("require 'minitest/autorun'")
345
+ end
346
+
347
+ it 'converts source tests into test directory' do
348
+ expect(File).to exist("#{output_dir}/test/test_login.rb")
349
+ end
350
+
351
+ it 'does not create spec directory with converted tests' do
352
+ expect(File).not_to exist("#{output_dir}/spec/login_spec.rb")
353
+ end
354
+
355
+ it 'does not create features directory with step definitions' do
356
+ expect(File).not_to exist("#{output_dir}/features/step_definitions/login_steps.rb")
357
+ end
358
+ end
359
+
360
+ # --- Automation-framework helper wiring ---
361
+
362
+ shared_examples 'rspec wired to selenium' do |output_dir|
363
+ it 'spec_helper includes DriverHelper' do
364
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
365
+ expect(content).to include('DriverHelper')
366
+ end
367
+
368
+ it 'spec_helper requires driver_helper' do
369
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
370
+ expect(content).to include("require_relative 'driver_helper'")
371
+ end
372
+ end
373
+
374
+ shared_examples 'rspec wired to watir' do |output_dir|
375
+ it 'spec_helper includes BrowserHelper' do
376
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
377
+ expect(content).to include('BrowserHelper')
378
+ end
379
+
380
+ it 'spec_helper requires browser_helper' do
381
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
382
+ expect(content).to include("require_relative 'browser_helper'")
383
+ end
384
+ end
385
+
386
+ shared_examples 'rspec wired to capybara' do |output_dir|
387
+ it 'spec_helper includes Capybara::DSL' do
388
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
389
+ expect(content).to include('Capybara::DSL')
390
+ end
391
+
392
+ it 'spec_helper requires capybara_helper' do
393
+ content = File.read("#{output_dir}/helpers/spec_helper.rb")
394
+ expect(content).to include("require_relative 'capybara_helper'")
395
+ end
396
+ end
397
+
398
+ shared_examples 'minitest wired to selenium' do |output_dir|
399
+ it 'test_helper includes DriverHelper' do
400
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
401
+ expect(content).to include('DriverHelper')
402
+ end
403
+
404
+ it 'test_helper requires driver_helper' do
405
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
406
+ expect(content).to include("require_relative 'driver_helper'")
407
+ end
408
+ end
409
+
410
+ shared_examples 'minitest wired to watir' do |output_dir|
411
+ it 'test_helper includes BrowserHelper' do
412
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
413
+ expect(content).to include('BrowserHelper')
414
+ end
415
+
416
+ it 'test_helper requires browser_helper' do
417
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
418
+ expect(content).to include("require_relative 'browser_helper'")
419
+ end
420
+ end
421
+
422
+ shared_examples 'minitest wired to capybara' do |output_dir|
423
+ it 'test_helper includes Capybara::DSL' do
424
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
425
+ expect(content).to include('Capybara::DSL')
426
+ end
427
+
428
+ it 'test_helper requires capybara_helper' do
429
+ content = File.read("#{output_dir}/helpers/test_helper.rb")
430
+ expect(content).to include("require_relative 'capybara_helper'")
431
+ end
432
+ end
433
+
434
+ # === The 9 combinations (3 automations x 3 frameworks) ===
435
+
436
+ # --- Selenium ---
437
+
438
+ context 'selenium + rspec' do
439
+ output = 'tmp_adopt_matrix_selenium_rspec'
440
+ it_behaves_like 'valid adopted project structure', output
441
+ it_behaves_like 'converted pages', output
442
+ it_behaves_like 'merged gems', output
443
+ it_behaves_like 'allure helper', output
444
+ it_behaves_like 'selenium helpers', output
445
+ it_behaves_like 'rspec structure', output
446
+ it_behaves_like 'rspec wired to selenium', output
447
+ end
448
+
449
+ context 'selenium + cucumber' do
450
+ output = 'tmp_adopt_matrix_selenium_cucumber'
451
+ it_behaves_like 'valid adopted project structure', output
452
+ it_behaves_like 'converted pages', output
453
+ it_behaves_like 'merged gems', output
454
+ it_behaves_like 'allure helper', output
455
+ it_behaves_like 'selenium helpers', output
456
+ it_behaves_like 'cucumber structure', output
457
+ end
458
+
459
+ context 'selenium + minitest' do
460
+ output = 'tmp_adopt_matrix_selenium_minitest'
461
+ it_behaves_like 'valid adopted project structure', output
462
+ it_behaves_like 'converted pages', output
463
+ it_behaves_like 'merged gems', output
464
+ it_behaves_like 'allure helper', output
465
+ it_behaves_like 'selenium helpers', output
466
+ it_behaves_like 'minitest structure', output
467
+ it_behaves_like 'minitest wired to selenium', output
468
+ end
469
+
470
+ # --- Capybara ---
471
+
472
+ context 'capybara + rspec' do
473
+ output = 'tmp_adopt_matrix_capybara_rspec'
474
+ it_behaves_like 'valid adopted project structure', output
475
+ it_behaves_like 'converted pages', output
476
+ it_behaves_like 'merged gems', output
477
+ it_behaves_like 'allure helper', output
478
+ it_behaves_like 'capybara helpers', output
479
+ it_behaves_like 'rspec structure', output
480
+ it_behaves_like 'rspec wired to capybara', output
481
+ end
482
+
483
+ context 'capybara + cucumber' do
484
+ output = 'tmp_adopt_matrix_capybara_cucumber'
485
+ it_behaves_like 'valid adopted project structure', output
486
+ it_behaves_like 'converted pages', output
487
+ it_behaves_like 'merged gems', output
488
+ it_behaves_like 'allure helper', output
489
+ it_behaves_like 'capybara helpers', output
490
+ it_behaves_like 'cucumber structure', output
491
+ end
492
+
493
+ context 'capybara + minitest' do
494
+ output = 'tmp_adopt_matrix_capybara_minitest'
495
+ it_behaves_like 'valid adopted project structure', output
496
+ it_behaves_like 'converted pages', output
497
+ it_behaves_like 'merged gems', output
498
+ it_behaves_like 'allure helper', output
499
+ it_behaves_like 'capybara helpers', output
500
+ it_behaves_like 'minitest structure', output
501
+ it_behaves_like 'minitest wired to capybara', output
502
+ end
503
+
504
+ # --- Watir ---
505
+
506
+ context 'watir + rspec' do
507
+ output = 'tmp_adopt_matrix_watir_rspec'
508
+ it_behaves_like 'valid adopted project structure', output
509
+ it_behaves_like 'converted pages', output
510
+ it_behaves_like 'merged gems', output
511
+ it_behaves_like 'allure helper', output
512
+ it_behaves_like 'watir helpers', output
513
+ it_behaves_like 'rspec structure', output
514
+ it_behaves_like 'rspec wired to watir', output
515
+ end
516
+
517
+ context 'watir + cucumber' do
518
+ output = 'tmp_adopt_matrix_watir_cucumber'
519
+ it_behaves_like 'valid adopted project structure', output
520
+ it_behaves_like 'converted pages', output
521
+ it_behaves_like 'merged gems', output
522
+ it_behaves_like 'allure helper', output
523
+ it_behaves_like 'watir helpers', output
524
+ it_behaves_like 'cucumber structure', output
525
+ end
526
+
527
+ context 'watir + minitest' do
528
+ output = 'tmp_adopt_matrix_watir_minitest'
529
+ it_behaves_like 'valid adopted project structure', output
530
+ it_behaves_like 'converted pages', output
531
+ it_behaves_like 'merged gems', output
532
+ it_behaves_like 'allure helper', output
533
+ it_behaves_like 'watir helpers', output
534
+ it_behaves_like 'minitest structure', output
535
+ it_behaves_like 'minitest wired to watir', output
536
+ end
537
+ end