diff --git a/README.md b/README.md index 5b2482ff..ca424e59 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Every generated project comes with: - **Page Object Model** — Base page and component classes with example pages - **Test examples** — Working login tests against the [Raider Test Store](https://raider-test-site.onrender.com/) - **Allure reporting** — Pre-configured with screenshot capture on failure +- **Raider reporting** — RSpec projects include [raider_reporter](https://github.com/RaiderHQ/raider_reporter), which writes results to `raider-results/` and uploads runs when `RAIDER_TOKEN` and `RAIDER_ENDPOINT` are set - **GitHub Actions CI** — Ready-to-use test pipeline - **Rake tasks** — Default, smoke, and regression tasks with tag-based filtering - **Retry support** — RSpec retry with configurable retry count via `RETRY_COUNT` env var diff --git a/RELEASE.md b/RELEASE.md index 4da154df..d09644fe 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,16 @@ # Release Process Documentation +## Release Notes + +### 3.3.0 + +- Generated RSpec projects now include `raider_reporter`: results are written to `raider-results/` and uploaded when `RAIDER_TOKEN` and `RAIDER_ENDPOINT` are set. Allure is still produced. Cucumber projects are unchanged. +- Screenshots (Allure too) are now taken only when an example fails, and are attached to both Allure and Raider. +- Generated GitHub CI now runs on push (main, master), pull requests and manual dispatch, passes the Raider settings to RSpec runs and uploads `raider-results/` as an artifact. +- Fixed the generated Gemfile `ruby_raider` pin so it follows the gem's own major.minor version. + +--- + ## Overview Ruby Raider uses a **fully automated release process**. You only need to run a single command, and everything else happens automatically via GitHub Actions. diff --git a/lib/generators/generator.rb b/lib/generators/generator.rb index b27a697e..99b3e546 100644 --- a/lib/generators/generator.rb +++ b/lib/generators/generator.rb @@ -71,6 +71,11 @@ def selenium_based? args.include?('selenium') end + def ruby_raider_version_requirement + major, minor = File.read(File.expand_path('../version', __dir__)).strip.split('.') + "~> #{major}.#{minor}" + end + private def _initializer diff --git a/lib/generators/infrastructure/templates/github.tt b/lib/generators/infrastructure/templates/github.tt index cf126715..f076928e 100644 --- a/lib/generators/infrastructure/templates/github.tt +++ b/lib/generators/infrastructure/templates/github.tt @@ -1,6 +1,9 @@ name: <%- if framework == 'cucumber' -%>Cucumber Tests<%- else -%>Rspec Tests<%- end -%> on: + push: + branches: [ main, master ] + pull_request: workflow_dispatch: inputs: browser: @@ -37,6 +40,21 @@ jobs: run: <%- if framework == 'cucumber' -%>bundle exec cucumber features --format pretty<%- else -%>bundle exec rspec spec --format documentation<%- end %> env: HEADLESS: true +<%- unless framework == 'cucumber' -%> + RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }} + RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }} +<%- end -%> + +<%- unless framework == 'cucumber' -%> + - name: Upload Raider results + uses: actions/upload-artifact@v4 + if: always() + with: + name: raider-results + path: raider-results/ + if-no-files-found: ignore + +<%- end -%> - name: Get Allure history uses: actions/checkout@v2 diff --git a/lib/generators/infrastructure/templates/github_appium.tt b/lib/generators/infrastructure/templates/github_appium.tt index 73b661ed..b5a6c2e1 100644 --- a/lib/generators/infrastructure/templates/github_appium.tt +++ b/lib/generators/infrastructure/templates/github_appium.tt @@ -2,9 +2,9 @@ name: Appium Tests on: push: - branches: [ main ] + branches: [ main, master ] pull_request: - branches: [ main ] + workflow_dispatch: jobs: <%- if ios? || cross_platform? -%> @@ -52,6 +52,18 @@ jobs: <%- end -%> env: APP_PATH: ${{ github.workspace }}/apps/RaiderTestApp.app +<%- unless cucumber? -%> + RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }} + RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }} + + - name: Upload Raider results + uses: actions/upload-artifact@v4 + if: always() + with: + name: raider-results-ios + path: raider-results/ + if-no-files-found: ignore +<%- end -%> <%- end -%> <%- if android? || cross_platform? -%> @@ -101,4 +113,16 @@ jobs: <%- end -%> env: APP_PATH: ${{ github.workspace }}/apps/app-release.apk +<%- unless cucumber? -%> + RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }} + RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }} + + - name: Upload Raider results + uses: actions/upload-artifact@v4 + if: always() + with: + name: raider-results-android + path: raider-results/ + if-no-files-found: ignore +<%- end -%> <%- end -%> diff --git a/lib/generators/templates/common/gemfile.tt b/lib/generators/templates/common/gemfile.tt index 3483ca63..1aef2105 100644 --- a/lib/generators/templates/common/gemfile.tt +++ b/lib/generators/templates/common/gemfile.tt @@ -20,6 +20,7 @@ gem 'parallel_tests' gem 'rake' gem 'reek' <%- if rspec? -%> +gem 'raider_reporter', '~> 0.2' gem 'rspec-retry' <%- end -%> gem '<%= framework %>' @@ -30,7 +31,7 @@ gem 'rubocop' <%- if rspec? -%> gem 'rubocop-rspec' <%- end -%> -gem 'ruby_raider', '~> 1.1.0' +gem 'ruby_raider', '<%= ruby_raider_version_requirement %>' <%- if selenium_based? %> gem 'selenium-webdriver' <%- end -%> diff --git a/lib/generators/templates/common/git_ignore.tt b/lib/generators/templates/common/git_ignore.tt index 7d4fcf99..a1b3ab8f 100644 --- a/lib/generators/templates/common/git_ignore.tt +++ b/lib/generators/templates/common/git_ignore.tt @@ -1,4 +1,5 @@ allure-results <%- if rspec? -%> +raider-results spec/examples.txt <%- end -%> \ No newline at end of file diff --git a/lib/generators/templates/common/read_me.tt b/lib/generators/templates/common/read_me.tt index bdd1d29b..78c23f86 100644 --- a/lib/generators/templates/common/read_me.tt +++ b/lib/generators/templates/common/read_me.tt @@ -82,6 +82,23 @@ raider g steps [NAME] # Create step definitions raider g scaffold [NAME] # Create page + test + steps ``` +<%- if rspec? -%> +## Reporting + +Every run writes machine-readable results to `raider-results/` (including `run.json`), next to the usual Allure results. +Allure reports are still produced. + +To upload runs to Raider, set these environment variables before running the suite: + +```bash +export RAIDER_TOKEN=your-token +export RAIDER_ENDPOINT=https://your-raider-endpoint +``` + +Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. Failure screenshots are attached to both +Allure and Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details. + +<%- end -%> ### Sponsors This project is tested with BrowserStack. diff --git a/lib/generators/templates/helpers/partials/screenshot.tt b/lib/generators/templates/helpers/partials/screenshot.tt index 456ca517..7e0b56d9 100644 --- a/lib/generators/templates/helpers/partials/screenshot.tt +++ b/lib/generators/templates/helpers/partials/screenshot.tt @@ -1,7 +1,7 @@ <% if selenium_based? %> - screenshot = driver.save_screenshot("#{temp_folder}/#{example_name}.png") + screenshot = driver.save_screenshot(screenshot_path) <% elsif watir? %> - screenshot = browser.screenshot.save("#{temp_folder}/#{example_name}.png") + screenshot = browser.screenshot.save(screenshot_path) <% else %> - screenshot = driver.screenshot("#{temp_folder}/#{example_name}.png") + screenshot = driver.screenshot(screenshot_path) <% end %> \ No newline at end of file diff --git a/lib/generators/templates/helpers/spec_helper.tt b/lib/generators/templates/helpers/spec_helper.tt index 04949cbc..012616a3 100644 --- a/lib/generators/templates/helpers/spec_helper.tt +++ b/lib/generators/templates/helpers/spec_helper.tt @@ -5,6 +5,8 @@ require 'axe-rspec' <%- end -%> require 'rspec' require 'rspec/retry' +require 'digest' +require 'raider_reporter/rspec' require 'tmpdir' require_relative 'allure_helper' <%- if watir? -%> @@ -21,7 +23,8 @@ module SpecHelper config.verbose_retry = true config.display_try_failure_messages = true config.default_retry_count = ENV.fetch('RETRY_COUNT', 0).to_i - config.formatter = AllureHelper.formatter + config.add_formatter(AllureHelper.formatter) + config.add_formatter(RaiderReporter::RSpec::Formatter) <%- if watir? -%> config.include(BrowserHelper) <%- else -%> @@ -53,10 +56,12 @@ module SpecHelper example_name = example.description if example.exception @_exception = example.exception - end - Dir.mktmpdir do |temp_folder| - <%= partial('screenshot', strip: true) %> - AllureHelper.add_screenshot(example_name, screenshot) + Dir.mktmpdir do |temp_folder| + screenshot_path = "#{temp_folder}/#{example_name}.png" + <%= partial('screenshot', strip: true) %> + AllureHelper.add_screenshot(example_name, screenshot) + RaiderReporter.attach('screenshot', screenshot_path, type: :png) + end end <%= partial('quit_driver', strip: true) %> end diff --git a/lib/generators/templates/helpers/test_helper.tt b/lib/generators/templates/helpers/test_helper.tt index d4a794b1..6f76217d 100644 --- a/lib/generators/templates/helpers/test_helper.tt +++ b/lib/generators/templates/helpers/test_helper.tt @@ -74,6 +74,7 @@ module TestHelper <%- end -%> <%- end -%> Dir.mktmpdir do |temp_folder| + screenshot_path = "#{temp_folder}/#{example_name}.png" <%= partial('screenshot', strip: true) %> <%- if allure_reporter? -%> AllureHelper.add_screenshot(example_name, screenshot) diff --git a/lib/version b/lib/version index e4604e3a..15a27998 100644 --- a/lib/version +++ b/lib/version @@ -1 +1 @@ -3.2.1 +3.3.0 diff --git a/spec/integration/content/ci_content_spec.rb b/spec/integration/content/ci_content_spec.rb index 0ec7a7f6..93a493f4 100644 --- a/spec/integration/content/ci_content_spec.rb +++ b/spec/integration/content/ci_content_spec.rb @@ -44,21 +44,106 @@ end end + shared_examples 'github triggers' do |project_name, file| + subject(:pipeline) { read_generated(project_name, ".github/workflows/#{file}") } + + let(:triggers) { YAML.safe_load(pipeline)[true] } + + it 'triggers on push to main and master' do + expect(triggers['push']['branches']).to include('main', 'master') + end + + it 'triggers on pull_request' do + expect(triggers).to have_key('pull_request') + end + + it 'triggers on workflow_dispatch' do + expect(triggers).to have_key('workflow_dispatch') + end + end + + shared_examples 'github raider wiring' do |project_name, file| + subject(:pipeline) { read_generated(project_name, ".github/workflows/#{file}") } + + let(:steps) { YAML.safe_load(pipeline)['jobs'].values.flat_map { |job| job['steps'] } } + + let(:test_steps) do + steps.select { |step| step['run'].to_s.include?('rspec') || step['with'].to_h['script'].to_s.include?('rspec') } + end + + it 'passes Raider settings to every test step' do + expect(test_steps).not_to be_empty + test_steps.each do |step| + expect(step['env']).to include('RAIDER_TOKEN' => '${{ secrets.RAIDER_TOKEN }}', + 'RAIDER_ENDPOINT' => '${{ vars.RAIDER_ENDPOINT }}') + end + end + + it 'uploads raider-results as an artifact even on failure in every job' do + uploads = steps.select { |step| step['uses'] == 'actions/upload-artifact@v4' } + expect(uploads.size).to eq(YAML.safe_load(pipeline)['jobs'].size) + uploads.each do |upload| + expect(upload['with']['path']).to eq('raider-results/') + expect(upload['if']).to eq('always()') + end + end + end + + shared_examples 'github without raider wiring' do |project_name, file| + subject(:pipeline) { read_generated(project_name, ".github/workflows/#{file}") } + + it 'has no Raider settings or artifact' do + expect(pipeline).not_to include('RAIDER_') + expect(pipeline).not_to include('raider-results') + end + end + + shared_examples 'github allure steps' do |project_name| + subject(:pipeline) { read_generated(project_name, '.github/workflows/test_pipeline.yml') } + + it 'keeps the Allure steps' do + expect(pipeline).to include('simple-elf/allure-report-action', 'peaceiris/actions-gh-pages') + end + end + context 'with rspec and selenium' do name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}" include_examples 'valid github pipeline', name include_examples 'github rspec pipeline', name + include_examples 'github triggers', name, 'test_pipeline.yml' + include_examples 'github raider wiring', name, 'test_pipeline.yml' + include_examples 'github allure steps', name end context 'with cucumber and selenium' do name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}" include_examples 'valid github pipeline', name include_examples 'github cucumber pipeline', name + include_examples 'github triggers', name, 'test_pipeline.yml' + include_examples 'github without raider wiring', name, 'test_pipeline.yml' + include_examples 'github allure steps', name end context 'with rspec and watir' do name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::WATIR}" include_examples 'valid github pipeline', name include_examples 'github rspec pipeline', name + include_examples 'github triggers', name, 'test_pipeline.yml' + include_examples 'github raider wiring', name, 'test_pipeline.yml' + include_examples 'github allure steps', name + end + + %w[android ios cross_platform].each do |automation| + context "with rspec and #{automation}" do + name = "#{FrameworkIndex::RSPEC}_#{automation}" + include_examples 'github triggers', name, 'appium_pipeline.yml' + include_examples 'github raider wiring', name, 'appium_pipeline.yml' + end + + context "with cucumber and #{automation}" do + name = "#{FrameworkIndex::CUCUMBER}_#{automation}" + include_examples 'github triggers', name, 'appium_pipeline.yml' + include_examples 'github without raider wiring', name, 'appium_pipeline.yml' + end end end diff --git a/spec/integration/content/common_content_spec.rb b/spec/integration/content/common_content_spec.rb index c3696822..6bfd3598 100644 --- a/spec/integration/content/common_content_spec.rb +++ b/spec/integration/content/common_content_spec.rb @@ -89,6 +89,10 @@ it 'uses documentation format' do expect(rspec_file).to include('--format documentation') end + + it 'leaves the raider formatter to spec_helper, since a command-line --format replaces .rspec formatters' do + expect(rspec_file).not_to include('RaiderReporter') + end end shared_examples 'no rspec dotfile' do |project_name| @@ -109,6 +113,10 @@ it 'includes spec/examples.txt' do expect(gitignore).to include('spec/examples.txt') end + + it 'includes raider-results' do + expect(gitignore).to include('raider-results') + end end shared_examples 'non-rspec gitignore' do |project_name| @@ -121,6 +129,10 @@ it 'does not include spec/examples.txt' do expect(gitignore).not_to include('spec/examples.txt') end + + it 'does not include raider-results' do + expect(gitignore).not_to include('raider-results') + end end # --- Spec helper retry config (C4) --- @@ -220,5 +232,4 @@ it_behaves_like 'gemfile without rspec-retry', name it_behaves_like 'cucumber config with retry', name end - end diff --git a/spec/integration/content/gemfile_content_spec.rb b/spec/integration/content/gemfile_content_spec.rb index f937eb33..79007c19 100644 --- a/spec/integration/content/gemfile_content_spec.rb +++ b/spec/integration/content/gemfile_content_spec.rb @@ -119,4 +119,42 @@ include_examples 'contains watir gems', name end + describe 'raider_reporter and ruby_raider pin' do + major_minor = File.read('lib/version').strip.split('.').first(2).join('.') + + AUTOMATION_TYPES.each do |automation| + context "with rspec and #{automation}" do + let(:gemfile) { read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'Gemfile') } + + it 'includes raider_reporter' do + expect(gemfile).to include("gem 'raider_reporter', '~> 0.2'") + end + + it 'keeps allure gems' do + expect(gemfile).to include("gem 'allure-rspec', '2.28.0'", "gem 'allure-ruby-commons', '2.28.0'") + end + + it 'pins ruby_raider to the generator major.minor' do + expect(gemfile).to include("gem 'ruby_raider', '~> #{major_minor}'") + end + end + + context "with cucumber and #{automation}" do + let(:gemfile) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Gemfile') } + + it 'does not include raider_reporter' do + expect(gemfile).not_to include('raider_reporter') + end + + it 'keeps allure-cucumber' do + expect(gemfile).to include("gem 'allure-cucumber', '2.28.0'") + end + + it 'pins ruby_raider to the generator major.minor' do + expect(gemfile).to include("gem 'ruby_raider', '~> #{major_minor}'") + end + end + end + end + end diff --git a/spec/integration/content/helper_content_spec.rb b/spec/integration/content/helper_content_spec.rb index 023d20fc..7bea8f1d 100644 --- a/spec/integration/content/helper_content_spec.rb +++ b/spec/integration/content/helper_content_spec.rb @@ -109,6 +109,47 @@ end end + shared_examples 'raider reporter spec helper' do |project_name, screenshot_call| + subject(:helper) { read_generated(project_name, 'helpers/spec_helper.rb') } + + it 'requires raider_reporter/rspec' do + expect(helper).to include("require 'raider_reporter/rspec'") + end + + it 'requires digest, which allure-ruby-commons uses without requiring' do + expect(helper).to include("require 'digest'") + end + + it 'adds the raider formatter in spec_helper so a command-line --format cannot drop it' do + expect(helper).to include('config.add_formatter(RaiderReporter::RSpec::Formatter)') + end + + it 'does not replace the formatter' do + expect(helper).not_to match(/config\.formatter\s*=/) + end + + it 'adds the allure formatter' do + expect(helper).to include('config.add_formatter(AllureHelper.formatter)') + end + + it 'screenshots only on failure' do + lines = helper.lines + start = lines.index { |line| line.include?('if example.exception') } + expect(start).not_to be_nil + indent = lines[start][/\A */].size + body = lines[(start + 1)..].take_while { |line| line.strip.empty? || line[/\A */].size > indent } + guarded = body.join + + expect(guarded).to include('AllureHelper.add_screenshot', 'RaiderReporter.attach', screenshot_call) + expect(lines.select { |line| line.include?('AllureHelper.add_screenshot') }.size).to eq(1) + expect(lines.select { |line| line.include?(screenshot_call) }.size).to eq(1) + end + + it 'attaches the screenshot to raider by path' do + expect(helper).to include("RaiderReporter.attach('screenshot', screenshot_path, type: :png)") + end + end + shared_examples 'selenium spec helper' do |project_name| subject(:helper) { read_generated(project_name, 'helpers/spec_helper.rb') } @@ -200,4 +241,25 @@ name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::IOS}" end + + # --- Raider reporter wiring (every rspec automation) --- + + AUTOMATION_TYPES.each do |automation| + context "with rspec and #{automation} raider reporter wiring" do + name = "#{FrameworkIndex::RSPEC}_#{automation}" + screenshot_call = { + 'selenium' => 'driver.save_screenshot(screenshot_path)', + 'watir' => 'browser.screenshot.save(screenshot_path)' + }.fetch(automation, 'driver.screenshot(screenshot_path)') + it_behaves_like 'raider reporter spec helper', name, screenshot_call + end + + context "with cucumber and #{automation}" do + it 'has no raider reporter wiring' do + project = "#{FrameworkIndex::CUCUMBER}_#{automation}" + files = Dir.glob("#{project}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) } + expect(files.select { |f| File.read(f).match?(/raider_reporter|RaiderReporter/) }).to be_empty + end + end + end end diff --git a/spec/integration/content/readme_content_spec.rb b/spec/integration/content/readme_content_spec.rb new file mode 100644 index 00000000..7253693f --- /dev/null +++ b/spec/integration/content/readme_content_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative 'content_helper' + +describe 'Generated Readme content' do + AUTOMATION_TYPES.each do |automation| + context "with rspec and #{automation}" do + subject(:readme) { read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'Readme.md') } + + it 'has a Reporting section' do + expect(readme).to include('## Reporting') + end + + it 'documents local results, upload settings and the raider_reporter README' do + expect(readme).to include('raider-results/', 'RAIDER_TOKEN', 'RAIDER_ENDPOINT', + 'https://github.com/RaiderHQ/raider_reporter') + end + + it 'says Allure is still produced' do + expect(readme).to match(/Allure/) + end + end + + context "with cucumber and #{automation}" do + it 'has no Reporting section' do + readme = read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Readme.md') + expect(readme).not_to include('RAIDER_TOKEN') + end + end + end +end diff --git a/spec/integration/end_to_end_spec.rb b/spec/integration/end_to_end_spec.rb index d9ff838e..4ff9cbe2 100644 --- a/spec/integration/end_to_end_spec.rb +++ b/spec/integration/end_to_end_spec.rb @@ -3,6 +3,7 @@ require_relative 'spec_helper' require 'timeout' require 'open3' +require 'json' # End-to-end integration tests that verify generated projects actually work # These tests: @@ -41,9 +42,19 @@ def run_in_project(project_name, command, timeout: 300) result end + # The generated Gemfile pins ruby_raider to this gem's major.minor, which may not be + # published yet (a release candidate branch). Resolve it from this checkout instead; + # the pin itself is covered by the Gemfile content specs. + def use_local_ruby_raider(project_name) + gemfile = File.join(project_name, 'Gemfile') + root = File.expand_path('../..', __dir__) + File.write(gemfile, File.read(gemfile).sub(/^gem 'ruby_raider'.*$/, "gem 'ruby_raider', path: '#{root}'")) + end + # Helper to install dependencies def bundle_install(project_name) puts "\n📦 Installing dependencies for #{project_name}..." + use_local_ruby_raider(project_name) result = run_in_project(project_name, 'bundle install --quiet', timeout: 180) unless result[:success] @@ -103,6 +114,25 @@ def run_cucumber(project_name) "STDOUT: #{result[:stdout]}\n" \ "STDERR: #{result[:stderr]}" end + + it 'writes raider-results/run.json listing every example without changing the exit status', :aggregate_failures, :slow do + skip 'Bundle install failed' unless bundle_install(project) + + FileUtils.rm_rf(File.join(project, 'raider-results')) + result = run_rspec(project) + summary = result[:stdout].match(/(\d+) examples?, (\d+) failures?/) + expect(summary).not_to be_nil, "No RSpec summary line.\nSTDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}" + examples = summary[1].to_i + failures = summary[2].to_i + + run_file = File.join(project, 'raider-results', 'run.json') + expect(File).to exist(run_file) + run = JSON.parse(File.read(run_file)) + expect(run['schema_version']).to eq(1) + expect(run['results'].size).to eq(examples) + expect(run['results'].count { |r| r['status'] == 'failed' }).to eq(failures) + expect(result[:exit_code]).to eq(failures.zero? ? 0 : 1) + end end # Shared example for Cucumber-based projects @@ -142,7 +172,6 @@ def run_cucumber(project_name) describe 'Watir + Cucumber' do include_examples 'executable cucumber project', 'cucumber_watir' end - end # Mobile and Visual frameworks require external services, so we only verify structure diff --git a/spec/system/support/system_test_helper.rb b/spec/system/support/system_test_helper.rb index 44ebf22d..f4cfcf21 100644 --- a/spec/system/support/system_test_helper.rb +++ b/spec/system/support/system_test_helper.rb @@ -15,6 +15,7 @@ def run_tests_with(framework, automation) Bundler.with_unbundled_env do Dir.chdir(project) do + use_checkout_ruby_raider result = run_command('bundle install --quiet') return result unless result[:success] @@ -25,6 +26,13 @@ def run_tests_with(framework, automation) private + # The generated Gemfile pins ruby_raider to this gem's major.minor, which may not be + # published yet. Resolve it from this checkout; the pin itself is covered by content specs. + def use_checkout_ruby_raider(root = File.expand_path('../../..', __dir__)) + gemfile = File.read('Gemfile') + File.write('Gemfile', gemfile.sub(/^gem 'ruby_raider'.*$/, "gem 'ruby_raider', path: '#{root}'")) + end + def run_command(command, env: {}) stdout, stderr, status = Open3.capture3(env, command) { success: status.success?, stdout:, stderr: }