ruby_raider 1.1.4 → 2.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/integration.yml +4 -6
  3. data/.github/workflows/reek.yml +6 -5
  4. data/.github/workflows/release.yml +175 -0
  5. data/.github/workflows/rubocop.yml +7 -6
  6. data/.github/workflows/system_tests.yml +83 -0
  7. data/.gitignore +1 -1
  8. data/.rubocop.yml +24 -0
  9. data/README.md +3 -1
  10. data/RELEASE.md +412 -0
  11. data/RELEASE_QUICK_GUIDE.md +77 -0
  12. data/bin/release +186 -0
  13. data/lib/adopter/adopt_menu.rb +150 -0
  14. data/lib/adopter/converters/base_converter.rb +85 -0
  15. data/lib/adopter/converters/identity_converter.rb +56 -0
  16. data/lib/adopter/migration_plan.rb +75 -0
  17. data/lib/adopter/migrator.rb +96 -0
  18. data/lib/adopter/plan_builder.rb +278 -0
  19. data/lib/adopter/project_analyzer.rb +256 -0
  20. data/lib/adopter/project_detector.rb +159 -0
  21. data/lib/commands/adopt_commands.rb +43 -0
  22. data/lib/generators/automation/templates/account.tt +9 -5
  23. data/lib/generators/automation/templates/appium_caps.tt +60 -6
  24. data/lib/generators/automation/templates/home.tt +4 -4
  25. data/lib/generators/automation/templates/login.tt +61 -4
  26. data/lib/generators/automation/templates/page.tt +13 -7
  27. data/lib/generators/automation/templates/partials/home_page_selector.tt +4 -4
  28. data/lib/generators/automation/templates/partials/initialize_selector.tt +3 -1
  29. data/lib/generators/automation/templates/partials/pdp_page_selector.tt +4 -4
  30. data/lib/generators/automation/templates/partials/visit_method.tt +11 -1
  31. data/lib/generators/automation/templates/pdp.tt +1 -1
  32. data/lib/generators/cucumber/templates/env.tt +6 -4
  33. data/lib/generators/cucumber/templates/partials/capybara_env.tt +20 -0
  34. data/lib/generators/cucumber/templates/partials/capybara_world.tt +6 -0
  35. data/lib/generators/cucumber/templates/partials/mobile_steps.tt +2 -2
  36. data/lib/generators/cucumber/templates/partials/web_steps.tt +4 -3
  37. data/lib/generators/cucumber/templates/steps.tt +2 -2
  38. data/lib/generators/cucumber/templates/world.tt +5 -3
  39. data/lib/generators/generator.rb +14 -2
  40. data/lib/generators/helper_generator.rb +16 -3
  41. data/lib/generators/infrastructure/github_generator.rb +6 -0
  42. data/lib/generators/infrastructure/templates/github.tt +11 -7
  43. data/lib/generators/infrastructure/templates/github_appium.tt +108 -0
  44. data/lib/generators/infrastructure/templates/gitlab.tt +5 -2
  45. data/lib/generators/invoke_generators.rb +1 -0
  46. data/lib/generators/menu_generator.rb +2 -0
  47. data/lib/generators/minitest/minitest_generator.rb +23 -0
  48. data/lib/generators/minitest/templates/test.tt +93 -0
  49. data/lib/generators/rspec/templates/spec.tt +12 -10
  50. data/lib/generators/template_renderer/partial_cache.rb +116 -0
  51. data/lib/generators/template_renderer/partial_resolver.rb +103 -0
  52. data/lib/generators/template_renderer/template_error.rb +50 -0
  53. data/lib/generators/template_renderer.rb +90 -0
  54. data/lib/generators/templates/common/config.tt +2 -2
  55. data/lib/generators/templates/common/gemfile.tt +15 -3
  56. data/lib/generators/templates/common/partials/web_config.tt +1 -1
  57. data/lib/generators/templates/common/read_me.tt +3 -1
  58. data/lib/generators/templates/helpers/allure_helper.tt +2 -2
  59. data/lib/generators/templates/helpers/browser_helper.tt +1 -0
  60. data/lib/generators/templates/helpers/capybara_helper.tt +28 -0
  61. data/lib/generators/templates/helpers/driver_helper.tt +1 -1
  62. data/lib/generators/templates/helpers/partials/allure_imports.tt +3 -1
  63. data/lib/generators/templates/helpers/partials/allure_requirements.tt +3 -1
  64. data/lib/generators/templates/helpers/partials/appium_driver.tt +46 -0
  65. data/lib/generators/templates/helpers/partials/axe_driver.tt +10 -0
  66. data/lib/generators/templates/helpers/partials/browserstack_config.tt +13 -0
  67. data/lib/generators/templates/helpers/partials/driver_and_options.tt +6 -114
  68. data/lib/generators/templates/helpers/partials/quit_driver.tt +3 -1
  69. data/lib/generators/templates/helpers/partials/screenshot.tt +3 -1
  70. data/lib/generators/templates/helpers/partials/selenium_driver.tt +25 -0
  71. data/lib/generators/templates/helpers/spec_helper.tt +17 -4
  72. data/lib/generators/templates/helpers/test_helper.tt +26 -0
  73. data/lib/generators/templates/helpers/visual_spec_helper.tt +1 -1
  74. data/lib/ruby_raider.rb +5 -0
  75. data/lib/version +1 -1
  76. data/spec/adopter/adopt_menu_spec.rb +176 -0
  77. data/spec/adopter/converters/identity_converter_spec.rb +145 -0
  78. data/spec/adopter/migration_plan_spec.rb +113 -0
  79. data/spec/adopter/migrator_spec.rb +277 -0
  80. data/spec/adopter/plan_builder_spec.rb +298 -0
  81. data/spec/adopter/project_analyzer_spec.rb +337 -0
  82. data/spec/adopter/project_detector_spec.rb +295 -0
  83. data/spec/generators/fixtures/templates/test.tt +1 -0
  84. data/spec/generators/fixtures/templates/test_partial.tt +1 -0
  85. data/spec/generators/template_renderer_spec.rb +298 -0
  86. data/spec/integration/commands/scaffolding_commands_spec.rb +2 -2
  87. data/spec/integration/commands/utility_commands_spec.rb +2 -2
  88. data/spec/integration/end_to_end_spec.rb +325 -0
  89. data/spec/integration/generators/automation_generator_spec.rb +11 -11
  90. data/spec/integration/generators/common_generator_spec.rb +40 -40
  91. data/spec/integration/generators/cucumber_generator_spec.rb +7 -7
  92. data/spec/integration/generators/github_generator_spec.rb +8 -8
  93. data/spec/integration/generators/gitlab_generator_spec.rb +8 -8
  94. data/spec/integration/generators/helpers_generator_spec.rb +73 -35
  95. data/spec/integration/generators/minitest_generator_spec.rb +70 -0
  96. data/spec/integration/generators/rspec_generator_spec.rb +7 -7
  97. data/spec/integration/settings_helper.rb +1 -1
  98. data/spec/integration/spec_helper.rb +20 -2
  99. data/spec/system/capybara_spec.rb +42 -0
  100. data/spec/system/selenium_spec.rb +19 -17
  101. data/spec/system/support/system_test_helper.rb +35 -0
  102. data/spec/system/watir_spec.rb +19 -17
  103. metadata +46 -16
  104. data/.github/workflows/push_gem.yml +0 -37
  105. data/.github/workflows/selenium.yml +0 -22
  106. data/.github/workflows/watir.yml +0 -22
  107. data/lib/generators/automation/templates/partials/android_caps.tt +0 -17
  108. data/lib/generators/automation/templates/partials/cross_platform_caps.tt +0 -25
  109. data/lib/generators/automation/templates/partials/ios_caps.tt +0 -18
  110. data/lib/generators/automation/templates/partials/selenium_account.tt +0 -9
  111. data/lib/generators/automation/templates/partials/selenium_login.tt +0 -34
  112. data/lib/generators/automation/templates/partials/watir_account.tt +0 -7
  113. data/lib/generators/automation/templates/partials/watir_login.tt +0 -32
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'rspec'
5
+ require_relative '../../lib/adopter/migration_plan'
6
+ require_relative '../../lib/adopter/migrator'
7
+
8
+ RSpec.describe Adopter::Migrator do
9
+ let(:output_dir) { 'tmp_migrator_test' }
10
+
11
+ after { FileUtils.rm_rf(output_dir) }
12
+
13
+ describe '#execute' do
14
+ context 'with skeleton generation' do
15
+ let(:plan) do
16
+ Adopter::MigrationPlan.new(
17
+ source_path: 'dummy_source',
18
+ output_path: output_dir,
19
+ target_automation: 'selenium',
20
+ target_framework: 'rspec',
21
+ ci_platform: 'github',
22
+ skeleton_structure: {
23
+ automation: 'selenium',
24
+ framework: 'rspec',
25
+ name: output_dir,
26
+ ci_platform: 'github'
27
+ }
28
+ )
29
+ end
30
+
31
+ it 'generates a raider project skeleton' do
32
+ described_class.new(plan).execute
33
+
34
+ expect(File).to exist("#{output_dir}/Gemfile")
35
+ expect(File).to exist("#{output_dir}/Rakefile")
36
+ expect(File).to exist("#{output_dir}/page_objects/pages")
37
+ expect(File).to exist("#{output_dir}/spec")
38
+ end
39
+ end
40
+
41
+ context 'writing converted pages' do
42
+ let(:plan) do
43
+ Adopter::MigrationPlan.new(
44
+ source_path: 'dummy_source',
45
+ output_path: output_dir,
46
+ target_automation: 'selenium',
47
+ target_framework: 'rspec',
48
+ skeleton_structure: {
49
+ automation: 'selenium',
50
+ framework: 'rspec',
51
+ name: output_dir
52
+ },
53
+ converted_pages: [
54
+ Adopter::ConvertedFile.new(
55
+ output_path: "#{output_dir}/page_objects/pages/login.rb",
56
+ content: "# frozen_string_literal: true\n\nclass LoginPage < Page\nend\n",
57
+ source_file: 'pages/login_page.rb',
58
+ conversion_notes: 'Restructured'
59
+ )
60
+ ]
61
+ )
62
+ end
63
+
64
+ it 'writes converted page files over the skeleton' do
65
+ results = described_class.new(plan).execute
66
+
67
+ expect(results[:pages]).to eq(1)
68
+ content = File.read("#{output_dir}/page_objects/pages/login.rb")
69
+ expect(content).to include('class LoginPage < Page')
70
+ end
71
+ end
72
+
73
+ context 'writing converted tests' do
74
+ let(:plan) do
75
+ Adopter::MigrationPlan.new(
76
+ source_path: 'dummy_source',
77
+ output_path: output_dir,
78
+ target_automation: 'selenium',
79
+ target_framework: 'rspec',
80
+ skeleton_structure: {
81
+ automation: 'selenium',
82
+ framework: 'rspec',
83
+ name: output_dir
84
+ },
85
+ converted_tests: [
86
+ Adopter::ConvertedFile.new(
87
+ output_path: "#{output_dir}/spec/login_spec.rb",
88
+ content: "# frozen_string_literal: true\n\ndescribe 'Login' do\nend\n",
89
+ source_file: 'spec/login_spec.rb',
90
+ conversion_notes: 'Restructured'
91
+ )
92
+ ]
93
+ )
94
+ end
95
+
96
+ it 'writes converted test files' do
97
+ results = described_class.new(plan).execute
98
+
99
+ expect(results[:tests]).to eq(1)
100
+ content = File.read("#{output_dir}/spec/login_spec.rb")
101
+ expect(content).to include("describe 'Login'")
102
+ end
103
+ end
104
+
105
+ context 'writing cucumber features and steps' do
106
+ let(:plan) do
107
+ Adopter::MigrationPlan.new(
108
+ source_path: 'dummy_source',
109
+ output_path: output_dir,
110
+ target_automation: 'selenium',
111
+ target_framework: 'cucumber',
112
+ skeleton_structure: {
113
+ automation: 'selenium',
114
+ framework: 'cucumber',
115
+ name: output_dir
116
+ },
117
+ converted_features: [
118
+ Adopter::ConvertedFile.new(
119
+ output_path: "#{output_dir}/features/login.feature",
120
+ content: "Feature: Login\n Scenario: Valid login\n",
121
+ source_file: 'features/login.feature',
122
+ conversion_notes: 'Copied as-is'
123
+ )
124
+ ],
125
+ converted_steps: [
126
+ Adopter::ConvertedFile.new(
127
+ output_path: "#{output_dir}/features/step_definitions/login_steps.rb",
128
+ content: "Given('I am on the login page') do\nend\n",
129
+ source_file: 'features/step_definitions/login_steps.rb',
130
+ conversion_notes: 'Updated page references'
131
+ )
132
+ ]
133
+ )
134
+ end
135
+
136
+ it 'writes feature and step files' do
137
+ results = described_class.new(plan).execute
138
+
139
+ expect(results[:features]).to eq(1)
140
+ expect(results[:steps]).to eq(1)
141
+ expect(File).to exist("#{output_dir}/features/login.feature")
142
+ expect(File).to exist("#{output_dir}/features/step_definitions/login_steps.rb")
143
+ end
144
+ end
145
+
146
+ context 'merging custom gems into Gemfile' do
147
+ let(:plan) do
148
+ Adopter::MigrationPlan.new(
149
+ source_path: 'dummy_source',
150
+ output_path: output_dir,
151
+ target_automation: 'selenium',
152
+ target_framework: 'rspec',
153
+ skeleton_structure: {
154
+ automation: 'selenium',
155
+ framework: 'rspec',
156
+ name: output_dir
157
+ },
158
+ gemfile_additions: %w[faker httparty]
159
+ )
160
+ end
161
+
162
+ it 'appends custom gems to the generated Gemfile' do
163
+ described_class.new(plan).execute
164
+
165
+ gemfile = File.read("#{output_dir}/Gemfile")
166
+ expect(gemfile).to include("gem 'faker'")
167
+ expect(gemfile).to include("gem 'httparty'")
168
+ expect(gemfile).to include('# Gems from source project')
169
+ end
170
+
171
+ it 'does not duplicate gems already in the Gemfile' do
172
+ described_class.new(plan).execute
173
+
174
+ # Selenium Gemfile already has 'rspec' — adding it again should be skipped
175
+ plan2 = Adopter::MigrationPlan.new(
176
+ source_path: 'dummy_source',
177
+ output_path: output_dir,
178
+ target_automation: 'selenium',
179
+ target_framework: 'rspec',
180
+ skeleton_structure: {
181
+ automation: 'selenium',
182
+ framework: 'rspec',
183
+ name: output_dir
184
+ },
185
+ gemfile_additions: %w[faker new_gem]
186
+ )
187
+ described_class.new(plan2).execute
188
+
189
+ gemfile = File.read("#{output_dir}/Gemfile")
190
+ expect(gemfile.scan("gem 'faker'").length).to eq(1)
191
+ expect(gemfile).to include("gem 'new_gem'")
192
+ end
193
+ end
194
+
195
+ context 'applying config overrides' do
196
+ let(:plan) do
197
+ Adopter::MigrationPlan.new(
198
+ source_path: 'dummy_source',
199
+ output_path: output_dir,
200
+ target_automation: 'selenium',
201
+ target_framework: 'rspec',
202
+ skeleton_structure: {
203
+ automation: 'selenium',
204
+ framework: 'rspec',
205
+ name: output_dir
206
+ },
207
+ config_overrides: { browser: 'firefox', url: 'https://example.com' }
208
+ )
209
+ end
210
+
211
+ it 'updates config.yml with overridden values' do
212
+ described_class.new(plan).execute
213
+
214
+ config = YAML.safe_load(File.read("#{output_dir}/config/config.yml"), permitted_classes: [Symbol])
215
+ expect(config['browser']).to eq('firefox')
216
+ expect(config['url']).to eq('https://example.com')
217
+ end
218
+ end
219
+
220
+ context 'error handling' do
221
+ let(:plan) do
222
+ Adopter::MigrationPlan.new(
223
+ source_path: 'dummy_source',
224
+ output_path: output_dir,
225
+ target_automation: 'selenium',
226
+ target_framework: 'rspec',
227
+ skeleton_structure: {
228
+ automation: 'selenium',
229
+ framework: 'rspec',
230
+ name: output_dir
231
+ },
232
+ converted_pages: [
233
+ Adopter::ConvertedFile.new(
234
+ output_path: "/dev/null/impossible/path/file.rb",
235
+ content: "content",
236
+ source_file: 'pages/bad.rb',
237
+ conversion_notes: 'Will fail'
238
+ )
239
+ ]
240
+ )
241
+ end
242
+
243
+ it 'captures write errors without stopping execution' do
244
+ results = described_class.new(plan).execute
245
+
246
+ expect(results[:errors]).not_to be_empty
247
+ expect(results[:errors].first).to include('Failed to write')
248
+ end
249
+ end
250
+
251
+ context 'results tracking' do
252
+ let(:plan) do
253
+ Adopter::MigrationPlan.new(
254
+ source_path: 'dummy_source',
255
+ output_path: output_dir,
256
+ target_automation: 'selenium',
257
+ target_framework: 'rspec',
258
+ skeleton_structure: {
259
+ automation: 'selenium',
260
+ framework: 'rspec',
261
+ name: output_dir
262
+ }
263
+ )
264
+ end
265
+
266
+ it 'returns zero counts when no files to convert' do
267
+ results = described_class.new(plan).execute
268
+
269
+ expect(results[:pages]).to eq(0)
270
+ expect(results[:tests]).to eq(0)
271
+ expect(results[:features]).to eq(0)
272
+ expect(results[:steps]).to eq(0)
273
+ expect(results[:errors]).to be_empty
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,298 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'rspec'
5
+ require_relative '../../lib/adopter/project_analyzer'
6
+ require_relative '../../lib/adopter/plan_builder'
7
+
8
+ RSpec.describe Adopter::PlanBuilder do
9
+ let(:project_dir) { 'tmp_plan_builder_project' }
10
+
11
+ before { FileUtils.mkdir_p(project_dir) }
12
+
13
+ after { FileUtils.rm_rf(project_dir) }
14
+
15
+ describe '#build' do
16
+ context 'with a selenium + rspec project converting to same' do
17
+ let(:params) do
18
+ {
19
+ source_path: project_dir,
20
+ output_path: 'my_raider_project',
21
+ target_automation: 'selenium',
22
+ target_framework: 'rspec',
23
+ ci_platform: 'github'
24
+ }
25
+ end
26
+
27
+ before do
28
+ File.write("#{project_dir}/Gemfile", <<~GEMFILE)
29
+ gem 'rspec'
30
+ gem 'selenium-webdriver'
31
+ gem 'faker'
32
+ GEMFILE
33
+ FileUtils.mkdir_p("#{project_dir}/pages")
34
+ FileUtils.mkdir_p("#{project_dir}/spec")
35
+
36
+ File.write("#{project_dir}/pages/login_page.rb", <<~RUBY)
37
+ class LoginPage < BasePage
38
+ def login(user, pass)
39
+ driver.find_element(id: 'user').send_keys user
40
+ end
41
+ end
42
+ RUBY
43
+
44
+ File.write("#{project_dir}/spec/login_spec.rb", <<~RUBY)
45
+ describe 'Login' do
46
+ it 'can log in' do
47
+ expect(result).to eq 'ok'
48
+ end
49
+ end
50
+ RUBY
51
+ end
52
+
53
+ it 'builds a valid migration plan' do
54
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
55
+ plan = described_class.new(analysis, params).build
56
+
57
+ expect(plan).to be_a(Adopter::MigrationPlan)
58
+ expect(plan.source_path).to eq(project_dir)
59
+ expect(plan.output_path).to eq('my_raider_project')
60
+ expect(plan.target_automation).to eq('selenium')
61
+ expect(plan.target_framework).to eq('rspec')
62
+ end
63
+
64
+ it 'builds correct skeleton structure' do
65
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
66
+ plan = described_class.new(analysis, params).build
67
+
68
+ expect(plan.skeleton_structure).to eq(
69
+ automation: 'selenium',
70
+ framework: 'rspec',
71
+ name: 'my_raider_project',
72
+ ci_platform: 'github'
73
+ )
74
+ end
75
+
76
+ it 'converts pages with raider paths' do
77
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
78
+ plan = described_class.new(analysis, params).build
79
+
80
+ expect(plan.converted_pages.length).to eq(1)
81
+ page = plan.converted_pages.first
82
+ expect(page.output_path).to eq('my_raider_project/page_objects/pages/login.rb')
83
+ expect(page.source_file).to eq('pages/login_page.rb')
84
+ end
85
+
86
+ it 'converts tests with raider paths' do
87
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
88
+ plan = described_class.new(analysis, params).build
89
+
90
+ expect(plan.converted_tests.length).to eq(1)
91
+ test = plan.converted_tests.first
92
+ expect(test.output_path).to eq('my_raider_project/spec/login_spec.rb')
93
+ end
94
+
95
+ it 'identifies custom gems' do
96
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
97
+ plan = described_class.new(analysis, params).build
98
+
99
+ expect(plan.gemfile_additions).to eq(['faker'])
100
+ end
101
+
102
+ it 'adds frozen_string_literal when missing' do
103
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
104
+ plan = described_class.new(analysis, params).build
105
+
106
+ page_content = plan.converted_pages.first.content
107
+ expect(page_content).to include('frozen_string_literal: true')
108
+ end
109
+ end
110
+
111
+ context 'converting to minitest output paths' do
112
+ let(:params) do
113
+ {
114
+ source_path: project_dir,
115
+ output_path: 'output',
116
+ target_automation: 'selenium',
117
+ target_framework: 'minitest'
118
+ }
119
+ end
120
+
121
+ before do
122
+ File.write("#{project_dir}/Gemfile", "gem 'rspec'\ngem 'selenium-webdriver'\n")
123
+ FileUtils.mkdir_p("#{project_dir}/spec")
124
+
125
+ File.write("#{project_dir}/spec/login_spec.rb", <<~RUBY)
126
+ describe 'Login' do
127
+ it 'works' do
128
+ expect(true).to be true
129
+ end
130
+ end
131
+ RUBY
132
+ end
133
+
134
+ it 'generates minitest-style output paths' do
135
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
136
+ plan = described_class.new(analysis, params).build
137
+
138
+ test = plan.converted_tests.first
139
+ expect(test.output_path).to eq('output/test/test_login.rb')
140
+ end
141
+ end
142
+
143
+ context 'converting to cucumber' do
144
+ let(:params) do
145
+ {
146
+ source_path: project_dir,
147
+ output_path: 'output',
148
+ target_automation: 'selenium',
149
+ target_framework: 'cucumber'
150
+ }
151
+ end
152
+
153
+ before do
154
+ File.write("#{project_dir}/Gemfile", "gem 'cucumber'\ngem 'selenium-webdriver'\n")
155
+ FileUtils.mkdir_p("#{project_dir}/features/step_definitions")
156
+
157
+ File.write("#{project_dir}/features/login.feature", <<~GHERKIN)
158
+ Feature: Login
159
+ Scenario: Valid login
160
+ Given I am on the login page
161
+ GHERKIN
162
+
163
+ File.write("#{project_dir}/features/step_definitions/login_steps.rb", <<~RUBY)
164
+ Given('I am on the login page') do
165
+ @page = LoginPage.new(driver)
166
+ end
167
+ RUBY
168
+ end
169
+
170
+ it 'plans feature file copies' do
171
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
172
+ plan = described_class.new(analysis, params).build
173
+
174
+ expect(plan.converted_features.length).to eq(1)
175
+ expect(plan.converted_features.first.output_path).to eq('output/features/login.feature')
176
+ end
177
+
178
+ it 'plans step definition conversions' do
179
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
180
+ plan = described_class.new(analysis, params).build
181
+
182
+ expect(plan.converted_steps.length).to eq(1)
183
+ expect(plan.converted_steps.first.output_path).to eq('output/features/step_definitions/login_steps.rb')
184
+ end
185
+
186
+ it 'skips rspec-style test conversion for cucumber target' do
187
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
188
+ plan = described_class.new(analysis, params).build
189
+
190
+ expect(plan.converted_tests).to be_empty
191
+ end
192
+ end
193
+
194
+ context 'converting capybara to selenium' do
195
+ let(:params) do
196
+ {
197
+ source_path: project_dir,
198
+ output_path: 'output',
199
+ target_automation: 'selenium',
200
+ target_framework: 'rspec'
201
+ }
202
+ end
203
+
204
+ before do
205
+ File.write("#{project_dir}/Gemfile", "gem 'capybara'\ngem 'rspec'\n")
206
+ FileUtils.mkdir_p("#{project_dir}/pages")
207
+
208
+ File.write("#{project_dir}/pages/login_page.rb", <<~RUBY)
209
+ class LoginPage < BasePage
210
+ include Capybara::DSL
211
+ def login(email, password)
212
+ fill_in 'email', with: email
213
+ click_button 'Login'
214
+ end
215
+ end
216
+ RUBY
217
+ end
218
+
219
+ it 'adds a warning when no converter is registered' do
220
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
221
+ plan = described_class.new(analysis, params).build
222
+
223
+ expect(plan.warnings).to include(match(/No converter for capybara.*selenium/))
224
+ end
225
+
226
+ it 'still includes the page with identity conversion' do
227
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
228
+ plan = described_class.new(analysis, params).build
229
+
230
+ expect(plan.converted_pages.length).to eq(1)
231
+ end
232
+ end
233
+
234
+ context 'with config overrides' do
235
+ let(:params) do
236
+ {
237
+ source_path: project_dir,
238
+ output_path: 'output',
239
+ target_automation: 'selenium',
240
+ target_framework: 'rspec',
241
+ browser: 'firefox',
242
+ url: 'https://override.com'
243
+ }
244
+ end
245
+
246
+ before do
247
+ File.write("#{project_dir}/Gemfile", "gem 'rspec'\ngem 'selenium-webdriver'\n")
248
+ FileUtils.mkdir_p("#{project_dir}/config")
249
+ File.write("#{project_dir}/config/config.yml", "browser: chrome\nurl: 'https://original.com'\n")
250
+ end
251
+
252
+ it 'uses parameter overrides over detected values' do
253
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
254
+ plan = described_class.new(analysis, params).build
255
+
256
+ expect(plan.config_overrides[:browser]).to eq('firefox')
257
+ expect(plan.config_overrides[:url]).to eq('https://override.com')
258
+ end
259
+ end
260
+
261
+ context 'converting to capybara updates page instantiation' do
262
+ let(:params) do
263
+ {
264
+ source_path: project_dir,
265
+ output_path: 'output',
266
+ target_automation: 'capybara',
267
+ target_framework: 'cucumber'
268
+ }
269
+ end
270
+
271
+ before do
272
+ File.write("#{project_dir}/Gemfile", "gem 'cucumber'\ngem 'selenium-webdriver'\n")
273
+ FileUtils.mkdir_p("#{project_dir}/features/step_definitions")
274
+
275
+ File.write("#{project_dir}/features/login.feature", <<~GHERKIN)
276
+ Feature: Login
277
+ Scenario: Test
278
+ Given I am on the login page
279
+ GHERKIN
280
+
281
+ File.write("#{project_dir}/features/step_definitions/login_steps.rb", <<~RUBY)
282
+ Given('I am on the login page') do
283
+ @page = LoginPage.new(driver)
284
+ end
285
+ RUBY
286
+ end
287
+
288
+ it 'removes driver argument from page instantiation in steps' do
289
+ analysis = Adopter::ProjectAnalyzer.new(project_dir).analyze
290
+ plan = described_class.new(analysis, params).build
291
+
292
+ step_content = plan.converted_steps.first.content
293
+ expect(step_content).to include('LoginPage.new')
294
+ expect(step_content).not_to include('LoginPage.new(driver)')
295
+ end
296
+ end
297
+ end
298
+ end