ruby_raider 3.3.0 → 3.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f5fc3418c82e5a121ebecbc862acd716ded56f9d7dc49be670e9f0361917deb
4
- data.tar.gz: b7464935c9d32155bd30d80750c914e6fd3ba95b3db3d6ed8eb95d5461f86fcb
3
+ metadata.gz: 11203a49dfd79c493cfa08d13576ecd10933eea755664e75f569c4860d74294b
4
+ data.tar.gz: 7338d98246ce0e765cb805fbfdb64b1feff88d215aeaecc5cb91184901ea0906
5
5
  SHA512:
6
- metadata.gz: 827db06d8dab522391496aeedbff5e199119ac287e548241e1ce23bb9ff65616043c1626cb35226ce1230a8c154953c9eb0bc3f65d5a7abaa9ee323dfd76522d
7
- data.tar.gz: e37a2899b4e8f78c941dd0f570f2a5c2833a61cc74cb30e392cc7e53565f1df78374853b0fd73b38aeabb7d8849fd22210cf534e153f3c8d9664128bd1566b5a
6
+ metadata.gz: 6e4c33e6293efb67fbe0207e62b434a422337df81d8936ff579a776d23ed89cf3d47bb3d22637a6d4ab28082003db155cb2e75c553aa8dba8966cf494ab0e90f
7
+ data.tar.gz: 3eff51512305847c73c9dfd8c83582772fe03fdf232047073428583edd25be07f644e8c133862ae633027e6efae9353255d803b430b0ed44fb9f1384df45dd76
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ require:
2
2
  - rubocop-rspec
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 3.1.0
5
+ TargetRubyVersion: 3.2.0
6
6
 
7
7
  # Layout
8
8
  Layout/CaseIndentation:
data/README.md CHANGED
@@ -59,8 +59,8 @@ Every generated project comes with:
59
59
 
60
60
  - **Page Object Model** — Base page and component classes with example pages
61
61
  - **Test examples** — Working login tests against the [Raider Test Store](https://raider-test-site.onrender.com/)
62
- - **Allure reporting** — Pre-configured with screenshot capture on failure
63
- - **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
62
+ - **Allure reporting** (RSpec) — Pre-configured with screenshot capture on failure
63
+ - **Raider reporting** — RSpec and Cucumber 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
64
64
  - **GitHub Actions CI** — Ready-to-use test pipeline
65
65
  - **Rake tasks** — Default, smoke, and regression tasks with tag-based filtering
66
66
  - **Retry support** — RSpec retry with configurable retry count via `RETRY_COUNT` env var
data/RELEASE.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## Release Notes
4
4
 
5
+ ### 3.5.0
6
+
7
+ - Generated Cucumber projects now report through `raider_reporter` `~> 0.4` instead of Allure: results are written to `raider-results/` and uploaded when `RAIDER_TOKEN` and `RAIDER_ENDPOINT` are set. The reporter is activated by `require 'raider_reporter/cucumber'` in `features/support/env.rb`.
8
+ - Cucumber projects no longer include `allure-cucumber` or `helpers/allure_helper.rb`; failure screenshots are attached to Raider only, and `.gitignore` lists `raider-results` instead of `allure-results`.
9
+ - Generated Cucumber GitHub CI now passes the Raider settings to the test run, uploads `raider-results/` as an artifact and includes the `raider-comment` job, matching RSpec projects. The Allure report and gh-pages steps are removed for Cucumber. RSpec pipelines are unchanged.
10
+ - The generated Readme has a "Reporting" section for Cucumber projects too.
11
+ - Generated Gemfiles now require `raider_reporter` `~> 0.4`.
12
+
13
+ ### 3.4.0
14
+
15
+ - Generated RSpec projects with GitHub CI now include a `raider-comment` job that comments Raider test results on pull requests via `raider-reporter comment`. It runs after every test job, only on pull requests, and needs the `RAIDER_TOKEN` secret and `RAIDER_ENDPOINT` variable. Cucumber projects are unchanged.
16
+ - The job needs `contents: read`, `pull-requests: write` and `statuses: write` permissions; it reports a `raider/tests` status, and pull requests from forks are skipped.
17
+ - Generated Gemfiles now require `raider_reporter` `~> 0.3`.
18
+ - Ruby Raider now requires Ruby 3.2.0 or newer.
19
+
5
20
  ### 3.3.0
6
21
 
7
22
  - 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.
@@ -40,6 +40,8 @@ class CommonGenerator < Generator
40
40
  end
41
41
 
42
42
  def create_allure_folder
43
+ return if cucumber?
44
+
43
45
  empty_directory "#{name}/allure-results"
44
46
  end
45
47
  end
@@ -1 +1 @@
1
- default: --format AllureCucumber::CucumberFormatter --out allure-results --publish-quiet --retry 0
1
+ default: --format pretty --publish-quiet --retry 0
@@ -1,15 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'raider_reporter/cucumber'
1
4
  require 'tmpdir'
2
- require_relative '../../helpers/allure_helper'
3
5
 
4
6
  Before do
5
- AllureHelper.configure
6
7
  driver.start_driver
7
8
  end
8
9
 
9
10
  After do |scenario|
10
- Dir.mktmpdir do |temp_folder|
11
- screenshot = driver.screenshot("#{temp_folder}/#{scenario.name}.png")
12
- AllureHelper.add_screenshot(scenario.name, screenshot)
11
+ if scenario.failed?
12
+ Dir.mktmpdir do |temp_folder|
13
+ screenshot_path = File.join(temp_folder, 'screenshot.png')
14
+ driver.screenshot(screenshot_path)
15
+ RaiderReporter.attach('screenshot', screenshot_path, type: :png)
16
+ end
13
17
  end
18
+ ensure
14
19
  driver.quit_driver
15
20
  end
@@ -4,9 +4,9 @@
4
4
  require 'axe-rspec'
5
5
  <%- end -%>
6
6
  require 'rspec'
7
+ require 'raider_reporter/cucumber'
7
8
  require 'tmpdir'
8
9
  require 'yaml'
9
- require_relative '../../helpers/allure_helper'
10
10
 
11
11
  Before do
12
12
  viewport = YAML.load_file('config/config.yml')['viewport']
@@ -18,9 +18,13 @@ Before do
18
18
  end
19
19
 
20
20
  After do |scenario|
21
- Dir.mktmpdir do |temp_folder|
22
- screenshot = driver.save_screenshot("#{temp_folder}/#{scenario.name}.png")
23
- AllureHelper.add_screenshot(scenario.name, screenshot)
21
+ if scenario.failed?
22
+ Dir.mktmpdir do |temp_folder|
23
+ screenshot_path = File.join(temp_folder, 'screenshot.png')
24
+ driver.save_screenshot(screenshot_path)
25
+ RaiderReporter.attach('screenshot', screenshot_path, type: :png)
26
+ end
24
27
  end
28
+ ensure
25
29
  driver.quit
26
- end
30
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'raider_reporter/cucumber'
3
4
  require 'tmpdir'
4
5
  require 'yaml'
5
- require_relative '../../helpers/allure_helper'
6
6
 
7
7
  Before do
8
8
  viewport = YAML.load_file('config/config.yml')['viewport']
@@ -14,9 +14,13 @@ Before do
14
14
  end
15
15
 
16
16
  After do |scenario|
17
- Dir.mktmpdir do |temp_folder|
18
- screenshot = browser.screenshot.save("#{temp_folder}/#{scenario.name}.png")
19
- AllureHelper.add_screenshot(scenario.name, screenshot)
17
+ if scenario.failed?
18
+ Dir.mktmpdir do |temp_folder|
19
+ screenshot_path = File.join(temp_folder, 'screenshot.png')
20
+ browser.screenshot.save(screenshot_path)
21
+ RaiderReporter.attach('screenshot', screenshot_path, type: :png)
22
+ end
20
23
  end
24
+ ensure
21
25
  browser.quit
22
- end
26
+ end
@@ -14,6 +14,8 @@ class HelpersGenerator < Generator
14
14
  private
15
15
 
16
16
  def generate_allure_helper
17
+ return if cucumber?
18
+
17
19
  template('helpers/allure_helper.tt', "#{name}/helpers/allure_helper.rb")
18
20
  end
19
21
 
@@ -33,19 +33,18 @@ jobs:
33
33
  with:
34
34
  chrome-version: stable
35
35
 
36
+ <%- unless framework == 'cucumber' -%>
36
37
  - name: Create allure-results folder
37
38
  run: mkdir -p allure-results
38
39
 
40
+ <%- end -%>
39
41
  - name: Run tests
40
42
  run: <%- if framework == 'cucumber' -%>bundle exec cucumber features --format pretty<%- else -%>bundle exec rspec spec --format documentation<%- end %>
41
43
  env:
42
44
  HEADLESS: true
43
- <%- unless framework == 'cucumber' -%>
44
45
  RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
45
46
  RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
46
- <%- end -%>
47
47
 
48
- <%- unless framework == 'cucumber' -%>
49
48
  - name: Upload Raider results
50
49
  uses: actions/upload-artifact@v4
51
50
  if: always()
@@ -54,8 +53,7 @@ jobs:
54
53
  path: raider-results/
55
54
  if-no-files-found: ignore
56
55
 
57
- <%- end -%>
58
-
56
+ <%- unless framework == 'cucumber' -%>
59
57
  - name: Get Allure history
60
58
  uses: actions/checkout@v2
61
59
  if: always()
@@ -81,3 +79,23 @@ jobs:
81
79
  PERSONAL_TOKEN: ${{ secrets.GITHUB_TOKEN }}
82
80
  PUBLISH_BRANCH: gh-pages
83
81
  PUBLISH_DIR: allure-history
82
+
83
+ <%- end -%>
84
+ raider-comment:
85
+ needs: [build]
86
+ if: always() && github.event_name == 'pull_request'
87
+ runs-on: ubuntu-latest
88
+ permissions:
89
+ contents: read
90
+ pull-requests: write
91
+ statuses: write
92
+ steps:
93
+ - uses: actions/checkout@v4
94
+ - uses: ruby/setup-ruby@v1
95
+ with: { ruby-version: 3.4.0, bundler-cache: true }
96
+ - name: Comment Raider results on the pull request
97
+ run: bundle exec raider-reporter comment
98
+ env:
99
+ RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
100
+ RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
101
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -52,7 +52,6 @@ jobs:
52
52
  <%- end -%>
53
53
  env:
54
54
  APP_PATH: ${{ github.workspace }}/apps/RaiderTestApp.app
55
- <%- unless cucumber? -%>
56
55
  RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
57
56
  RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
58
57
 
@@ -64,7 +63,6 @@ jobs:
64
63
  path: raider-results/
65
64
  if-no-files-found: ignore
66
65
  <%- end -%>
67
- <%- end -%>
68
66
 
69
67
  <%- if android? || cross_platform? -%>
70
68
  android-tests:
@@ -113,7 +111,6 @@ jobs:
113
111
  <%- end -%>
114
112
  env:
115
113
  APP_PATH: ${{ github.workspace }}/apps/app-release.apk
116
- <%- unless cucumber? -%>
117
114
  RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
118
115
  RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
119
116
 
@@ -125,4 +122,22 @@ jobs:
125
122
  path: raider-results/
126
123
  if-no-files-found: ignore
127
124
  <%- end -%>
128
- <%- end -%>
125
+
126
+ raider-comment:
127
+ needs: [<%= [('ios-tests' if ios? || cross_platform?), ('android-tests' if android? || cross_platform?)].compact.join(', ') %>]
128
+ if: always() && github.event_name == 'pull_request'
129
+ runs-on: ubuntu-latest
130
+ permissions:
131
+ contents: read
132
+ pull-requests: write
133
+ statuses: write
134
+ steps:
135
+ - uses: actions/checkout@v4
136
+ - uses: ruby/setup-ruby@v1
137
+ with: { ruby-version: 3.4.0, bundler-cache: true }
138
+ - name: Comment Raider results on the pull request
139
+ run: bundle exec raider-reporter comment
140
+ env:
141
+ RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
142
+ RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
143
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -5,9 +5,7 @@ gem 'activesupport'
5
5
  gem 'appium_lib'
6
6
  gem 'appium_console'
7
7
  <% end %>
8
- <%- if cucumber? -%>
9
- gem 'allure-cucumber', '2.28.0'
10
- <%- else -%>
8
+ <%- if rspec? -%>
11
9
  gem 'allure-rspec', '2.28.0'
12
10
  gem 'allure-ruby-commons', '2.28.0'
13
11
  <%- end -%>
@@ -19,8 +17,8 @@ gem 'parallel_split_test'
19
17
  gem 'parallel_tests'
20
18
  gem 'rake'
21
19
  gem 'reek'
20
+ gem 'raider_reporter', '~> 0.4'
22
21
  <%- if rspec? -%>
23
- gem 'raider_reporter', '~> 0.2'
24
22
  gem 'rspec-retry'
25
23
  <%- end -%>
26
24
  gem '<%= framework %>'
@@ -1,5 +1,7 @@
1
- allure-results
2
1
  <%- if rspec? -%>
2
+ allure-results
3
+ <%- end -%>
3
4
  raider-results
5
+ <%- if rspec? -%>
4
6
  spec/examples.txt
5
7
  <%- end -%>
@@ -82,11 +82,15 @@ raider g steps [NAME] # Create step definitions
82
82
  raider g scaffold [NAME] # Create page + test + steps
83
83
  ```
84
84
 
85
- <%- if rspec? -%>
86
85
  ## Reporting
87
86
 
87
+ <%- if rspec? -%>
88
88
  Every run writes machine-readable results to `raider-results/` (including `run.json`), next to the usual Allure results.
89
89
  Allure reports are still produced.
90
+ <%- else -%>
91
+ Every run writes machine-readable results to `raider-results/` (including `run.json`). The reporter runs in plugin mode:
92
+ `features/support/env.rb` requires `raider_reporter/cucumber`, so no extra Cucumber formatter flag is needed.
93
+ <%- end -%>
90
94
 
91
95
  To upload runs to Raider, set these environment variables before running the suite:
92
96
 
@@ -95,10 +99,14 @@ export RAIDER_TOKEN=your-token
95
99
  export RAIDER_ENDPOINT=https://your-raider-endpoint
96
100
  ```
97
101
 
102
+ <%- if rspec? -%>
98
103
  Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. Failure screenshots are attached to both
99
104
  Allure and Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details.
100
-
105
+ <%- else -%>
106
+ Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. Failure screenshots are attached to
107
+ Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details.
101
108
  <%- end -%>
109
+
102
110
  ### Sponsors
103
111
 
104
112
  This project is tested with BrowserStack.
data/lib/version CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 3.5.0
data/ruby_raider.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.email = 'agustin.pe94@gmail.com'
10
10
  s.homepage = 'https://github.com/RubyRaider/ruby_raider'
11
11
  s.license = 'MIT'
12
- s.required_ruby_version = '>= 3.1.0'
12
+ s.required_ruby_version = '>= 3.2.0'
13
13
  s.files = `git ls-files -z`.split("\x0")
14
14
  s.bindir = 'bin'
15
15
  s.executables << 'raider'
@@ -62,13 +62,13 @@ describe 'CI pipeline content' do
62
62
  end
63
63
  end
64
64
 
65
- shared_examples 'github raider wiring' do |project_name, file|
65
+ shared_examples 'github raider wiring' do |project_name, file, runner = 'rspec'|
66
66
  subject(:pipeline) { read_generated(project_name, ".github/workflows/#{file}") }
67
67
 
68
68
  let(:steps) { YAML.safe_load(pipeline)['jobs'].values.flat_map { |job| job['steps'] } }
69
69
 
70
70
  let(:test_steps) do
71
- steps.select { |step| step['run'].to_s.include?('rspec') || step['with'].to_h['script'].to_s.include?('rspec') }
71
+ steps.select { |step| step['run'].to_s.include?(runner) || step['with'].to_h['script'].to_s.include?(runner) }
72
72
  end
73
73
 
74
74
  it 'passes Raider settings to every test step' do
@@ -81,7 +81,7 @@ describe 'CI pipeline content' do
81
81
 
82
82
  it 'uploads raider-results as an artifact even on failure in every job' do
83
83
  uploads = steps.select { |step| step['uses'] == 'actions/upload-artifact@v4' }
84
- expect(uploads.size).to eq(YAML.safe_load(pipeline)['jobs'].size)
84
+ expect(uploads.size).to eq(YAML.safe_load(pipeline)['jobs'].except('raider-comment').size)
85
85
  uploads.each do |upload|
86
86
  expect(upload['with']['path']).to eq('raider-results/')
87
87
  expect(upload['if']).to eq('always()')
@@ -89,12 +89,42 @@ describe 'CI pipeline content' do
89
89
  end
90
90
  end
91
91
 
92
- shared_examples 'github without raider wiring' do |project_name, file|
92
+ shared_examples 'github raider-comment job' do |project_name, file, test_jobs|
93
93
  subject(:pipeline) { read_generated(project_name, ".github/workflows/#{file}") }
94
94
 
95
- it 'has no Raider settings or artifact' do
96
- expect(pipeline).not_to include('RAIDER_')
97
- expect(pipeline).not_to include('raider-results')
95
+ let(:jobs) { YAML.safe_load(pipeline)['jobs'] }
96
+ let(:job) { jobs['raider-comment'] }
97
+
98
+ it 'is a valid workflow with exactly the expected jobs' do
99
+ expect(jobs.keys).to match_array(test_jobs + ['raider-comment'])
100
+ end
101
+
102
+ it 'needs every test job' do
103
+ expect(job['needs']).to match_array(test_jobs)
104
+ end
105
+
106
+ it 'runs always, on pull requests only' do
107
+ expect(job['if']).to eq("always() && github.event_name == 'pull_request'")
108
+ expect(job['runs-on']).to eq('ubuntu-latest')
109
+ end
110
+
111
+ it 'has the least permissions required' do
112
+ expect(job['permissions']).to eq('contents' => 'read', 'pull-requests' => 'write', 'statuses' => 'write')
113
+ end
114
+
115
+ it 'checks out, sets up the same Ruby as the test jobs and comments' do
116
+ steps = job['steps']
117
+ expect(steps[0]).to eq('uses' => 'actions/checkout@v4')
118
+ expect(steps[1]['uses']).to eq('ruby/setup-ruby@v1')
119
+ test_ruby = jobs.values_at(*test_jobs).flat_map { |j| j['steps'] }
120
+ .find { |step| step['uses'] == 'ruby/setup-ruby@v1' }['with']['ruby-version']
121
+ expect(steps[1]['with']).to eq('ruby-version' => test_ruby, 'bundler-cache' => true)
122
+ expect(steps[2]).to eq('name' => 'Comment Raider results on the pull request',
123
+ 'run' => 'bundle exec raider-reporter comment',
124
+ 'env' => { 'RAIDER_TOKEN' => '${{ secrets.RAIDER_TOKEN }}',
125
+ 'RAIDER_ENDPOINT' => '${{ vars.RAIDER_ENDPOINT }}',
126
+ 'GITHUB_TOKEN' => '${{ secrets.GITHUB_TOKEN }}' })
127
+ expect(steps.size).to eq(3)
98
128
  end
99
129
  end
100
130
 
@@ -106,12 +136,21 @@ describe 'CI pipeline content' do
106
136
  end
107
137
  end
108
138
 
139
+ shared_examples 'github without allure steps' do |project_name|
140
+ subject(:pipeline) { read_generated(project_name, '.github/workflows/test_pipeline.yml') }
141
+
142
+ it 'has no Allure report or gh-pages steps' do
143
+ expect(pipeline).not_to match(/allure|gh-pages/i)
144
+ end
145
+ end
146
+
109
147
  context 'with rspec and selenium' do
110
148
  name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::SELENIUM}"
111
149
  include_examples 'valid github pipeline', name
112
150
  include_examples 'github rspec pipeline', name
113
151
  include_examples 'github triggers', name, 'test_pipeline.yml'
114
152
  include_examples 'github raider wiring', name, 'test_pipeline.yml'
153
+ include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
115
154
  include_examples 'github allure steps', name
116
155
  end
117
156
 
@@ -120,8 +159,9 @@ describe 'CI pipeline content' do
120
159
  include_examples 'valid github pipeline', name
121
160
  include_examples 'github cucumber pipeline', name
122
161
  include_examples 'github triggers', name, 'test_pipeline.yml'
123
- include_examples 'github without raider wiring', name, 'test_pipeline.yml'
124
- include_examples 'github allure steps', name
162
+ include_examples 'github raider wiring', name, 'test_pipeline.yml', 'cucumber'
163
+ include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
164
+ include_examples 'github without allure steps', name
125
165
  end
126
166
 
127
167
  context 'with rspec and watir' do
@@ -130,12 +170,27 @@ describe 'CI pipeline content' do
130
170
  include_examples 'github rspec pipeline', name
131
171
  include_examples 'github triggers', name, 'test_pipeline.yml'
132
172
  include_examples 'github raider wiring', name, 'test_pipeline.yml'
173
+ include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
133
174
  include_examples 'github allure steps', name
134
175
  end
135
176
 
136
- %w[android ios cross_platform].each do |automation|
177
+ context 'with cucumber and watir' do
178
+ name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::WATIR}"
179
+ include_examples 'valid github pipeline', name
180
+ include_examples 'github cucumber pipeline', name
181
+ include_examples 'github triggers', name, 'test_pipeline.yml'
182
+ include_examples 'github raider wiring', name, 'test_pipeline.yml', 'cucumber'
183
+ include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
184
+ include_examples 'github without allure steps', name
185
+ end
186
+
187
+ appium_jobs = { 'android' => %w[android-tests], 'ios' => %w[ios-tests],
188
+ 'cross_platform' => %w[ios-tests android-tests] }
189
+
190
+ appium_jobs.each do |automation, test_jobs|
137
191
  context "with rspec and #{automation}" do
138
192
  name = "#{FrameworkIndex::RSPEC}_#{automation}"
193
+ include_examples 'github raider-comment job', name, 'appium_pipeline.yml', test_jobs
139
194
  include_examples 'github triggers', name, 'appium_pipeline.yml'
140
195
  include_examples 'github raider wiring', name, 'appium_pipeline.yml'
141
196
  end
@@ -143,7 +198,8 @@ describe 'CI pipeline content' do
143
198
  context "with cucumber and #{automation}" do
144
199
  name = "#{FrameworkIndex::CUCUMBER}_#{automation}"
145
200
  include_examples 'github triggers', name, 'appium_pipeline.yml'
146
- include_examples 'github without raider wiring', name, 'appium_pipeline.yml'
201
+ include_examples 'github raider wiring', name, 'appium_pipeline.yml', 'cucumber'
202
+ include_examples 'github raider-comment job', name, 'appium_pipeline.yml', test_jobs
147
203
  end
148
204
  end
149
205
  end
@@ -122,16 +122,16 @@ describe 'Common file content' do
122
122
  shared_examples 'non-rspec gitignore' do |project_name|
123
123
  subject(:gitignore) { read_generated(project_name, '.gitignore') }
124
124
 
125
- it 'includes allure-results' do
126
- expect(gitignore).to include('allure-results')
125
+ it 'does not include allure-results' do
126
+ expect(gitignore).not_to include('allure-results')
127
127
  end
128
128
 
129
129
  it 'does not include spec/examples.txt' do
130
130
  expect(gitignore).not_to include('spec/examples.txt')
131
131
  end
132
132
 
133
- it 'does not include raider-results' do
134
- expect(gitignore).not_to include('raider-results')
133
+ it 'includes raider-results' do
134
+ expect(gitignore).to include('raider-results')
135
135
  end
136
136
  end
137
137
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'content_helper'
4
+
5
+ describe 'Cucumber raider_reporter wiring' do
6
+ screenshot_calls = {
7
+ 'selenium' => 'driver.save_screenshot(screenshot_path)',
8
+ 'watir' => 'browser.screenshot.save(screenshot_path)'
9
+ }
10
+
11
+ AUTOMATION_TYPES.each do |automation|
12
+ context "with cucumber and #{automation}" do
13
+ let(:project_name) { "#{FrameworkIndex::CUCUMBER}_#{automation}" }
14
+ let(:env) { read_generated(project_name, 'features/support/env.rb') }
15
+ let(:screenshot_call) { screenshot_calls.fetch(automation, 'driver.screenshot(screenshot_path)') }
16
+ let(:hook) { env[/^\s*After do.*\z/m] }
17
+ let(:quit_call) { automation == 'watir' ? 'browser.quit' : 'driver.quit' }
18
+
19
+ it 'activates the reporter by requiring raider_reporter/cucumber' do
20
+ expect(env).to include("require 'raider_reporter/cucumber'")
21
+ end
22
+
23
+ it 'does not reference the allure helper' do
24
+ expect(env).not_to match(/AllureHelper|allure_helper/)
25
+ end
26
+
27
+ it 'takes the screenshot only for failed scenarios, inside the guard block' do
28
+ guarded = /if scenario\.failed\?\n\s+Dir\.mktmpdir do \|temp_folder\|\n(?<body>.*?)\n\s+end\n\s+end\nensure/m
29
+ expect(hook.match(guarded)[:body]).to include(screenshot_call)
30
+ end
31
+
32
+ it 'uses a fixed file name in the tmpdir, never the scenario name' do
33
+ expect(env).to include("screenshot_path = File.join(temp_folder, 'screenshot.png')")
34
+ expect(hook).not_to include('scenario.name')
35
+ end
36
+
37
+ it 'quits the driver in an ensure of the After block, without a redundant begin' do
38
+ quit = %w[android ios cross_platform].include?(automation) ? 'driver.quit_driver' : quit_call
39
+ expect(hook).to match(/^\s*After do \|scenario\|\n.*\n^ensure\n\s+#{Regexp.escape(quit)}\nend/m)
40
+ expect(hook).not_to match(/^\s+begin$/)
41
+ end
42
+
43
+ it 'attaches the screenshot path before quitting the driver' do
44
+ attach = "RaiderReporter.attach('screenshot', screenshot_path, type: :png)"
45
+ expect(env).to include(attach)
46
+ expect(env.index(screenshot_call)).to be < env.index(attach)
47
+ expect(env.index(attach)).to be < env.index(/driver\.quit|browser\.quit/, env.index(attach))
48
+ end
49
+
50
+ it 'does not generate the allure helper' do
51
+ expect(File).not_to exist(File.join(project_name, 'helpers/allure_helper.rb'))
52
+ end
53
+
54
+ it 'does not mention Allure in any generated file' do
55
+ files = Dir.glob("#{project_name}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) }
56
+ expect(files.select { |f| File.read(f).match?(/allure/i) }).to be_empty
57
+ end
58
+
59
+ it 'does not create an allure-results folder' do
60
+ expect(File).not_to exist(File.join(project_name, 'allure-results'))
61
+ end
62
+ end
63
+ end
64
+ end
@@ -58,8 +58,8 @@ describe 'Gemfile content' do
58
58
  expect(gemfile).to include("gem 'cucumber'")
59
59
  end
60
60
 
61
- it 'includes allure-cucumber' do
62
- expect(gemfile).to include("gem 'allure-cucumber'")
61
+ it 'does not include allure-cucumber' do
62
+ expect(gemfile).not_to include('allure')
63
63
  end
64
64
 
65
65
  it 'includes rspec as cucumber dependency' do
@@ -127,7 +127,7 @@ describe 'Gemfile content' do
127
127
  let(:gemfile) { read_generated("#{FrameworkIndex::RSPEC}_#{automation}", 'Gemfile') }
128
128
 
129
129
  it 'includes raider_reporter' do
130
- expect(gemfile).to include("gem 'raider_reporter', '~> 0.2'")
130
+ expect(gemfile).to include("gem 'raider_reporter', '~> 0.4'")
131
131
  end
132
132
 
133
133
  it 'keeps allure gems' do
@@ -142,12 +142,12 @@ describe 'Gemfile content' do
142
142
  context "with cucumber and #{automation}" do
143
143
  let(:gemfile) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Gemfile') }
144
144
 
145
- it 'does not include raider_reporter' do
146
- expect(gemfile).not_to include('raider_reporter')
145
+ it 'includes raider_reporter' do
146
+ expect(gemfile).to include("gem 'raider_reporter', '~> 0.4'")
147
147
  end
148
148
 
149
- it 'keeps allure-cucumber' do
150
- expect(gemfile).to include("gem 'allure-cucumber', '2.28.0'")
149
+ it 'drops allure-cucumber' do
150
+ expect(gemfile).not_to include('allure')
151
151
  end
152
152
 
153
153
  it 'pins ruby_raider to the generator major.minor' do
@@ -221,13 +221,11 @@ describe 'Helper file content' do
221
221
  name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::SELENIUM}"
222
222
  it_behaves_like 'valid driver helper', name
223
223
  it_behaves_like 'selenium driver helper', name
224
- it_behaves_like 'valid allure helper', name
225
224
  end
226
225
 
227
226
  context 'with cucumber and watir' do
228
227
  name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::WATIR}"
229
228
  it_behaves_like 'valid browser helper', name
230
- it_behaves_like 'valid allure helper', name
231
229
  end
232
230
 
233
231
  # --- Mobile contexts (with AppiumRecorder) ---
@@ -255,10 +253,8 @@ describe 'Helper file content' do
255
253
  end
256
254
 
257
255
  context "with cucumber and #{automation}" do
258
- it 'has no raider reporter wiring' do
259
- project = "#{FrameworkIndex::CUCUMBER}_#{automation}"
260
- files = Dir.glob("#{project}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) }
261
- expect(files.select { |f| File.read(f).match?(/raider_reporter|RaiderReporter/) }).to be_empty
256
+ it 'does not generate the allure helper' do
257
+ expect(File).not_to exist("#{FrameworkIndex::CUCUMBER}_#{automation}/helpers/allure_helper.rb")
262
258
  end
263
259
  end
264
260
  end
@@ -22,9 +22,19 @@ describe 'Generated Readme content' do
22
22
  end
23
23
 
24
24
  context "with cucumber and #{automation}" do
25
- it 'has no Reporting section' do
26
- readme = read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Readme.md')
27
- expect(readme).not_to include('RAIDER_TOKEN')
25
+ subject(:readme) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Readme.md') }
26
+
27
+ it 'has a Reporting section' do
28
+ expect(readme).to include('## Reporting')
29
+ end
30
+
31
+ it 'documents local results, upload settings, plugin mode and the raider_reporter README' do
32
+ expect(readme).to include('raider-results/', 'RAIDER_TOKEN', 'RAIDER_ENDPOINT', 'features/support/env.rb',
33
+ 'https://github.com/RaiderHQ/raider_reporter')
34
+ end
35
+
36
+ it 'does not mention Allure' do
37
+ expect(readme).not_to match(/allure/i)
28
38
  end
29
39
  end
30
40
  end
@@ -110,6 +110,10 @@ describe 'Test file content' do
110
110
  it 'has default profile' do
111
111
  expect(config).to include('default:')
112
112
  end
113
+
114
+ it 'uses the pretty formatter, with the reporter activated from env.rb' do
115
+ expect(config.strip).to eq('default: --format pretty --publish-quiet --retry 0')
116
+ end
113
117
  end
114
118
 
115
119
  # --- RSpec contexts ---
@@ -84,7 +84,7 @@ describe 'End-to-End Project Generation and Execution' do
84
84
  # Helper to run Cucumber tests
85
85
  def run_cucumber(project_name)
86
86
  puts "\n🥒 Running Cucumber tests in #{project_name}..."
87
- result = run_in_project(project_name, 'bundle exec cucumber features/ --format pretty', timeout: 120)
87
+ result = run_in_project(project_name, 'HEADLESS=true bundle exec cucumber features/ --format pretty', timeout: 120)
88
88
 
89
89
  puts result[:stdout] if result[:stdout].length.positive?
90
90
 
@@ -104,6 +104,15 @@ describe 'End-to-End Project Generation and Execution' do
104
104
  expect(bundle_install(project)).to be true
105
105
  end
106
106
 
107
+ it 'resolves raider_reporter 0.4 from RubyGems and ships the raider-reporter executable', :aggregate_failures do
108
+ skip 'Bundle install failed' unless bundle_install(project)
109
+
110
+ expect(File.read(File.join(project, 'Gemfile.lock'))).to match(/^ raider_reporter \(0\.4\.\d+\)$/)
111
+ result = run_in_project(project, 'bundle exec raider-reporter --help', timeout: 60)
112
+ expect(result[:exit_code]).to eq(0), "STDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}"
113
+ expect(result[:stdout] + result[:stderr]).to include('raider-reporter comment')
114
+ end
115
+
107
116
  it 'runs generated RSpec tests successfully', :slow do
108
117
  skip 'Bundle install failed' unless bundle_install(project)
109
118
 
@@ -153,6 +162,85 @@ describe 'End-to-End Project Generation and Execution' do
153
162
  "STDOUT: #{result[:stdout]}\n" \
154
163
  "STDERR: #{result[:stderr]}"
155
164
  end
165
+
166
+ it 'resolves raider_reporter 0.4 from RubyGems and requires its Cucumber plugin', :aggregate_failures do
167
+ skip 'Bundle install failed' unless bundle_install(project)
168
+
169
+ lockfile = File.read(File.join(project, 'Gemfile.lock'))
170
+ expect(lockfile).to match(/^ raider_reporter \(0\.4\.\d+\)$/)
171
+ expect(File.read(File.join(project, 'features/support/env.rb'))).to include("require 'raider_reporter/cucumber'")
172
+ end
173
+
174
+ it 'prints pretty output and keeps the exit status of a failing Cucumber run', :aggregate_failures, :slow do
175
+ skip 'Bundle install failed' unless bundle_install(project)
176
+
177
+ result = run_cucumber_with_failure(project)
178
+
179
+ expect(result[:stdout]).to include('Feature: Reporter failure')
180
+ expect(result[:stdout]).to include('Scenario: A deliberately failing scenario')
181
+ expect(result[:stdout]).to match(/\d+ scenarios? \(\d+ failed/)
182
+ expect(result[:exit_code]).to eq(1)
183
+ end
184
+
185
+ it 'writes raider-results/run.json listing every scenario with a failing scenario screenshot',
186
+ :aggregate_failures, :slow do
187
+ skip 'Bundle install failed' unless bundle_install(project)
188
+
189
+ result = run_cucumber_with_failure(project)
190
+ summary = result[:stdout].match(/(\d+) scenarios? \((?:(\d+) failed)?/)
191
+ expect(summary).not_to be_nil, "No Cucumber summary line.\nSTDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}"
192
+
193
+ run = JSON.parse(File.read(File.join(project, 'raider-results', 'run.json')))
194
+ expect(run['schema_version']).to eq(1)
195
+ expect(run['results'].size).to eq(summary[1].to_i)
196
+ expect(run['results'].count { |r| r['status'] == 'failed' }).to eq(summary[2].to_i)
197
+
198
+ failing = run['results'].find { |r| r['full_name'] == 'Reporter failure: A deliberately failing scenario' }
199
+ expect(failing).not_to be_nil, "Scenario missing from run.json: #{run['results'].map { |r| r['full_name'] }}"
200
+ expect(failing['status']).to eq('failed')
201
+ expect(failing['file_path']).to eq('features/reporter_failure.feature')
202
+ expect(failing['failure']['message']).to start_with(
203
+ 'Then the reporter records this failure (features/reporter_failure.feature:5)'
204
+ )
205
+ screenshot = failing['attachments'].find { |a| a['name'] == 'screenshot' }
206
+ expect(screenshot).not_to be_nil, "No screenshot attachment: #{failing['attachments']}"
207
+ expect(screenshot['content_type']).to eq('image/png')
208
+ end
209
+ end
210
+
211
+ # Runs the project's features plus a deliberately failing scenario, then removes that scenario again
212
+ def run_cucumber_with_failure(project)
213
+ add_failing_cucumber_scenario(project)
214
+ FileUtils.rm_rf(File.join(project, 'raider-results'))
215
+ run_cucumber(project)
216
+ ensure
217
+ remove_failing_cucumber_scenario(project)
218
+ end
219
+
220
+ # Adds a scenario that always fails at its last step, after the browser has been started
221
+ def add_failing_cucumber_scenario(project)
222
+ File.write(File.join(project, 'features/reporter_failure.feature'), <<~FEATURE)
223
+ Feature: Reporter failure
224
+
225
+ Scenario: A deliberately failing scenario
226
+ Given the reporter has a started scenario
227
+ Then the reporter records this failure
228
+ FEATURE
229
+ File.write(File.join(project, 'features/step_definitions/reporter_failure_steps.rb'), <<~STEPS)
230
+ # frozen_string_literal: true
231
+
232
+ Given('the reporter has a started scenario') {}
233
+
234
+ Then('the reporter records this failure') do
235
+ raise 'deliberate failure for raider_reporter'
236
+ end
237
+ STEPS
238
+ end
239
+
240
+ # Keeps the deliberate failure from leaking into the other examples that share the project
241
+ def remove_failing_cucumber_scenario(project)
242
+ FileUtils.rm_f(File.join(project, 'features/reporter_failure.feature'))
243
+ FileUtils.rm_f(File.join(project, 'features/step_definitions/reporter_failure_steps.rb'))
156
244
  end
157
245
 
158
246
  # Test Web Frameworks (these can run without external services)
@@ -5,8 +5,12 @@ require_relative '../spec_helper'
5
5
 
6
6
  describe HelpersGenerator do
7
7
  shared_examples 'creates common helpers' do |name|
8
- it 'creates an allure helper file' do
9
- expect(File).to exist("#{name}/helpers/allure_helper.rb")
8
+ it 'creates an allure helper file only for rspec projects' do
9
+ if name.start_with?(FrameworkIndex::CUCUMBER)
10
+ expect(File).not_to exist("#{name}/helpers/allure_helper.rb")
11
+ else
12
+ expect(File).to exist("#{name}/helpers/allure_helper.rb")
13
+ end
10
14
  end
11
15
  end
12
16
 
@@ -101,5 +105,4 @@ describe HelpersGenerator do
101
105
  include_examples 'creates cucumber helpers', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::CROSS_PLATFORM}"
102
106
  include_examples 'creates cross platform helpers', "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::CROSS_PLATFORM}"
103
107
  end
104
-
105
108
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_raider
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Agustin Pequeno
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-11 00:00:00.000000000 Z
10
+ date: 2026-10-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -306,6 +306,7 @@ files:
306
306
  - spec/integration/content/common_content_spec.rb
307
307
  - spec/integration/content/config_content_spec.rb
308
308
  - spec/integration/content/content_helper.rb
309
+ - spec/integration/content/cucumber_reporter_content_spec.rb
309
310
  - spec/integration/content/gemfile_content_spec.rb
310
311
  - spec/integration/content/helper_content_spec.rb
311
312
  - spec/integration/content/page_content_spec.rb
@@ -346,7 +347,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
346
347
  requirements:
347
348
  - - ">="
348
349
  - !ruby/object:Gem::Version
349
- version: 3.1.0
350
+ version: 3.2.0
350
351
  required_rubygems_version: !ruby/object:Gem::Requirement
351
352
  requirements:
352
353
  - - ">="