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,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../lib/llm/response_parser'
4
-
5
- RSpec.describe Llm::ResponseParser do
6
- describe '.parse_json' do
7
- it 'parses raw JSON' do
8
- result = described_class.parse_json('{"key": "value"}')
9
- expect(result).to eq(key: 'value')
10
- end
11
-
12
- it 'parses JSON from markdown code block' do
13
- response = "```json\n{\"key\": \"value\"}\n```"
14
- result = described_class.parse_json(response)
15
- expect(result).to eq(key: 'value')
16
- end
17
-
18
- it 'extracts JSON from surrounding text' do
19
- response = "Here is the result:\n{\"key\": \"value\"}\nDone."
20
- result = described_class.parse_json(response)
21
- expect(result).to eq(key: 'value')
22
- end
23
-
24
- it 'returns nil for nil input' do
25
- expect(described_class.parse_json(nil)).to be_nil
26
- end
27
-
28
- it 'returns nil for empty input' do
29
- expect(described_class.parse_json('')).to be_nil
30
- end
31
-
32
- it 'returns nil for invalid JSON' do
33
- expect(described_class.parse_json('not json at all')).to be_nil
34
- end
35
-
36
- it 'returns nil for JSON arrays (not hashes)' do
37
- expect(described_class.parse_json('[1, 2, 3]')).to be_nil
38
- end
39
- end
40
-
41
- describe '.extract_elements' do
42
- let(:valid_response) do
43
- {
44
- elements: [
45
- { name: 'email_field', type: 'input', locator: { type: 'id', value: 'email' }, purpose: 'Email input' },
46
- { name: 'submit_btn', type: 'button', locator: { type: 'id', value: 'submit' }, text: 'Submit' }
47
- ]
48
- }.to_json
49
- end
50
-
51
- it 'extracts and normalizes elements' do
52
- elements = described_class.extract_elements(valid_response)
53
- expect(elements.length).to eq(2)
54
- expect(elements.first[:name]).to eq('email_field')
55
- expect(elements.first[:locator][:type]).to eq(:id)
56
- end
57
-
58
- it 'skips elements without required fields' do
59
- response = { elements: [{ name: 'incomplete' }] }.to_json
60
- elements = described_class.extract_elements(response)
61
- expect(elements).to be_empty
62
- end
63
-
64
- it 'returns nil for non-element response' do
65
- expect(described_class.extract_elements('{"other": "data"}')).to be_nil
66
- end
67
-
68
- it 'returns nil for nil response' do
69
- expect(described_class.extract_elements(nil)).to be_nil
70
- end
71
- end
72
-
73
- describe '.extract_scenarios' do
74
- let(:valid_response) do
75
- {
76
- scenarios: [
77
- { method: 'login', description: 'logs in with valid credentials', assertion_hint: 'expect redirect' }
78
- ]
79
- }.to_json
80
- end
81
-
82
- it 'extracts scenarios' do
83
- scenarios = described_class.extract_scenarios(valid_response)
84
- expect(scenarios.length).to eq(1)
85
- expect(scenarios.first[:method]).to eq('login')
86
- end
87
-
88
- it 'returns nil for non-scenario response' do
89
- expect(described_class.extract_scenarios('{"other": "data"}')).to be_nil
90
- end
91
- end
92
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fileutils'
4
- require 'rspec'
5
- require_relative '../../lib/adopter/adopt_menu'
6
-
7
- RSpec.describe Adopter::AdoptMenu do # rubocop:disable RSpec/FilePath
8
- let(:prompt) { instance_double(TTY::Prompt) }
9
- let(:source_dir) { 'tmp_adopt_interactive_source' }
10
- let(:output_dir) { 'tmp_adopt_interactive_output' }
11
-
12
- before do
13
- allow(TTY::Prompt).to receive(:new).and_return(prompt)
14
- allow(prompt).to receive(:say)
15
- allow(prompt).to receive(:warn)
16
- allow(prompt).to receive(:error)
17
-
18
- FileUtils.mkdir_p(source_dir)
19
- File.write("#{source_dir}/Gemfile", "gem 'rspec'\ngem 'selenium-webdriver'\n")
20
- FileUtils.mkdir_p("#{source_dir}/spec")
21
- File.write("#{source_dir}/spec/login_spec.rb", "describe 'Login' do\n it('works') { expect(true).to be true }\nend\n")
22
- end
23
-
24
- after do
25
- FileUtils.rm_rf(source_dir)
26
- FileUtils.rm_rf(output_dir)
27
- end
28
-
29
- describe '#run' do
30
- before do
31
- allow(prompt).to receive(:ask).and_return(source_dir, output_dir)
32
- allow(prompt).to receive(:select)
33
- .with('Select target automation framework:', anything, anything).and_return('Selenium')
34
- allow(prompt).to receive(:select)
35
- .with('Select target test framework:', anything, anything).and_return('Rspec')
36
- allow(prompt).to receive(:select)
37
- .with('Configure CI/CD?', anything, anything).and_return(nil)
38
- end
39
-
40
- it 'shows detection preview' do
41
- described_class.new.run
42
-
43
- expect(prompt).to have_received(:say).with(match(/Detected project settings/))
44
- expect(prompt).to have_received(:say).with(match(/Automation: selenium/))
45
- expect(prompt).to have_received(:say).with(match(/Framework:.*rspec/))
46
- end
47
-
48
- it 'executes adoption and shows results' do
49
- described_class.new.run
50
-
51
- expect(prompt).to have_received(:say).with(match(/Adoption complete/))
52
- expect(prompt).to have_received(:say).with(match(/Pages converted/))
53
- expect(prompt).to have_received(:say).with(match(/Tests converted/))
54
- end
55
-
56
- it 'generates the output project' do
57
- described_class.new.run
58
-
59
- expect(File).to exist("#{output_dir}/Gemfile")
60
- end
61
-
62
- it 'shows output path and bundle install instruction' do
63
- described_class.new.run
64
-
65
- expect(prompt).to have_received(:say).with(match(/Output: #{output_dir}/))
66
- expect(prompt).to have_received(:say).with(match(/bundle install/))
67
- end
68
- end
69
-
70
- describe '#run with mobile source project' do
71
- before do
72
- File.write("#{source_dir}/Gemfile", "gem 'rspec'\ngem 'appium_lib'\n")
73
- allow(prompt).to receive(:ask).and_return(source_dir, output_dir)
74
- allow(prompt).to receive(:select).and_return('Selenium', 'Rspec', nil)
75
- end
76
-
77
- it 'shows MobileProjectError message' do
78
- described_class.new.run
79
-
80
- expect(prompt).to have_received(:error).with(match(/Mobile.*Appium.*cannot be adopted/))
81
- end
82
- end
83
-
84
- describe 'validation constants' do
85
- it 'defines web automations' do
86
- expect(described_class::WEB_AUTOMATIONS).to contain_exactly('selenium', 'capybara', 'watir')
87
- end
88
-
89
- it 'defines test frameworks' do
90
- expect(described_class::TEST_FRAMEWORKS).to contain_exactly('rspec', 'cucumber', 'minitest')
91
- end
92
-
93
- it 'defines CI platforms' do
94
- expect(described_class::CI_PLATFORMS).to contain_exactly('github', 'gitlab')
95
- end
96
- end
97
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fileutils'
4
- require_relative '../../lib/scaffolding/page_introspector'
5
-
6
- RSpec.describe PageIntrospector do
7
- subject(:introspector) { described_class.new(page_file) }
8
-
9
- let(:tmp_dir) { 'tmp_introspector_test' }
10
- let(:page_file) { "#{tmp_dir}/login_page.rb" }
11
-
12
- before do
13
- FileUtils.mkdir_p(tmp_dir)
14
- File.write(page_file, <<~RUBY)
15
- class LoginPage < Page
16
- def url(_page)
17
- '/login'
18
- end
19
-
20
- def login(username, password)
21
- username_field.send_keys username
22
- password_field.send_keys password
23
- login_button.click
24
- end
25
-
26
- def welcome_message
27
- driver.find_element(css: '.welcome').text
28
- end
29
-
30
- private
31
-
32
- def username_field
33
- driver.find_element(id: 'username')
34
- end
35
-
36
- def password_field
37
- driver.find_element(id: 'password')
38
- end
39
-
40
- def login_button
41
- driver.find_element(id: 'login-btn')
42
- end
43
- end
44
- RUBY
45
- end
46
-
47
- after do
48
- FileUtils.rm_rf(tmp_dir)
49
- end
50
-
51
- it 'extracts class name' do
52
- expect(introspector.class_name).to eq('LoginPage')
53
- end
54
-
55
- it 'extracts public methods' do
56
- method_names = introspector.methods.map { |m| m[:name] }
57
- expect(method_names).to include('login')
58
- expect(method_names).to include('welcome_message')
59
- end
60
-
61
- it 'skips url method' do
62
- method_names = introspector.methods.map { |m| m[:name] }
63
- expect(method_names).not_to include('url')
64
- end
65
-
66
- it 'skips private methods' do
67
- method_names = introspector.methods.map { |m| m[:name] }
68
- expect(method_names).not_to include('username_field')
69
- expect(method_names).not_to include('password_field')
70
- expect(method_names).not_to include('login_button')
71
- end
72
-
73
- it 'extracts method parameters' do
74
- login_method = introspector.methods.find { |m| m[:name] == 'login' }
75
- expect(login_method[:params]).to eq(%w[username password])
76
- end
77
-
78
- it 'handles methods with no parameters' do
79
- welcome = introspector.methods.find { |m| m[:name] == 'welcome_message' }
80
- expect(welcome[:params]).to eq([])
81
- end
82
- end
@@ -1,110 +0,0 @@
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