ruby_raider 3.0.1 → 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 (189) 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 +1 -192
  6. data/lib/commands/utility_commands.rb +0 -43
  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/partials/debug_diagnostics.tt +1 -3
  38. data/lib/generators/templates/helpers/partials/debug_start.tt +1 -3
  39. data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
  40. data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
  41. data/lib/generators/templates/helpers/spec_helper.tt +2 -39
  42. data/lib/ruby_raider.rb +2 -50
  43. data/lib/scaffolding/project_detector.rb +2 -9
  44. data/lib/scaffolding/scaffolding.rb +0 -109
  45. data/lib/scaffolding/templates/component.tt +1 -4
  46. data/lib/scaffolding/templates/page_object.tt +0 -10
  47. data/lib/scaffolding/templates/spec.tt +2 -6
  48. data/lib/scaffolding/templates/steps.tt +0 -2
  49. data/lib/utilities/utilities.rb +23 -29
  50. data/lib/version +1 -1
  51. data/sig/commands/scaffolding_commands.rbs +0 -12
  52. data/sig/commands/utility_commands.rbs +0 -6
  53. data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
  54. data/sig/generators/generator.rbs +0 -11
  55. data/sig/generators/helper_generator.rbs +1 -5
  56. data/sig/generators/invoke_generators.rbs +1 -2
  57. data/sig/generators/menu_generator.rbs +2 -6
  58. data/sig/generators/rspec/rspec_generator.rbs +0 -2
  59. data/sig/ruby_raider.rbs +1 -3
  60. data/sig/scaffolding/project_detector.rbs +0 -1
  61. data/sig/scaffolding/scaffolding.rbs +0 -23
  62. data/sig/utilities/utilities.rbs +0 -4
  63. data/spec/commands/raider_commands_spec.rb +0 -61
  64. data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
  65. data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
  66. data/spec/integration/commands/utility_commands_spec.rb +5 -5
  67. data/spec/integration/content/ci_content_spec.rb +6 -61
  68. data/spec/integration/content/common_content_spec.rb +0 -64
  69. data/spec/integration/content/config_content_spec.rb +1 -51
  70. data/spec/integration/content/content_helper.rb +2 -2
  71. data/spec/integration/content/gemfile_content_spec.rb +0 -71
  72. data/spec/integration/content/helper_content_spec.rb +4 -240
  73. data/spec/integration/content/page_content_spec.rb +0 -89
  74. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  75. data/spec/integration/content/test_content_spec.rb +0 -119
  76. data/spec/integration/end_to_end_features_spec.rb +13 -411
  77. data/spec/integration/end_to_end_spec.rb +0 -96
  78. data/spec/integration/generators/axe_addon_spec.rb +2 -52
  79. data/spec/integration/generators/common_generator_spec.rb +1 -13
  80. data/spec/integration/generators/config_features_spec.rb +3 -81
  81. data/spec/integration/generators/debug_helper_spec.rb +0 -20
  82. data/spec/integration/generators/github_generator_spec.rb +5 -15
  83. data/spec/integration/generators/helpers_generator_spec.rb +0 -37
  84. data/spec/integration/scaffolding_e2e_spec.rb +2 -237
  85. data/spec/integration/settings_helper.rb +1 -3
  86. data/spec/integration/spec_helper.rb +6 -11
  87. data/spec/menus/menu_generator_spec.rb +4 -107
  88. data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -26
  89. data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
  90. data/spec/system/selenium_spec.rb +1 -4
  91. data/spec/system/support/system_test_helper.rb +0 -1
  92. data/spec/system/watir_spec.rb +1 -4
  93. data/spec/utilities/headless_config_spec.rb +0 -7
  94. metadata +3 -97
  95. data/lib/adopter/adopt_menu.rb +0 -146
  96. data/lib/adopter/converters/base_converter.rb +0 -84
  97. data/lib/adopter/converters/identity_converter.rb +0 -53
  98. data/lib/adopter/migration_plan.rb +0 -74
  99. data/lib/adopter/migrator.rb +0 -96
  100. data/lib/adopter/plan_builder.rb +0 -275
  101. data/lib/adopter/project_analyzer.rb +0 -252
  102. data/lib/adopter/project_detector.rb +0 -157
  103. data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
  104. data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
  105. data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
  106. data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
  107. data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
  108. data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
  109. data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
  110. data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
  111. data/lib/generators/minitest/minitest_generator.rb +0 -35
  112. data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
  113. data/lib/generators/minitest/templates/performance_test.tt +0 -18
  114. data/lib/generators/minitest/templates/test.tt +0 -64
  115. data/lib/generators/minitest/templates/visual_test.tt +0 -23
  116. data/lib/generators/rspec/templates/performance_spec.tt +0 -18
  117. data/lib/generators/rspec/templates/visual_spec.tt +0 -20
  118. data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
  119. data/lib/generators/templates/helpers/performance_helper.tt +0 -57
  120. data/lib/generators/templates/helpers/visual_helper.tt +0 -58
  121. data/lib/llm/client.rb +0 -79
  122. data/lib/llm/config.rb +0 -57
  123. data/lib/llm/prompts.rb +0 -84
  124. data/lib/llm/provider.rb +0 -27
  125. data/lib/llm/providers/anthropic_provider.rb +0 -43
  126. data/lib/llm/providers/ollama_provider.rb +0 -56
  127. data/lib/llm/providers/openai_provider.rb +0 -42
  128. data/lib/llm/response_parser.rb +0 -67
  129. data/lib/plugin/plugin.rb +0 -111
  130. data/lib/plugin/plugin_exposer.rb +0 -55
  131. data/lib/scaffolding/crud_generator.rb +0 -94
  132. data/lib/scaffolding/dry_run_presenter.rb +0 -16
  133. data/lib/scaffolding/page_introspector.rb +0 -45
  134. data/lib/scaffolding/scaffold_menu.rb +0 -103
  135. data/lib/scaffolding/templates/page_from_url.tt +0 -75
  136. data/lib/scaffolding/templates/spec_from_page.tt +0 -31
  137. data/lib/scaffolding/templates/spec_from_url.tt +0 -46
  138. data/lib/scaffolding/url_analyzer.rb +0 -179
  139. data/sig/adopter/adopt_menu.rbs +0 -25
  140. data/sig/adopter/converters/base_converter.rbs +0 -23
  141. data/sig/adopter/converters/identity_converter.rbs +0 -16
  142. data/sig/adopter/migration_plan.rbs +0 -34
  143. data/sig/adopter/migrator.rbs +0 -21
  144. data/sig/adopter/plan_builder.rbs +0 -38
  145. data/sig/adopter/project_analyzer.rbs +0 -39
  146. data/sig/adopter/project_detector.rbs +0 -26
  147. data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
  148. data/sig/generators/minitest/minitest_generator.rbs +0 -8
  149. data/sig/llm/client.rbs +0 -15
  150. data/sig/llm/config.rbs +0 -20
  151. data/sig/llm/prompts.rbs +0 -8
  152. data/sig/llm/provider.rbs +0 -12
  153. data/sig/llm/providers/anthropic_provider.rbs +0 -16
  154. data/sig/llm/providers/ollama_provider.rbs +0 -18
  155. data/sig/llm/providers/openai_provider.rbs +0 -16
  156. data/sig/llm/response_parser.rbs +0 -13
  157. data/sig/plugin/plugin.rbs +0 -24
  158. data/sig/plugin/plugin_exposer.rbs +0 -20
  159. data/sig/scaffolding/crud_generator.rbs +0 -16
  160. data/sig/scaffolding/dry_run_presenter.rbs +0 -4
  161. data/sig/scaffolding/page_introspector.rbs +0 -14
  162. data/sig/scaffolding/scaffold_menu.rbs +0 -18
  163. data/sig/scaffolding/url_analyzer.rbs +0 -28
  164. data/spec/adopter/adopt_menu_spec.rb +0 -176
  165. data/spec/adopter/converters/identity_converter_spec.rb +0 -145
  166. data/spec/adopter/migration_plan_spec.rb +0 -113
  167. data/spec/adopter/migrator_spec.rb +0 -277
  168. data/spec/adopter/plan_builder_spec.rb +0 -298
  169. data/spec/adopter/project_analyzer_spec.rb +0 -337
  170. data/spec/adopter/project_detector_spec.rb +0 -295
  171. data/spec/generators/generator_spec.rb +0 -23
  172. data/spec/integration/content/reporter_content_spec.rb +0 -236
  173. data/spec/integration/content/skip_flags_content_spec.rb +0 -206
  174. data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
  175. data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
  176. data/spec/integration/generators/minitest_generator_spec.rb +0 -64
  177. data/spec/integration/generators/reporter_spec.rb +0 -159
  178. data/spec/integration/generators/skip_flags_spec.rb +0 -134
  179. data/spec/integration/generators/visual_addon_spec.rb +0 -148
  180. data/spec/llm/client_spec.rb +0 -79
  181. data/spec/llm/config_spec.rb +0 -92
  182. data/spec/llm/prompts_spec.rb +0 -49
  183. data/spec/llm/response_parser_spec.rb +0 -92
  184. data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
  185. data/spec/scaffolding/page_introspector_spec.rb +0 -82
  186. data/spec/scaffolding/url_analyzer_spec.rb +0 -110
  187. data/spec/system/adopt_matrix_spec.rb +0 -537
  188. data/spec/system/adopt_spec.rb +0 -225
  189. data/spec/system/capybara_spec.rb +0 -42
@@ -1,275 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'migration_plan'
4
- require_relative 'converters/identity_converter'
5
-
6
- module Adopter
7
- class PlanBuilder
8
- def initialize(analysis, params)
9
- @analysis = analysis
10
- @params = params
11
- @warnings = []
12
- @manual_actions = []
13
- end
14
-
15
- def build
16
- MigrationPlan.new(
17
- source_path: @params[:source_path],
18
- output_path: @params[:output_path],
19
- target_automation: @params[:target_automation],
20
- target_framework: @params[:target_framework],
21
- ci_platform: @params[:ci_platform],
22
- skeleton_structure: build_skeleton_structure,
23
- converted_pages: plan_page_conversions,
24
- converted_tests: plan_test_conversions,
25
- converted_features: plan_feature_conversions,
26
- converted_steps: plan_step_conversions,
27
- gemfile_additions: @analysis[:custom_gems] || [],
28
- config_overrides: extract_config_overrides,
29
- warnings: @warnings,
30
- manual_actions: @manual_actions
31
- )
32
- end
33
-
34
- private
35
-
36
- def build_skeleton_structure
37
- {
38
- automation: @params[:target_automation],
39
- framework: @params[:target_framework],
40
- name: @params[:output_path],
41
- ci_platform: @params[:ci_platform]
42
- }
43
- end
44
-
45
- def plan_page_conversions
46
- pages = @analysis[:pages] || []
47
- pages.map do |page|
48
- source_file = File.join(@params[:source_path], page[:path])
49
- content = File.read(source_file)
50
- converted = convert_page(content, page)
51
-
52
- ConvertedFile.new(
53
- output_path: raider_page_path(page[:class_name]),
54
- content: converted,
55
- source_file: page[:path],
56
- conversion_notes: page_conversion_notes
57
- )
58
- end
59
- end
60
-
61
- def plan_test_conversions
62
- tests = @analysis[:tests] || []
63
- return [] if target_cucumber?
64
-
65
- tests.reject { |t| t[:type] == :cucumber }.map do |test|
66
- source_file = File.join(@params[:source_path], test[:path])
67
- content = File.read(source_file)
68
- converted = convert_test(content, test)
69
-
70
- ConvertedFile.new(
71
- output_path: raider_test_path(test),
72
- content: converted,
73
- source_file: test[:path],
74
- conversion_notes: test_conversion_notes(test[:type])
75
- )
76
- end
77
- end
78
-
79
- def plan_feature_conversions
80
- return [] unless target_cucumber?
81
-
82
- features = @analysis[:features] || []
83
- features.map do |feature|
84
- source_file = File.join(@params[:source_path], feature[:path])
85
- content = File.read(source_file)
86
-
87
- ConvertedFile.new(
88
- output_path: raider_feature_path(feature[:path]),
89
- content:,
90
- source_file: feature[:path],
91
- conversion_notes: 'Feature file copied as-is'
92
- )
93
- end
94
- end
95
-
96
- def plan_step_conversions
97
- return [] unless target_cucumber?
98
-
99
- steps = @analysis[:step_definitions] || []
100
- steps.map do |step|
101
- source_file = File.join(@params[:source_path], step[:path])
102
- content = File.read(source_file)
103
- converted = convert_step_page_references(content)
104
-
105
- ConvertedFile.new(
106
- output_path: raider_step_path(step[:path]),
107
- content: converted,
108
- source_file: step[:path],
109
- conversion_notes: 'Step definitions with updated page references'
110
- )
111
- end
112
- end
113
-
114
- # --- Page conversion ---
115
-
116
- def convert_page(content, page)
117
- source_dsl = @analysis[:source_dsl]
118
- target = @params[:target_automation]
119
-
120
- if same_automation_dsl?(source_dsl, target)
121
- restructure_page(content)
122
- else
123
- convert_page_dsl(content, page, source_dsl, target)
124
- end
125
- end
126
-
127
- def convert_page_dsl(content, page, source_dsl, target)
128
- converter = find_page_converter(source_dsl, target)
129
- if converter
130
- converter.call(content, page)
131
- else
132
- add_warning("No converter for #{source_dsl} → #{target}. Page '#{page[:class_name]}' copied as-is.")
133
- restructure_page(content)
134
- end
135
- end
136
-
137
- def find_page_converter(source_dsl, target)
138
- key = :"#{normalize_dsl(source_dsl)}_to_#{target}"
139
- page_converters[key]
140
- end
141
-
142
- def page_converters
143
- @page_converters ||= {}
144
- end
145
-
146
- def register_page_converter(key, converter)
147
- page_converters[key] = converter
148
- end
149
-
150
- # --- Test conversion ---
151
-
152
- def convert_test(content, test)
153
- source_framework = test[:type]
154
- target = @params[:target_framework]
155
-
156
- if source_framework.to_s == target
157
- restructure_test(content)
158
- else
159
- convert_test_framework(content, test, source_framework, target)
160
- end
161
- end
162
-
163
- def convert_test_framework(content, test, source_framework, target)
164
- converter = find_test_converter(source_framework, target)
165
- if converter
166
- converter.call(content, test)
167
- else
168
- add_warning("No converter for #{source_framework} → #{target}. Test '#{test[:path]}' copied as-is.")
169
- restructure_test(content)
170
- end
171
- end
172
-
173
- def find_test_converter(source_framework, target)
174
- key = :"#{source_framework}_to_#{target}"
175
- test_converters[key]
176
- end
177
-
178
- def test_converters
179
- @test_converters ||= {}
180
- end
181
-
182
- # --- Restructuring (identity conversion) ---
183
-
184
- def identity_converter
185
- @identity_converter ||= Converters::IdentityConverter.new(@params[:target_automation])
186
- end
187
-
188
- def restructure_page(content)
189
- identity_converter.convert_page(content, {})
190
- end
191
-
192
- def restructure_test(content)
193
- identity_converter.convert_test(content, {})
194
- end
195
-
196
- def convert_step_page_references(content)
197
- identity_converter.convert_step(content)
198
- end
199
-
200
- # --- Path helpers ---
201
-
202
- def raider_page_path(class_name)
203
- filename = class_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
204
- filename = filename.delete_suffix('_page')
205
- "#{@params[:output_path]}/page_objects/pages/#{filename}.rb"
206
- end
207
-
208
- def raider_test_path(test)
209
- basename = File.basename(test[:path], '.rb')
210
- target = @params[:target_framework]
211
-
212
- case target
213
- when 'rspec'
214
- name = basename.delete_prefix('test_').delete_suffix('_spec')
215
- "#{@params[:output_path]}/spec/#{name}_spec.rb"
216
- when 'minitest'
217
- name = basename.delete_prefix('test_').delete_suffix('_test').delete_suffix('_spec')
218
- "#{@params[:output_path]}/test/test_#{name}.rb"
219
- else
220
- "#{@params[:output_path]}/spec/#{basename}.rb"
221
- end
222
- end
223
-
224
- def raider_feature_path(source_path)
225
- filename = File.basename(source_path)
226
- "#{@params[:output_path]}/features/#{filename}"
227
- end
228
-
229
- def raider_step_path(source_path)
230
- filename = File.basename(source_path)
231
- "#{@params[:output_path]}/features/step_definitions/#{filename}"
232
- end
233
-
234
- # --- Config ---
235
-
236
- def extract_config_overrides
237
- overrides = {}
238
- overrides[:browser] = @params[:browser] || @analysis[:browser]
239
- overrides[:url] = @params[:url] || @analysis[:url]
240
- overrides.compact
241
- end
242
-
243
- # --- Helpers ---
244
-
245
- def same_automation_dsl?(source_dsl, target)
246
- normalize_dsl(source_dsl) == target
247
- end
248
-
249
- def normalize_dsl(dsl)
250
- case dsl
251
- when :site_prism then 'capybara'
252
- else dsl.to_s
253
- end
254
- end
255
-
256
- def target_cucumber?
257
- @params[:target_framework] == 'cucumber'
258
- end
259
-
260
- def page_conversion_notes
261
- source = @analysis[:source_dsl]
262
- target = @params[:target_automation]
263
- same_automation_dsl?(source, target) ? 'Restructured to Raider conventions' : "Converted from #{source} to #{target}"
264
- end
265
-
266
- def test_conversion_notes(source_type)
267
- target = @params[:target_framework]
268
- source_type.to_s == target ? 'Restructured to Raider conventions' : "Converted from #{source_type} to #{target}"
269
- end
270
-
271
- def add_warning(message)
272
- @warnings << message
273
- end
274
- end
275
- end
@@ -1,252 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'project_detector'
4
-
5
- module Adopter
6
- class MobileProjectError < StandardError; end
7
-
8
- class ProjectAnalyzer
9
- KNOWN_FRAMEWORK_GEMS = %w[
10
- activesupport allure-cucumber allure-rspec allure-minitest allure-ruby-commons
11
- appium_lib appium_console axe-core-rspec axe-core-selenium capybara
12
- cucumber eyes_selenium eyes_universal minitest minitest-reporters
13
- parallel_split_test parallel_tests rake reek rspec rubocop rubocop-rspec
14
- rubocop-minitest ruby_raider selenium-webdriver site_prism watir
15
- ].freeze
16
-
17
- PAGE_DSL_PATTERNS = {
18
- site_prism: /class\s+\w+\s*<\s*SitePrism::Page/,
19
- capybara: /include\s+Capybara::DSL|\.fill_in\b|\.click_on\b|\.click_button\b/,
20
- selenium: /\.find_element\s*\(|driver\.navigate/,
21
- watir: /browser\.text_field|browser\.button|browser\.element/
22
- }.freeze
23
-
24
- PAGE_CLASS_PATTERNS = [
25
- /class\s+\w+\s*<\s*SitePrism::Page/,
26
- /class\s+\w+\s*<\s*(?:Page|BasePage|AbstractPage)/,
27
- /include\s+Capybara::DSL/,
28
- /\.find_element\s*\(/,
29
- /browser\.text_field|browser\.button/,
30
- /def\s+(?:visit|login|click_|fill_|select_|navigate)/
31
- ].freeze
32
-
33
- TEST_FILE_PATTERNS = {
34
- rspec: { glob: '**/*_spec.rb', marker: /\b(?:describe|context|it)\b/ },
35
- cucumber_feature: { glob: '**/*.feature', marker: /\b(?:Feature|Scenario)\b/ },
36
- cucumber_step: { glob: '**/*_steps.rb', marker: /\b(?:Given|When|Then)\b/ },
37
- minitest: { glob: '**/test_*.rb', marker: /class\s+\w+\s*<\s*Minitest::Test/ },
38
- minitest_alt: { glob: '**/*_test.rb', marker: /class\s+\w+\s*<\s*Minitest::Test/ }
39
- }.freeze
40
-
41
- HELPER_ROLES = {
42
- driver: /module\s+DriverHelper|def\s+(?:driver|create_driver)\b/,
43
- browser: /module\s+BrowserHelper|def\s+(?:browser|create_browser)\b/,
44
- capybara: /Capybara\.configure|Capybara\.register_driver/,
45
- env: /^(?:Before|After)\s+do\b/,
46
- factory: /FactoryBot|ModelFactory|def\s+self\.for\b/,
47
- spec_helper: /RSpec\.configure|Minitest::Test/
48
- }.freeze
49
-
50
- def initialize(source_path, overrides = {})
51
- @source_path = source_path
52
- @overrides = overrides
53
- end
54
-
55
- def analyze
56
- detection = ProjectDetector.detect(@source_path)
57
- validate_web_only!(detection)
58
-
59
- {
60
- **detection,
61
- **@overrides.compact,
62
- pages: discover_pages,
63
- tests: discover_tests,
64
- helpers: discover_helpers,
65
- features: discover_features,
66
- step_definitions: discover_step_definitions,
67
- custom_gems: discover_custom_gems,
68
- source_dsl: detect_page_dsl
69
- }
70
- end
71
-
72
- private
73
-
74
- def validate_web_only!(detection)
75
- return unless detection[:automation] == 'appium'
76
-
77
- raise MobileProjectError,
78
- 'Mobile (Appium) projects cannot be adopted. Only web-based projects are supported.'
79
- end
80
-
81
- def discover_pages
82
- page_path = detect_page_dir
83
- return [] unless page_path
84
-
85
- ruby_files_in(page_path).filter_map do |file|
86
- content = File.read(file)
87
- next unless page_like?(content)
88
-
89
- {
90
- path: relative_path(file),
91
- class_name: extract_class_name(content),
92
- base_class: extract_base_class(content),
93
- methods: extract_public_methods(content)
94
- }
95
- end
96
- end
97
-
98
- def discover_tests
99
- results = []
100
- TEST_FILE_PATTERNS.each do |type, config|
101
- Dir.glob(File.join(@source_path, config[:glob])).each do |file|
102
- content = File.read(file)
103
- next unless content.match?(config[:marker])
104
-
105
- results << {
106
- path: relative_path(file),
107
- type: type.to_s.split('_').first.to_sym,
108
- class_name: extract_class_name(content),
109
- test_methods: extract_test_methods(content, type)
110
- }
111
- end
112
- end
113
- results
114
- end
115
-
116
- def discover_helpers
117
- helper_files = Dir.glob(File.join(@source_path, '**', '*helper*.rb')) +
118
- Dir.glob(File.join(@source_path, '**', 'support', '*.rb')) +
119
- Dir.glob(File.join(@source_path, '**', 'env.rb'))
120
-
121
- helper_files.uniq.first(30).filter_map do |file|
122
- next unless File.file?(file)
123
-
124
- content = File.read(file)
125
- role = detect_helper_role(content)
126
-
127
- {
128
- path: relative_path(file),
129
- role:,
130
- modules_defined: extract_modules(content)
131
- }
132
- end
133
- end
134
-
135
- def discover_features
136
- Dir.glob(File.join(@source_path, '**', '*.feature')).map do |file|
137
- {
138
- path: relative_path(file),
139
- scenarios: count_scenarios(File.read(file))
140
- }
141
- end
142
- end
143
-
144
- def discover_step_definitions
145
- Dir.glob(File.join(@source_path, '**', '*_steps.rb')).map do |file|
146
- {
147
- path: relative_path(file),
148
- steps: count_steps(File.read(file))
149
- }
150
- end
151
- end
152
-
153
- def discover_custom_gems
154
- gemfile = File.join(@source_path, 'Gemfile')
155
- return [] unless File.exist?(gemfile)
156
-
157
- ProjectDetector.send(:parse_gemfile, @source_path).reject do |gem_name|
158
- KNOWN_FRAMEWORK_GEMS.include?(gem_name)
159
- end
160
- end
161
-
162
- def detect_page_dsl
163
- page_files = discover_pages.map { |p| File.join(@source_path, p[:path]) }
164
- return :raw if page_files.empty?
165
-
166
- page_contents = page_files.first(20).map { |f| File.read(f) }.join("\n")
167
-
168
- PAGE_DSL_PATTERNS.each do |dsl, pattern|
169
- return dsl if page_contents.match?(pattern)
170
- end
171
-
172
- :raw
173
- end
174
-
175
- # --- File discovery helpers ---
176
-
177
- def detect_page_dir
178
- detected = ProjectDetector.detect_page_path(@source_path)
179
- return File.join(@source_path, detected) if detected
180
-
181
- # Fallback: scan all .rb files for page-like classes
182
- nil
183
- end
184
-
185
- def ruby_files_in(dir)
186
- Dir.glob(File.join(dir, '**', '*.rb'))
187
- end
188
-
189
- def relative_path(absolute)
190
- absolute.sub("#{@source_path}/", '')
191
- end
192
-
193
- # --- Content analysis helpers ---
194
-
195
- def page_like?(content)
196
- PAGE_CLASS_PATTERNS.any? { |pattern| content.match?(pattern) }
197
- end
198
-
199
- def extract_class_name(content)
200
- match = content.match(/class\s+(\w+)/)
201
- match&.[](1)
202
- end
203
-
204
- def extract_base_class(content)
205
- match = content.match(/class\s+\w+\s*<\s*([\w:]+)/)
206
- match&.[](1)
207
- end
208
-
209
- def extract_public_methods(content)
210
- in_private = false
211
- content.each_line.filter_map do |line|
212
- in_private = true if line.match?(/^\s*private\b/)
213
- next if in_private
214
-
215
- match = line.match(/^\s*def\s+(\w+)/)
216
- match[1] if match && match[1] != 'initialize'
217
- end
218
- end
219
-
220
- def extract_test_methods(content, type)
221
- case type
222
- when :rspec
223
- content.scan(/\bit\s+['"]([^'"]+)['"]/).flatten
224
- when :minitest, :minitest_alt
225
- content.scan(/def\s+(test_\w+)/).flatten
226
- when :cucumber_step
227
- content.scan(/(?:Given|When|Then)\(['"]([^'"]+)['"]/).flatten
228
- else
229
- []
230
- end
231
- end
232
-
233
- def extract_modules(content)
234
- content.scan(/module\s+(\w+)/).flatten
235
- end
236
-
237
- def detect_helper_role(content)
238
- HELPER_ROLES.each do |role, pattern|
239
- return role if content.match?(pattern)
240
- end
241
- :custom
242
- end
243
-
244
- def count_scenarios(content)
245
- content.scan(/^\s*Scenario/).length
246
- end
247
-
248
- def count_steps(content)
249
- content.scan(/^\s*(?:Given|When|Then|And|But)\b/).length
250
- end
251
- end
252
- end
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Adopter
4
- module ProjectDetector
5
- GEM_AUTOMATION_MAP = {
6
- 'site_prism' => 'capybara',
7
- 'capybara' => 'capybara',
8
- 'selenium-webdriver' => 'selenium',
9
- 'watir' => 'watir',
10
- 'appium_lib' => 'appium',
11
- 'eyes_selenium' => 'selenium',
12
- 'axe-core-selenium' => 'selenium',
13
- 'axe-core-rspec' => 'selenium'
14
- }.freeze
15
-
16
- GEM_FRAMEWORK_MAP = {
17
- 'cucumber' => 'cucumber',
18
- 'rspec' => 'rspec',
19
- 'minitest' => 'minitest'
20
- }.freeze
21
-
22
- BROWSERS = %w[chrome firefox safari edge].freeze
23
-
24
- module_function
25
-
26
- def detect(path = '.')
27
- {
28
- automation: detect_automation(path),
29
- framework: detect_framework(path),
30
- page_path: detect_page_path(path),
31
- spec_path: detect_spec_path(path),
32
- feature_path: detect_feature_path(path),
33
- helper_path: detect_helper_path(path),
34
- browser: detect_browser(path),
35
- url: detect_url(path),
36
- ci_platform: detect_ci_platform(path)
37
- }.compact
38
- end
39
-
40
- def detect_automation(path)
41
- gems = parse_gemfile(path)
42
- GEM_AUTOMATION_MAP.each do |gem_name, automation|
43
- return automation if gems.include?(gem_name)
44
- end
45
- detect_automation_from_requires(path)
46
- end
47
-
48
- def detect_framework(path)
49
- gems = parse_gemfile(path)
50
- GEM_FRAMEWORK_MAP.each do |gem_name, framework|
51
- return framework if gems.include?(gem_name)
52
- end
53
- return 'rspec' if Dir.exist?(File.join(path, 'spec'))
54
- return 'cucumber' if Dir.exist?(File.join(path, 'features'))
55
- return 'minitest' if Dir.exist?(File.join(path, 'test'))
56
-
57
- nil
58
- end
59
-
60
- def detect_page_path(path)
61
- candidates = %w[page_objects/pages page_objects pages page]
62
- find_existing_dir(path, candidates)
63
- end
64
-
65
- def detect_spec_path(path)
66
- candidates = %w[spec spec/features spec/tests test tests]
67
- find_existing_dir(path, candidates)
68
- end
69
-
70
- def detect_feature_path(path)
71
- candidates = %w[features features/scenarios]
72
- find_existing_dir(path, candidates)
73
- end
74
-
75
- def detect_helper_path(path)
76
- candidates = %w[helpers support spec/support features/support]
77
- find_existing_dir(path, candidates)
78
- end
79
-
80
- def detect_browser(path)
81
- config_files = helper_and_config_files(path)
82
- config_files.each do |file|
83
- next unless File.exist?(file)
84
-
85
- content = File.read(file)
86
- BROWSERS.each do |browser|
87
- return browser if content.match?(/(?:browser|driver)\s*[:=]\s*[:'"]?#{browser}\b/i)
88
- end
89
- end
90
- nil
91
- end
92
-
93
- def detect_url(path)
94
- config_files = helper_and_config_files(path)
95
- config_files.each do |file|
96
- next unless File.exist?(file)
97
-
98
- content = File.read(file)
99
- match = content.match(%r{(?:base_url|url|app_host)\s*[:=]\s*['"]?(https?://[^\s'"]+)})
100
- return match[1] if match
101
- end
102
- nil
103
- end
104
-
105
- def detect_ci_platform(path)
106
- return 'github' if Dir.exist?(File.join(path, '.github', 'workflows'))
107
- return 'gitlab' if File.exist?(File.join(path, '.gitlab-ci.yml'))
108
-
109
- nil
110
- end
111
-
112
- def parse_gemfile(path)
113
- gemfile = File.join(path, 'Gemfile')
114
- return [] unless File.exist?(gemfile)
115
-
116
- File.readlines(gemfile).filter_map do |line|
117
- match = line.match(/^\s*gem\s+['"]([^'"]+)['"]/)
118
- match[1] if match
119
- end
120
- end
121
-
122
- def detect_automation_from_requires(path)
123
- ruby_files = Dir.glob(File.join(path, '**', '*.rb'))
124
- ruby_files.first(50).each do |file|
125
- content = File.read(file)
126
- return 'capybara' if content.include?("require 'capybara'") || content.include?("require 'site_prism'")
127
- return 'selenium' if content.include?("require 'selenium-webdriver'")
128
- return 'watir' if content.include?("require 'watir'")
129
- return 'appium' if content.include?("require 'appium_lib'")
130
- end
131
- nil
132
- end
133
-
134
- def find_existing_dir(path, candidates)
135
- candidates.each do |candidate|
136
- return candidate if Dir.exist?(File.join(path, candidate))
137
- end
138
- nil
139
- end
140
-
141
- def helper_and_config_files(path)
142
- explicit = [
143
- File.join(path, 'config', 'config.yml'),
144
- File.join(path, 'spec', 'spec_helper.rb'),
145
- File.join(path, 'test', 'test_helper.rb'),
146
- File.join(path, 'features', 'support', 'env.rb'),
147
- File.join(path, 'support', 'env.rb'),
148
- File.join(path, '.env')
149
- ]
150
- helpers = Dir.glob(File.join(path, '**', '*helper*.rb')).first(10)
151
- explicit + helpers
152
- end
153
-
154
- private_class_method :detect_automation_from_requires, :find_existing_dir,
155
- :helper_and_config_files, :parse_gemfile
156
- end
157
- end