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.
- checksums.yaml +4 -4
- data/.github/workflows/e2e_tests.yml +0 -17
- data/.github/workflows/system_tests.yml +0 -22
- data/README.md +8 -62
- data/lib/commands/scaffolding_commands.rb +1 -192
- data/lib/commands/utility_commands.rb +0 -43
- data/lib/generators/automation/templates/login.tt +1 -9
- data/lib/generators/automation/templates/page.tt +1 -7
- data/lib/generators/automation/templates/partials/initialize_selector.tt +1 -3
- data/lib/generators/automation/templates/partials/visit_method.tt +2 -6
- data/lib/generators/common_generator.rb +0 -6
- data/lib/generators/cucumber/cucumber_generator.rb +0 -24
- data/lib/generators/cucumber/templates/accessibility_steps.tt +2 -6
- data/lib/generators/cucumber/templates/cucumber.tt +0 -7
- data/lib/generators/cucumber/templates/env.tt +1 -3
- data/lib/generators/cucumber/templates/partials/appium_env.tt +1 -6
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +0 -5
- data/lib/generators/cucumber/templates/partials/watir_env.tt +0 -5
- data/lib/generators/cucumber/templates/partials/web_steps.tt +4 -4
- data/lib/generators/cucumber/templates/world.tt +1 -3
- data/lib/generators/generator.rb +2 -46
- data/lib/generators/helper_generator.rb +4 -42
- data/lib/generators/infrastructure/templates/github.tt +2 -2
- data/lib/generators/infrastructure/templates/github_appium.tt +2 -6
- data/lib/generators/invoke_generators.rb +4 -25
- data/lib/generators/menu_generator.rb +10 -100
- data/lib/generators/rspec/rspec_generator.rb +0 -11
- data/lib/generators/rspec/templates/accessibility_spec.tt +2 -6
- data/lib/generators/rspec/templates/spec.tt +6 -8
- data/lib/generators/templates/common/gemfile.tt +0 -26
- data/lib/generators/templates/common/git_ignore.tt +0 -2
- data/lib/generators/templates/common/partials/mobile_config.tt +0 -4
- data/lib/generators/templates/common/partials/web_config.tt +0 -4
- data/lib/generators/templates/common/rakefile.tt +0 -9
- data/lib/generators/templates/common/read_me.tt +4 -10
- data/lib/generators/templates/helpers/allure_helper.tt +0 -10
- data/lib/generators/templates/helpers/partials/debug_diagnostics.tt +1 -3
- data/lib/generators/templates/helpers/partials/debug_start.tt +1 -3
- data/lib/generators/templates/helpers/partials/quit_driver.tt +1 -3
- data/lib/generators/templates/helpers/partials/screenshot.tt +1 -3
- data/lib/generators/templates/helpers/spec_helper.tt +2 -39
- data/lib/ruby_raider.rb +2 -50
- data/lib/scaffolding/project_detector.rb +2 -9
- data/lib/scaffolding/scaffolding.rb +0 -109
- data/lib/scaffolding/templates/component.tt +1 -4
- data/lib/scaffolding/templates/page_object.tt +0 -10
- data/lib/scaffolding/templates/spec.tt +2 -6
- data/lib/scaffolding/templates/steps.tt +0 -2
- data/lib/utilities/utilities.rb +23 -29
- data/lib/version +1 -1
- data/sig/commands/scaffolding_commands.rbs +0 -12
- data/sig/commands/utility_commands.rbs +0 -6
- data/sig/generators/cucumber/cucumber_generator.rbs +0 -4
- data/sig/generators/generator.rbs +0 -11
- data/sig/generators/helper_generator.rbs +1 -5
- data/sig/generators/invoke_generators.rbs +1 -2
- data/sig/generators/menu_generator.rbs +2 -6
- data/sig/generators/rspec/rspec_generator.rbs +0 -2
- data/sig/ruby_raider.rbs +1 -3
- data/sig/scaffolding/project_detector.rbs +0 -1
- data/sig/scaffolding/scaffolding.rbs +0 -23
- data/sig/utilities/utilities.rbs +0 -4
- data/spec/commands/raider_commands_spec.rb +0 -61
- data/spec/integration/commands/browser_update_after_creation_spec.rb +123 -0
- data/spec/integration/commands/scaffolding_commands_spec.rb +0 -20
- data/spec/integration/commands/utility_commands_spec.rb +5 -5
- data/spec/integration/content/ci_content_spec.rb +6 -61
- data/spec/integration/content/common_content_spec.rb +0 -64
- data/spec/integration/content/config_content_spec.rb +1 -51
- data/spec/integration/content/content_helper.rb +2 -2
- data/spec/integration/content/gemfile_content_spec.rb +0 -71
- data/spec/integration/content/helper_content_spec.rb +4 -240
- data/spec/integration/content/page_content_spec.rb +0 -89
- data/spec/integration/content/syntax_validation_spec.rb +2 -2
- data/spec/integration/content/test_content_spec.rb +0 -119
- data/spec/integration/end_to_end_features_spec.rb +13 -411
- data/spec/integration/end_to_end_spec.rb +0 -96
- data/spec/integration/generators/axe_addon_spec.rb +2 -52
- data/spec/integration/generators/common_generator_spec.rb +1 -13
- data/spec/integration/generators/config_features_spec.rb +3 -81
- data/spec/integration/generators/debug_helper_spec.rb +0 -20
- data/spec/integration/generators/github_generator_spec.rb +5 -15
- data/spec/integration/generators/helpers_generator_spec.rb +0 -37
- data/spec/integration/scaffolding_e2e_spec.rb +2 -237
- data/spec/integration/settings_helper.rb +1 -3
- data/spec/integration/spec_helper.rb +6 -11
- data/spec/menus/menu_generator_spec.rb +4 -107
- data/spec/scaffolding/scaffold_project_detector_spec.rb +4 -26
- data/spec/scaffolding/scaffolding_features_spec.rb +0 -183
- data/spec/system/selenium_spec.rb +1 -4
- data/spec/system/support/system_test_helper.rb +0 -1
- data/spec/system/watir_spec.rb +1 -4
- data/spec/utilities/headless_config_spec.rb +0 -7
- metadata +3 -97
- data/lib/adopter/adopt_menu.rb +0 -146
- data/lib/adopter/converters/base_converter.rb +0 -84
- data/lib/adopter/converters/identity_converter.rb +0 -53
- data/lib/adopter/migration_plan.rb +0 -74
- data/lib/adopter/migrator.rb +0 -96
- data/lib/adopter/plan_builder.rb +0 -275
- data/lib/adopter/project_analyzer.rb +0 -252
- data/lib/adopter/project_detector.rb +0 -157
- data/lib/generators/cucumber/templates/partials/capybara_env.tt +0 -38
- data/lib/generators/cucumber/templates/partials/capybara_world.tt +0 -6
- data/lib/generators/cucumber/templates/performance_feature.tt +0 -5
- data/lib/generators/cucumber/templates/performance_steps.tt +0 -17
- data/lib/generators/cucumber/templates/visual_feature.tt +0 -5
- data/lib/generators/cucumber/templates/visual_steps.tt +0 -19
- data/lib/generators/infrastructure/gitlab_generator.rb +0 -11
- data/lib/generators/infrastructure/templates/gitlab.tt +0 -46
- data/lib/generators/minitest/minitest_generator.rb +0 -35
- data/lib/generators/minitest/templates/accessibility_test.tt +0 -26
- data/lib/generators/minitest/templates/performance_test.tt +0 -18
- data/lib/generators/minitest/templates/test.tt +0 -64
- data/lib/generators/minitest/templates/visual_test.tt +0 -23
- data/lib/generators/rspec/templates/performance_spec.tt +0 -18
- data/lib/generators/rspec/templates/visual_spec.tt +0 -20
- data/lib/generators/templates/helpers/capybara_helper.tt +0 -32
- data/lib/generators/templates/helpers/performance_helper.tt +0 -57
- data/lib/generators/templates/helpers/visual_helper.tt +0 -58
- data/lib/llm/client.rb +0 -79
- data/lib/llm/config.rb +0 -57
- data/lib/llm/prompts.rb +0 -84
- data/lib/llm/provider.rb +0 -27
- data/lib/llm/providers/anthropic_provider.rb +0 -43
- data/lib/llm/providers/ollama_provider.rb +0 -56
- data/lib/llm/providers/openai_provider.rb +0 -42
- data/lib/llm/response_parser.rb +0 -67
- data/lib/plugin/plugin.rb +0 -111
- data/lib/plugin/plugin_exposer.rb +0 -55
- data/lib/scaffolding/crud_generator.rb +0 -94
- data/lib/scaffolding/dry_run_presenter.rb +0 -16
- data/lib/scaffolding/page_introspector.rb +0 -45
- data/lib/scaffolding/scaffold_menu.rb +0 -103
- data/lib/scaffolding/templates/page_from_url.tt +0 -75
- data/lib/scaffolding/templates/spec_from_page.tt +0 -31
- data/lib/scaffolding/templates/spec_from_url.tt +0 -46
- data/lib/scaffolding/url_analyzer.rb +0 -179
- data/sig/adopter/adopt_menu.rbs +0 -25
- data/sig/adopter/converters/base_converter.rbs +0 -23
- data/sig/adopter/converters/identity_converter.rbs +0 -16
- data/sig/adopter/migration_plan.rbs +0 -34
- data/sig/adopter/migrator.rbs +0 -21
- data/sig/adopter/plan_builder.rbs +0 -38
- data/sig/adopter/project_analyzer.rbs +0 -39
- data/sig/adopter/project_detector.rbs +0 -26
- data/sig/generators/infrastructure/gitlab_generator.rbs +0 -4
- data/sig/generators/minitest/minitest_generator.rbs +0 -8
- data/sig/llm/client.rbs +0 -15
- data/sig/llm/config.rbs +0 -20
- data/sig/llm/prompts.rbs +0 -8
- data/sig/llm/provider.rbs +0 -12
- data/sig/llm/providers/anthropic_provider.rbs +0 -16
- data/sig/llm/providers/ollama_provider.rbs +0 -18
- data/sig/llm/providers/openai_provider.rbs +0 -16
- data/sig/llm/response_parser.rbs +0 -13
- data/sig/plugin/plugin.rbs +0 -24
- data/sig/plugin/plugin_exposer.rbs +0 -20
- data/sig/scaffolding/crud_generator.rbs +0 -16
- data/sig/scaffolding/dry_run_presenter.rbs +0 -4
- data/sig/scaffolding/page_introspector.rbs +0 -14
- data/sig/scaffolding/scaffold_menu.rbs +0 -18
- data/sig/scaffolding/url_analyzer.rbs +0 -28
- data/spec/adopter/adopt_menu_spec.rb +0 -176
- data/spec/adopter/converters/identity_converter_spec.rb +0 -145
- data/spec/adopter/migration_plan_spec.rb +0 -113
- data/spec/adopter/migrator_spec.rb +0 -277
- data/spec/adopter/plan_builder_spec.rb +0 -298
- data/spec/adopter/project_analyzer_spec.rb +0 -337
- data/spec/adopter/project_detector_spec.rb +0 -295
- data/spec/generators/generator_spec.rb +0 -23
- data/spec/integration/content/reporter_content_spec.rb +0 -236
- data/spec/integration/content/skip_flags_content_spec.rb +0 -206
- data/spec/integration/generators/gitlab_generator_spec.rb +0 -38
- data/spec/integration/generators/lighthouse_addon_spec.rb +0 -132
- data/spec/integration/generators/minitest_generator_spec.rb +0 -64
- data/spec/integration/generators/reporter_spec.rb +0 -159
- data/spec/integration/generators/skip_flags_spec.rb +0 -134
- data/spec/integration/generators/visual_addon_spec.rb +0 -148
- data/spec/llm/client_spec.rb +0 -79
- data/spec/llm/config_spec.rb +0 -92
- data/spec/llm/prompts_spec.rb +0 -49
- data/spec/llm/response_parser_spec.rb +0 -92
- data/spec/menus/adopter_adopt_menu_spec.rb +0 -97
- data/spec/scaffolding/page_introspector_spec.rb +0 -82
- data/spec/scaffolding/url_analyzer_spec.rb +0 -110
- data/spec/system/adopt_matrix_spec.rb +0 -537
- data/spec/system/adopt_spec.rb +0 -225
- 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,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,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,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
|