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,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rspec'
4
- require 'tmpdir'
5
- require_relative '../../helpers/allure_helper'
6
- require_relative '../../helpers/capybara_helper'
7
- require_relative '../../helpers/video_helper'
8
- require_relative '../../helpers/debug_helper'
9
-
10
- CapybaraHelper.configure
11
-
12
- Before do
13
- viewport = YAML.load_file('config/config.yml')['viewport']
14
- if viewport
15
- Capybara.current_session.driver.browser.manage.window.resize_to(viewport['width'], viewport['height'])
16
- else
17
- Capybara.current_session.driver.browser.manage.window.maximize
18
- end
19
- @video_recorder = VideoHelper.recorder_for(Capybara.current_session.driver)
20
- @video_recorder&.start(self.class.to_s)
21
- DebugHelper.enable_network_logging(Capybara.current_session.driver)
22
- @debug_action_logger = DebugHelper.action_logger_for(self.class.to_s)
23
- end
24
-
25
- After do |scenario|
26
- if scenario.failed?
27
- DebugHelper.capture_failure_diagnostics(Capybara.current_session.driver, scenario.name, exception: scenario.exception)
28
- DebugHelper.capture_network_logs(DebugHelper.resolve_selenium_driver(Capybara.current_session.driver), DebugHelper.sanitize(scenario.name))
29
- end
30
- @debug_action_logger&.close
31
- video_file = @video_recorder&.stop
32
- AllureHelper.add_video(scenario.name, video_file)
33
- Dir.mktmpdir do |temp_folder|
34
- screenshot = Capybara.page.save_screenshot("#{temp_folder}/#{scenario.name}.png")
35
- AllureHelper.add_screenshot(scenario.name, screenshot)
36
- end
37
- Capybara.reset_sessions!
38
- end
@@ -1,6 +0,0 @@
1
- require 'capybara/dsl'
2
- require_relative '../../helpers/capybara_helper'
3
- require_relative '../../models/user_factory'
4
-
5
- CapybaraHelper.configure
6
- World(Capybara::DSL)
@@ -1,5 +0,0 @@
1
- Feature: Performance Audit
2
-
3
- Scenario: Page meets performance threshold
4
- Given I audit the home page performance
5
- Then the performance score should be above the threshold
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../helpers/performance_helper'
4
-
5
- include PerformanceHelper
6
-
7
- Given('I audit the home page performance') do
8
- @result = assert_performance_above('https://raider-test-site.onrender.com/', threshold: 0.7)
9
- end
10
-
11
- Then('the performance score should be above the threshold') do
12
- expect(@result[:status]).not_to eq(:error),
13
- "Lighthouse error: #{@result[:message]}"
14
-
15
- expect(@result[:passed]).to be(true),
16
- "Performance below threshold (#{@result[:threshold]}): #{@result[:scores]}"
17
- end
@@ -1,5 +0,0 @@
1
- Feature: Visual Regression
2
-
3
- Scenario: Login page matches baseline
4
- Given I'm on the login page
5
- Then the page should match the visual baseline "login_page"
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../helpers/visual_helper'
4
- require_relative '../../page_objects/pages/login'
5
-
6
- include VisualHelper
7
-
8
- Given("I'm on the login page") do
9
- @login_page = <% if capybara? %>Login.new<% elsif watir? %>Login.new(browser)<% else %>Login.new(driver)<% end %>
10
- @login_page.<% if capybara? %>visit_page<% else %>visit<% end %>
11
- end
12
-
13
- Then('the page should match the visual baseline {string}') do |baseline_name|
14
- screenshot_path = <% if capybara? %>page.save_screenshot("tmp/visual_#{baseline_name}.png")<% elsif watir? %>browser.screenshot.save("tmp/visual_#{baseline_name}.png")<% else %>driver.save_screenshot("tmp/visual_#{baseline_name}.png")<% end %>
15
- result = compare_screenshot(baseline_name, screenshot_path)
16
-
17
- expect(result[:status]).not_to eq(:mismatch),
18
- "Visual diff: #{(result[:diff].to_f * 100).round(2)}% difference. See: #{result[:diff_path]}"
19
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../generator'
4
-
5
- class GitlabGenerator < Generator
6
- def generate_gitlab_file
7
- return unless web?
8
-
9
- template('gitlab.tt', "#{name}/gitlab-ci.yml")
10
- end
11
- end
@@ -1,46 +0,0 @@
1
- stages:
2
- - setup
3
- - test
4
- - report
5
-
6
- variables:
7
- RUBY_VERSION: "<%= ruby_version %>.0"
8
- HEADLESS: "true"
9
-
10
- setup_ruby:
11
- stage: setup
12
- image: ruby:${RUBY_VERSION}
13
- script:
14
- - bundle install --jobs $(nproc) --retry 3
15
- cache:
16
- paths:
17
- - vendor/bundle
18
-
19
- run_tests:
20
- stage: test
21
- image: ruby:${RUBY_VERSION}
22
- before_script:
23
- - apt-get update -qq && apt-get install -y -qq chromium chromium-driver
24
- - mkdir -p allure-results
25
- script:
26
- - <%- if framework == 'cucumber' -%>bundle exec cucumber features --format pretty<%- elsif minitest? -%>bundle exec ruby -Itest test/test_login_page.rb<%- else -%>bundle exec rspec spec --format documentation<%- end %>
27
- artifacts:
28
- paths:
29
- - allure-results/
30
- when: always
31
-
32
- pages:
33
- stage: report
34
- image: alpine/git
35
- dependencies:
36
- - run_tests
37
- script:
38
- - apk add --no-cache curl unzip
39
- - curl -o allure.zip -L https://github.com/allure-framework/allure2/releases/latest/download/allure-commandline.zip
40
- - unzip allure.zip -d /tmp/
41
- - /tmp/allure-*/bin/allure generate allure-results --clean -o public
42
- artifacts:
43
- paths:
44
- - public
45
- only:
46
- - main
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../generator'
4
-
5
- class MinitestGenerator < Generator
6
- def generate_login_test
7
- return unless web?
8
-
9
- template('test.tt', "#{name}/test/test_login_page.rb")
10
- end
11
-
12
- def generate_pdp_test
13
- return unless mobile?
14
-
15
- template('test.tt', "#{name}/test/test_pdp_page.rb")
16
- end
17
-
18
- def generate_visual_test
19
- return unless visual_addon? && web?
20
-
21
- template('visual_test.tt', "#{name}/test/test_visual.rb")
22
- end
23
-
24
- def generate_accessibility_test
25
- return unless axe_addon? && web?
26
-
27
- template('accessibility_test.tt', "#{name}/test/test_accessibility.rb")
28
- end
29
-
30
- def generate_performance_test
31
- return unless lighthouse_addon? && web?
32
-
33
- template('performance_test.tt', "#{name}/test/test_performance.rb")
34
- end
35
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../helpers/test_helper'
4
-
5
- class TestAccessibility < Minitest::Test
6
- def setup
7
- super
8
- <% if capybara? -%>
9
- visit '/'
10
- <% elsif watir? -%>
11
- browser.goto 'https://raider-test-site.onrender.com/'
12
- <% else -%>
13
- driver.get 'https://raider-test-site.onrender.com/'
14
- <% end -%>
15
- end
16
-
17
- def test_no_accessibility_violations_on_page
18
- <% if capybara? -%>
19
- assert_axe_clean page
20
- <% elsif watir? -%>
21
- assert_axe_clean browser.driver
22
- <% else -%>
23
- assert_axe_clean driver
24
- <% end -%>
25
- end
26
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../helpers/test_helper'
4
- require_relative '../helpers/performance_helper'
5
-
6
- class TestPerformance < Minitest::Test
7
- include PerformanceHelper
8
-
9
- def test_meets_performance_threshold
10
- result = assert_performance_above('https://raider-test-site.onrender.com/', threshold: 0.7)
11
-
12
- refute_equal :error, result[:status],
13
- "Lighthouse error: #{result[:message]}"
14
-
15
- assert result[:passed],
16
- "Performance below threshold (#{result[:threshold]}): #{result[:scores]}"
17
- end
18
- end
@@ -1,64 +0,0 @@
1
- <%- if selenium_based? || watir? || capybara? -%>
2
- # frozen_string_literal: true
3
-
4
- require_relative '../helpers/test_helper'
5
- require_relative '../models/model_factory'
6
- require_relative '../page_objects/pages/account'
7
- require_relative '../page_objects/pages/login'
8
-
9
- # Uncomment for interactive debugging:
10
- # require 'debug' # Ruby's built-in debugger (binding.break)
11
- # require 'pry' # Pry debugger (binding.pry)
12
-
13
- <%- if selenium_based? || watir? || capybara? -%>
14
- class TestLogin < Minitest::Test
15
- def setup
16
- @user = ModelFactory.for('users')['registered user']
17
- @login_page = <% if capybara? -%>Login.new<% elsif watir? -%>Login.new(browser)<% else -%>Login.new(driver)<% end %>
18
- @account_page = <% if capybara? -%>Account.new<% elsif watir? -%>Account.new(browser)<% else -%>Account.new(driver)<% end %>
19
- end
20
-
21
- def teardown
22
- <%= partial('quit_driver', strip: true) %>
23
- end
24
-
25
- def test_login_with_right_credentials
26
- # binding.break # Uncomment to pause here and inspect state
27
- @login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
28
- @login_page.login(@user['username'], @user['password'])
29
- @account_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
30
- assert_equal "Welcome back #{@user['name']}", @account_page.header.customer_name
31
- end
32
-
33
- def test_login_with_wrong_credentials
34
- @login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
35
- @login_page.login(@user['username'], 'wrongPassword')
36
- @login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
37
- assert_equal 'Login or register', @account_page.header.customer_name
38
- end
39
- end
40
- <%- end -%>
41
- <%- else -%>
42
- <% if cross_platform? -%>
43
- require_relative '../helpers/appium_helper'
44
- <%- end -%>
45
- require_relative '../helpers/test_helper'
46
- require_relative '../page_objects/pages/home'
47
- require_relative '../page_objects/pages/pdp'
48
-
49
- class TestPdp < Minitest::Test
50
- def setup
51
- @home_page = Home.new(driver)
52
- @pdp_page = Pdp.new(driver)
53
- end
54
-
55
- def teardown
56
- driver.quit_driver
57
- end
58
-
59
- def test_shows_add_to_cart_button
60
- @home_page.go_to_product_detail
61
- assert_equal 'Add to Cart', @pdp_page.add_to_cart_text
62
- end
63
- end
64
- <%- end -%>
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../helpers/test_helper'
4
- require_relative '../helpers/visual_helper'
5
- require_relative '../page_objects/pages/login'
6
-
7
- class TestVisualRegression < Minitest::Test
8
- include VisualHelper
9
-
10
- def setup
11
- super
12
- @login_page = <% if capybara? -%>Login.new<% elsif watir? -%>Login.new(browser)<% else -%>Login.new(driver)<% end %>
13
- end
14
-
15
- def test_login_page_matches_baseline
16
- @login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
17
- screenshot_path = <% if capybara? -%>page.save_screenshot('tmp/visual_login.png')<% elsif watir? -%>browser.screenshot.save('tmp/visual_login.png')<% else -%>driver.save_screenshot('tmp/visual_login.png')<% end %>
18
- result = compare_screenshot('login_page', screenshot_path)
19
-
20
- refute_equal :mismatch, result[:status],
21
- "Visual diff: #{(result[:diff].to_f * 100).round(2)}% difference. See: #{result[:diff_path]}"
22
- end
23
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../helpers/spec_helper'
4
- require_relative '../helpers/performance_helper'
5
-
6
- describe 'Performance Audit' do
7
- include PerformanceHelper
8
-
9
- it 'meets performance threshold' do
10
- result = assert_performance_above('https://raider-test-site.onrender.com/', threshold: 0.7)
11
-
12
- expect(result[:status]).not_to eq(:error),
13
- "Lighthouse error: #{result[:message]}"
14
-
15
- expect(result[:passed]).to be(true),
16
- "Performance below threshold (#{result[:threshold]}): #{result[:scores]}"
17
- end
18
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../helpers/spec_helper'
4
- require_relative '../helpers/visual_helper'
5
- require_relative '../page_objects/pages/login'
6
-
7
- describe 'Visual Regression' do
8
- include VisualHelper
9
-
10
- let(:login_page) { <% if capybara? -%>Login.new<% elsif watir? -%>Login.new(browser)<% else -%>Login.new(driver)<% end -%> }
11
-
12
- it 'login page matches baseline' do
13
- login_page.<% if capybara? -%>visit_page<% else -%>visit<% end %>
14
- screenshot_path = <% if capybara? -%>page.save_screenshot('tmp/visual_login.png')<% elsif watir? -%>browser.screenshot.save('tmp/visual_login.png')<% else -%>driver.save_screenshot('tmp/visual_login.png')<% end %>
15
- result = compare_screenshot('login_page', screenshot_path)
16
-
17
- expect(result[:status]).not_to eq(:mismatch),
18
- "Visual diff: #{(result[:diff].to_f * 100).round(2)}% difference. See: #{result[:diff_path]}"
19
- end
20
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'capybara'
4
- require 'capybara/dsl'
5
- require 'selenium-webdriver'
6
- require 'yaml'
7
-
8
- module CapybaraHelper
9
- def self.configure
10
- config = YAML.load_file('config/config.yml')
11
-
12
- Capybara.configure do |capybara_config|
13
- capybara_config.default_driver = :selenium
14
- capybara_config.javascript_driver = :selenium
15
- capybara_config.app_host = config['url']
16
- capybara_config.default_max_wait_time = config.fetch('timeout', 10)
17
- capybara_config.run_server = false
18
- end
19
-
20
- Capybara.register_driver :selenium do |app|
21
- browser = config['browser'].to_sym
22
- args = config['browser_arguments'][config['browser']] || []
23
- args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
24
- options = Selenium::WebDriver::Chrome::Options.new(args:)
25
- debug_cfg = config['debug'] || {}
26
- if debug_cfg.fetch('enabled', false) || ENV['DEBUG']&.downcase == 'true'
27
- options.add_option('goog:loggingPrefs', { browser: 'ALL', performance: 'ALL' })
28
- end
29
- Capybara::Selenium::Driver.new(app, browser:, options:)
30
- end
31
- end
32
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'json'
5
- require 'fileutils'
6
- require 'yaml'
7
-
8
- module PerformanceHelper
9
- REPORTS_DIR = 'lighthouse_reports'
10
-
11
- def run_lighthouse_audit(url, categories: %w[performance accessibility best-practices seo])
12
- FileUtils.mkdir_p(REPORTS_DIR)
13
-
14
- report_path = File.join(REPORTS_DIR, "report_#{Time.now.to_i}.json")
15
- category_flags = categories.map { |c| "--only-categories=#{c}" }.join(' ')
16
-
17
- config_timeout = if File.exist?('config/config.yml')
18
- YAML.load_file('config/config.yml').fetch('timeout', 10)
19
- else
20
- 10
21
- end
22
- max_wait = config_timeout * 1000
23
-
24
- command = "lighthouse #{url} --output=json --output-path=#{report_path} " \
25
- "--chrome-flags=\"--headless --no-sandbox\" #{category_flags} " \
26
- "--max-wait-for-load=#{max_wait} --quiet"
27
-
28
- stdout, stderr, status = Open3.capture3(command)
29
-
30
- unless status.success?
31
- return { status: :error, message: "Lighthouse failed: #{stderr.strip.empty? ? stdout : stderr}" }
32
- end
33
-
34
- report = JSON.parse(File.read(report_path))
35
- scores = extract_scores(report)
36
-
37
- { status: :success, scores:, report_path: }
38
- end
39
-
40
- def assert_performance_above(url, threshold: 0.8, categories: %w[performance])
41
- result = run_lighthouse_audit(url, categories:)
42
- return result if result[:status] == :error
43
-
44
- passed = result[:scores].all? { |_category, score| score >= threshold }
45
-
46
- { status: passed ? :pass : :fail, scores: result[:scores], passed:, threshold:,
47
- report_path: result[:report_path] }
48
- end
49
-
50
- private
51
-
52
- def extract_scores(report)
53
- report.fetch('categories', {}).each_with_object({}) do |(key, data), scores|
54
- scores[key] = data['score']
55
- end
56
- end
57
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'chunky_png'
4
- require 'fileutils'
5
-
6
- module VisualHelper
7
- BASELINE_DIR = 'visual_baselines'
8
- DIFF_DIR = 'visual_diffs'
9
-
10
- def compare_screenshot(name, screenshot_path, threshold: 0.01)
11
- FileUtils.mkdir_p(BASELINE_DIR)
12
- FileUtils.mkdir_p(DIFF_DIR)
13
-
14
- baseline_path = File.join(BASELINE_DIR, "#{name}.png")
15
-
16
- unless File.exist?(baseline_path)
17
- FileUtils.cp(screenshot_path, baseline_path)
18
- return { status: :baseline_created, path: baseline_path }
19
- end
20
-
21
- baseline = ChunkyPNG::Image.from_file(baseline_path)
22
- current = ChunkyPNG::Image.from_file(screenshot_path)
23
-
24
- diff_pixels = 0
25
- total_pixels = baseline.width * baseline.height
26
-
27
- diff_image = ChunkyPNG::Image.new(baseline.width, baseline.height)
28
-
29
- baseline.height.times do |y|
30
- baseline.width.times do |x|
31
- if pixel_match?(baseline[x, y], current[x, y])
32
- diff_image[x, y] = ChunkyPNG::Color.rgba(0, 0, 0, 50)
33
- else
34
- diff_pixels += 1
35
- diff_image[x, y] = ChunkyPNG::Color.rgb(255, 0, 0)
36
- end
37
- end
38
- end
39
-
40
- diff_percentage = diff_pixels.to_f / total_pixels
41
-
42
- if diff_percentage > threshold
43
- diff_path = File.join(DIFF_DIR, "#{name}_diff.png")
44
- diff_image.save(diff_path)
45
- { status: :mismatch, diff: diff_percentage, diff_path: }
46
- else
47
- { status: :match, diff: diff_percentage }
48
- end
49
- end
50
-
51
- private
52
-
53
- def pixel_match?(pixel1, pixel2)
54
- r1, g1, b1 = ChunkyPNG::Color.r(pixel1), ChunkyPNG::Color.g(pixel1), ChunkyPNG::Color.b(pixel1)
55
- r2, g2, b2 = ChunkyPNG::Color.r(pixel2), ChunkyPNG::Color.g(pixel2), ChunkyPNG::Color.b(pixel2)
56
- (r1 - r2).abs <= 5 && (g1 - g2).abs <= 5 && (b1 - b2).abs <= 5
57
- end
58
- end
data/lib/llm/client.rb DELETED
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'net/http'
4
- require_relative 'config'
5
-
6
- module Llm
7
- # Facade for LLM completion with retry logic and graceful fallback.
8
- # Returns nil on any failure — callers should always have a non-AI fallback.
9
- module Client
10
- MAX_RETRIES = 3
11
- BASE_DELAY = 1
12
-
13
- # Only retry on transient network errors, not configuration or API errors
14
- RETRYABLE_ERRORS = [
15
- Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED,
16
- Errno::ECONNRESET, Errno::ETIMEDOUT, SocketError
17
- ].freeze
18
-
19
- class << self
20
- def complete(prompt, system_prompt: nil)
21
- provider = build_provider
22
- return nil unless provider
23
-
24
- attempt = 0
25
- begin
26
- attempt += 1
27
- provider.complete(prompt, system_prompt:)
28
- rescue *RETRYABLE_ERRORS => e
29
- if attempt < MAX_RETRIES
30
- sleep(BASE_DELAY * (2**(attempt - 1)))
31
- retry
32
- end
33
- warn "[Ruby Raider] LLM failed after #{MAX_RETRIES} attempts: #{e.message}"
34
- nil
35
- rescue StandardError => e
36
- warn "[Ruby Raider] LLM error: #{e.message}"
37
- nil
38
- end
39
- end
40
-
41
- def available?
42
- config = Config.new
43
- return false unless config.configured?
44
-
45
- provider = config.build_provider
46
- provider&.available? || false
47
- end
48
-
49
- def status
50
- config = Config.new
51
- return { configured: false, provider: nil } unless config.configured?
52
-
53
- provider = config.build_provider
54
- {
55
- configured: true,
56
- provider: config.provider_name,
57
- model: config.model,
58
- available: provider&.available? || false
59
- }
60
- end
61
-
62
- private
63
-
64
- def build_provider
65
- config = Config.new
66
- unless config.configured?
67
- warn '[Ruby Raider] No LLM configured. Use: raider u llm ollama'
68
- return nil
69
- end
70
- provider = config.build_provider
71
- unless provider&.available?
72
- warn "[Ruby Raider] LLM provider '#{config.provider_name}' is not available"
73
- return nil
74
- end
75
- provider
76
- end
77
- end
78
- end
79
- end
data/lib/llm/config.rb DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
-
5
- module Llm
6
- # Reads LLM configuration from env vars and config/config.yml
7
- # Env vars take precedence over config file values.
8
- class Config
9
- PROVIDERS = %w[openai anthropic ollama].freeze
10
-
11
- attr_reader :provider_name, :api_key, :model, :url
12
-
13
- def initialize
14
- @provider_name = env('RUBY_RAIDER_LLM_PROVIDER') || config_value('llm_provider')
15
- @api_key = env('RUBY_RAIDER_LLM_API_KEY') || config_value('llm_api_key')
16
- @model = env('RUBY_RAIDER_LLM_MODEL') || config_value('llm_model')
17
- @url = env('RUBY_RAIDER_LLM_URL') || config_value('llm_url')
18
- end
19
-
20
- def configured?
21
- return false unless @provider_name && PROVIDERS.include?(@provider_name)
22
- return true if @provider_name == 'ollama'
23
-
24
- !@api_key.nil? && !@api_key.empty?
25
- end
26
-
27
- def build_provider
28
- return nil unless configured?
29
-
30
- case @provider_name
31
- when 'openai'
32
- require_relative 'providers/openai_provider'
33
- Providers::OpenaiProvider.new(api_key: @api_key, model: @model)
34
- when 'anthropic'
35
- require_relative 'providers/anthropic_provider'
36
- Providers::AnthropicProvider.new(api_key: @api_key, model: @model)
37
- when 'ollama'
38
- require_relative 'providers/ollama_provider'
39
- Providers::OllamaProvider.new(model: @model, url: @url)
40
- end
41
- end
42
-
43
- private
44
-
45
- def env(key)
46
- value = ENV.fetch(key, nil)
47
- value&.strip&.empty? ? nil : value&.strip
48
- end
49
-
50
- def config_value(key)
51
- return nil unless File.exist?('config/config.yml')
52
-
53
- data = YAML.load_file('config/config.yml')
54
- data&.dig(key)
55
- end
56
- end
57
- end