ruby_raider 3.0.0 → 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 (193) 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 +22 -188
  6. data/lib/commands/utility_commands.rb +10 -44
  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/browser_helper.tt +1 -1
  38. data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +1 -3
  39. data/lib/generators/templates/helpers/partials/debug_start.tt +1 -3
  40. data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
  41. data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
  42. data/lib/generators/templates/helpers/partials/selenium_driver.tt +1 -1
  43. data/lib/generators/templates/helpers/spec_helper.tt +2 -39
  44. data/lib/ruby_raider.rb +2 -50
  45. data/lib/scaffolding/project_detector.rb +30 -13
  46. data/lib/scaffolding/scaffolding.rb +0 -109
  47. data/lib/scaffolding/templates/component.tt +1 -4
  48. data/lib/scaffolding/templates/page_object.tt +0 -10
  49. data/lib/scaffolding/templates/spec.tt +2 -6
  50. data/lib/scaffolding/templates/steps.tt +0 -2
  51. data/lib/utilities/utilities.rb +26 -24
  52. data/lib/version +1 -1
  53. data/sig/commands/scaffolding_commands.rbs +0 -12
  54. data/sig/commands/utility_commands.rbs +0 -6
  55. data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
  56. data/sig/generators/generator.rbs +0 -11
  57. data/sig/generators/helper_generator.rbs +1 -5
  58. data/sig/generators/invoke_generators.rbs +1 -2
  59. data/sig/generators/menu_generator.rbs +2 -6
  60. data/sig/generators/rspec/rspec_generator.rbs +0 -2
  61. data/sig/ruby_raider.rbs +1 -3
  62. data/sig/scaffolding/project_detector.rbs +0 -1
  63. data/sig/scaffolding/scaffolding.rbs +0 -23
  64. data/sig/utilities/utilities.rbs +0 -4
  65. data/spec/commands/raider_commands_spec.rb +0 -61
  66. data/spec/commands/scaffolding_commands_spec.rb +22 -0
  67. data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
  68. data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
  69. data/spec/integration/commands/utility_commands_spec.rb +9 -9
  70. data/spec/integration/content/ci_content_spec.rb +6 -61
  71. data/spec/integration/content/common_content_spec.rb +0 -64
  72. data/spec/integration/content/config_content_spec.rb +1 -51
  73. data/spec/integration/content/content_helper.rb +2 -2
  74. data/spec/integration/content/gemfile_content_spec.rb +0 -71
  75. data/spec/integration/content/helper_content_spec.rb +4 -240
  76. data/spec/integration/content/page_content_spec.rb +0 -89
  77. data/spec/integration/content/syntax_validation_spec.rb +2 -2
  78. data/spec/integration/content/test_content_spec.rb +0 -119
  79. data/spec/integration/end_to_end_features_spec.rb +13 -411
  80. data/spec/integration/end_to_end_spec.rb +0 -96
  81. data/spec/integration/generators/axe_addon_spec.rb +2 -52
  82. data/spec/integration/generators/common_generator_spec.rb +1 -13
  83. data/spec/integration/generators/config_features_spec.rb +3 -81
  84. data/spec/integration/generators/debug_helper_spec.rb +0 -20
  85. data/spec/integration/generators/github_generator_spec.rb +5 -15
  86. data/spec/integration/generators/helpers_generator_spec.rb +0 -37
  87. data/spec/integration/scaffolding_e2e_spec.rb +540 -0
  88. data/spec/integration/settings_helper.rb +1 -3
  89. data/spec/integration/spec_helper.rb +6 -11
  90. data/spec/menus/menu_generator_spec.rb +4 -107
  91. data/spec/scaffolding/scaffold_project_detector_spec.rb +42 -26
  92. data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
  93. data/spec/system/selenium_spec.rb +1 -4
  94. data/spec/system/support/system_test_helper.rb +0 -1
  95. data/spec/system/watir_spec.rb +1 -4
  96. data/spec/utilities/headless_config_spec.rb +82 -0
  97. data/spec/utilities/utilities_spec.rb +105 -0
  98. metadata +7 -97
  99. data/lib/adopter/adopt_menu.rb +0 -146
  100. data/lib/adopter/converters/base_converter.rb +0 -84
  101. data/lib/adopter/converters/identity_converter.rb +0 -53
  102. data/lib/adopter/migration_plan.rb +0 -74
  103. data/lib/adopter/migrator.rb +0 -96
  104. data/lib/adopter/plan_builder.rb +0 -275
  105. data/lib/adopter/project_analyzer.rb +0 -252
  106. data/lib/adopter/project_detector.rb +0 -157
  107. data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
  108. data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
  109. data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
  110. data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
  111. data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
  112. data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
  113. data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
  114. data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
  115. data/lib/generators/minitest/minitest_generator.rb +0 -35
  116. data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
  117. data/lib/generators/minitest/templates/performance_test.tt +0 -18
  118. data/lib/generators/minitest/templates/test.tt +0 -64
  119. data/lib/generators/minitest/templates/visual_test.tt +0 -23
  120. data/lib/generators/rspec/templates/performance_spec.tt +0 -18
  121. data/lib/generators/rspec/templates/visual_spec.tt +0 -20
  122. data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
  123. data/lib/generators/templates/helpers/performance_helper.tt +0 -57
  124. data/lib/generators/templates/helpers/visual_helper.tt +0 -58
  125. data/lib/llm/client.rb +0 -79
  126. data/lib/llm/config.rb +0 -57
  127. data/lib/llm/prompts.rb +0 -84
  128. data/lib/llm/provider.rb +0 -27
  129. data/lib/llm/providers/anthropic_provider.rb +0 -43
  130. data/lib/llm/providers/ollama_provider.rb +0 -56
  131. data/lib/llm/providers/openai_provider.rb +0 -42
  132. data/lib/llm/response_parser.rb +0 -67
  133. data/lib/plugin/plugin.rb +0 -111
  134. data/lib/plugin/plugin_exposer.rb +0 -55
  135. data/lib/scaffolding/crud_generator.rb +0 -94
  136. data/lib/scaffolding/dry_run_presenter.rb +0 -16
  137. data/lib/scaffolding/page_introspector.rb +0 -45
  138. data/lib/scaffolding/scaffold_menu.rb +0 -103
  139. data/lib/scaffolding/templates/page_from_url.tt +0 -75
  140. data/lib/scaffolding/templates/spec_from_page.tt +0 -31
  141. data/lib/scaffolding/templates/spec_from_url.tt +0 -46
  142. data/lib/scaffolding/url_analyzer.rb +0 -179
  143. data/sig/adopter/adopt_menu.rbs +0 -25
  144. data/sig/adopter/converters/base_converter.rbs +0 -23
  145. data/sig/adopter/converters/identity_converter.rbs +0 -16
  146. data/sig/adopter/migration_plan.rbs +0 -34
  147. data/sig/adopter/migrator.rbs +0 -21
  148. data/sig/adopter/plan_builder.rbs +0 -38
  149. data/sig/adopter/project_analyzer.rbs +0 -39
  150. data/sig/adopter/project_detector.rbs +0 -26
  151. data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
  152. data/sig/generators/minitest/minitest_generator.rbs +0 -8
  153. data/sig/llm/client.rbs +0 -15
  154. data/sig/llm/config.rbs +0 -20
  155. data/sig/llm/prompts.rbs +0 -8
  156. data/sig/llm/provider.rbs +0 -12
  157. data/sig/llm/providers/anthropic_provider.rbs +0 -16
  158. data/sig/llm/providers/ollama_provider.rbs +0 -18
  159. data/sig/llm/providers/openai_provider.rbs +0 -16
  160. data/sig/llm/response_parser.rbs +0 -13
  161. data/sig/plugin/plugin.rbs +0 -24
  162. data/sig/plugin/plugin_exposer.rbs +0 -20
  163. data/sig/scaffolding/crud_generator.rbs +0 -16
  164. data/sig/scaffolding/dry_run_presenter.rbs +0 -4
  165. data/sig/scaffolding/page_introspector.rbs +0 -14
  166. data/sig/scaffolding/scaffold_menu.rbs +0 -18
  167. data/sig/scaffolding/url_analyzer.rbs +0 -28
  168. data/spec/adopter/adopt_menu_spec.rb +0 -176
  169. data/spec/adopter/converters/identity_converter_spec.rb +0 -145
  170. data/spec/adopter/migration_plan_spec.rb +0 -113
  171. data/spec/adopter/migrator_spec.rb +0 -277
  172. data/spec/adopter/plan_builder_spec.rb +0 -298
  173. data/spec/adopter/project_analyzer_spec.rb +0 -337
  174. data/spec/adopter/project_detector_spec.rb +0 -295
  175. data/spec/generators/generator_spec.rb +0 -23
  176. data/spec/integration/content/reporter_content_spec.rb +0 -236
  177. data/spec/integration/content/skip_flags_content_spec.rb +0 -206
  178. data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
  179. data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
  180. data/spec/integration/generators/minitest_generator_spec.rb +0 -64
  181. data/spec/integration/generators/reporter_spec.rb +0 -159
  182. data/spec/integration/generators/skip_flags_spec.rb +0 -134
  183. data/spec/integration/generators/visual_addon_spec.rb +0 -148
  184. data/spec/llm/client_spec.rb +0 -79
  185. data/spec/llm/config_spec.rb +0 -92
  186. data/spec/llm/prompts_spec.rb +0 -49
  187. data/spec/llm/response_parser_spec.rb +0 -92
  188. data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
  189. data/spec/scaffolding/page_introspector_spec.rb +0 -82
  190. data/spec/scaffolding/url_analyzer_spec.rb +0 -110
  191. data/spec/system/adopt_matrix_spec.rb +0 -537
  192. data/spec/system/adopt_spec.rb +0 -225
  193. 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'] && !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