ruby_raider 3.8.0 → 3.10.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/release.yml +18 -29
- data/.github/workflows/security.yml +68 -0
- data/.rubocop.yml +28 -0
- data/README.md +22 -4
- data/docs/superpowers/plans/2026-10-25-failure-video-replay.md +400 -0
- data/docs/superpowers/plans/2026-10-26-parity-cleanup.md +233 -0
- data/docs/superpowers/plans/2026-10-28-failure-card.md +295 -0
- data/docs/superpowers/specs/2026-10-25-failure-video-replay-design.md +214 -0
- data/docs/superpowers/specs/2026-10-28-failure-card-design.md +210 -0
- data/lib/commands/scaffolding_commands.rb +3 -3
- data/lib/commands/utility_commands.rb +36 -6
- data/lib/generators/cucumber/templates/cucumber.tt +1 -1
- data/lib/generators/cucumber/templates/partials/appium_env.tt +6 -0
- data/lib/generators/cucumber/templates/partials/selenium_env.tt +6 -0
- data/lib/generators/cucumber/templates/partials/watir_env.tt +6 -0
- data/lib/generators/helper_generator.rb +5 -1
- data/lib/generators/infrastructure/azure_generator.rb +1 -0
- data/lib/generators/infrastructure/github_generator.rb +1 -0
- data/lib/generators/infrastructure/templates/azure_pipelines.tt +26 -2
- data/lib/generators/infrastructure/templates/github.tt +31 -1
- data/lib/generators/infrastructure/templates/github_appium.tt +50 -1
- data/lib/generators/invoke_generators.rb +20 -2
- data/lib/generators/rspec/rspec_generator.rb +0 -1
- data/lib/generators/templates/common/gemfile.tt +1 -1
- data/lib/generators/templates/common/git_ignore.tt +3 -0
- data/lib/generators/templates/common/partials/mobile_config.tt +5 -0
- data/lib/generators/templates/common/partials/web_config.tt +6 -1
- data/lib/generators/templates/common/read_me.tt +80 -2
- data/lib/generators/templates/helpers/browser_helper.tt +5 -1
- data/lib/generators/templates/helpers/driver_helper.tt +1 -0
- data/lib/generators/templates/helpers/partials/selenium_driver.tt +4 -1
- data/lib/generators/templates/helpers/spec_helper.tt +8 -0
- data/lib/generators/templates/helpers/video_helper.tt +338 -171
- data/lib/ruby_raider/ffmpeg.rb +280 -0
- data/lib/ruby_raider.rb +2 -3
- data/lib/scaffolding/project_detector.rb +1 -3
- data/lib/scaffolding/scaffolding.rb +0 -1
- data/lib/utilities/utilities.rb +15 -2
- data/lib/version +1 -1
- data/ruby_raider.gemspec +3 -1
- data/sig/commands/utility_commands.rbs +4 -1
- data/sig/generators/invoke_generators.rbs +2 -3
- data/sig/ruby_raider/ffmpeg.rbs +23 -0
- data/sig/utilities/utilities.rbs +1 -0
- data/spec/commands/raider_commands_spec.rb +56 -1
- data/spec/generators/fixtures/video_helper_stubs/selenium-webdriver.rb +28 -0
- data/spec/generators/video_helper_spec.rb +608 -0
- data/spec/integration/commands/browser_update_after_creation_spec.rb +7 -6
- data/spec/integration/commands/utility_commands_spec.rb +2 -2
- data/spec/integration/content/azure_content_spec.rb +48 -3
- data/spec/integration/content/ci_content_spec.rb +115 -5
- data/spec/integration/content/common_content_spec.rb +8 -0
- data/spec/integration/content/config_content_spec.rb +14 -0
- data/spec/integration/content/cucumber_reporter_content_spec.rb +22 -1
- data/spec/integration/content/gemfile_content_spec.rb +2 -3
- data/spec/integration/content/helper_content_spec.rb +146 -0
- data/spec/integration/content/page_content_spec.rb +0 -1
- data/spec/integration/content/readme_content_spec.rb +65 -0
- data/spec/integration/content/rspec_reporter_content_spec.rb +2 -2
- data/spec/integration/content/test_content_spec.rb +2 -3
- data/spec/integration/end_to_end_features_spec.rb +0 -2
- data/spec/integration/end_to_end_spec.rb +252 -26
- data/spec/integration/generators/azure_generator_spec.rb +7 -6
- data/spec/integration/generators/common_generator_spec.rb +1 -1
- data/spec/integration/generators/invoke_generators_spec.rb +52 -0
- data/spec/integration/scaffolding_e2e_spec.rb +13 -24
- data/spec/scaffolding/scaffold_project_detector_spec.rb +0 -1
- data/spec/scaffolding/scaffolding_features_spec.rb +0 -1
- data/spec/system/selenium_spec.rb +0 -1
- data/spec/system/watir_spec.rb +0 -1
- data/spec/utilities/ffmpeg_spec.rb +424 -0
- data/spec/utilities/utilities_spec.rb +19 -2
- data/spec/utilities/video_command_spec.rb +120 -0
- metadata +49 -11
- data/lib/commands/adopt_commands.rb +0 -42
- data/lib/commands/loaded_commands.rb +0 -9
- data/lib/commands/plugin_commands.rb +0 -32
- data/sig/commands/adopt_commands.rbs +0 -8
- data/sig/commands/loaded_commands.rbs +0 -5
- data/sig/commands/plugin_commands.rbs +0 -9
|
@@ -12,7 +12,9 @@ pool:
|
|
|
12
12
|
|
|
13
13
|
variables:
|
|
14
14
|
HEADLESS: 'true'
|
|
15
|
-
#
|
|
15
|
+
# Where ffmpeg for failure videos is cached between runs.
|
|
16
|
+
RAIDER_CACHE_DIR: $(Pipeline.Workspace)/.cache/raider
|
|
17
|
+
# Add RAIDER_TOKEN (secret) and RAIDER_ENDPOINT (your Raider Hideout URL) as pipeline variables.
|
|
16
18
|
|
|
17
19
|
jobs:
|
|
18
20
|
- job: tests
|
|
@@ -21,6 +23,15 @@ jobs:
|
|
|
21
23
|
inputs: { versionSpec: '3.4' }
|
|
22
24
|
- script: bundle install
|
|
23
25
|
displayName: Install gems
|
|
26
|
+
- task: Cache@2
|
|
27
|
+
displayName: Cache ffmpeg for failure videos
|
|
28
|
+
inputs:
|
|
29
|
+
key: 'raider-ffmpeg-<%= RubyRaider::Ffmpeg::MANIFEST[:version] %> | "$(Agent.OS)" | Gemfile.lock'
|
|
30
|
+
path: $(RAIDER_CACHE_DIR)
|
|
31
|
+
# Video is optional: a failed download is visible here but never fails the build.
|
|
32
|
+
- script: bundle exec raider u video install
|
|
33
|
+
displayName: Install ffmpeg for failure videos
|
|
34
|
+
continueOnError: true
|
|
24
35
|
- script: <%- if cucumber? -%>bundle exec raider-reporter exec -- bundle exec cucumber features/ --format pretty<%- else -%>bundle exec raider-reporter exec -- bundle exec rspec spec/ --format documentation<%- end %>
|
|
25
36
|
displayName: Run tests
|
|
26
37
|
env:
|
|
@@ -31,7 +42,7 @@ jobs:
|
|
|
31
42
|
condition: always()
|
|
32
43
|
inputs: { targetPath: raider-results, artifact: raider-results-$(System.JobPositionInPhase)-$(System.JobAttempt) }
|
|
33
44
|
- job: raider_comment
|
|
34
|
-
dependsOn: tests
|
|
45
|
+
dependsOn: [tests, raider_gate]
|
|
35
46
|
condition: and(always(), eq(variables['Build.Reason'], 'PullRequest'))
|
|
36
47
|
steps:
|
|
37
48
|
- task: UseRubyVersion@0
|
|
@@ -43,3 +54,16 @@ jobs:
|
|
|
43
54
|
RAIDER_TOKEN: $(RAIDER_TOKEN)
|
|
44
55
|
RAIDER_ENDPOINT: $(RAIDER_ENDPOINT)
|
|
45
56
|
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
|
57
|
+
- job: raider_gate
|
|
58
|
+
dependsOn: tests
|
|
59
|
+
condition: and(always(), eq(variables['RAIDER_GATE'], '1'))
|
|
60
|
+
steps:
|
|
61
|
+
- task: UseRubyVersion@0
|
|
62
|
+
inputs: { versionSpec: '3.4' }
|
|
63
|
+
- script: bundle install
|
|
64
|
+
- script: bundle exec raider-reporter gate
|
|
65
|
+
displayName: Wait for the Raider quality gate
|
|
66
|
+
env:
|
|
67
|
+
RAIDER_TOKEN: $(RAIDER_TOKEN)
|
|
68
|
+
RAIDER_ENDPOINT: $(RAIDER_ENDPOINT)
|
|
69
|
+
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
|
@@ -33,8 +33,20 @@ jobs:
|
|
|
33
33
|
with:
|
|
34
34
|
chrome-version: stable
|
|
35
35
|
|
|
36
|
+
- name: Cache ffmpeg for failure videos
|
|
37
|
+
uses: actions/cache@v4
|
|
38
|
+
with:
|
|
39
|
+
path: ~/.cache/raider
|
|
40
|
+
key: raider-ffmpeg-<%= RubyRaider::Ffmpeg::MANIFEST[:version] %>-${{ runner.os }}-${{ hashFiles('**/Gemfile.lock') }}
|
|
41
|
+
|
|
42
|
+
- name: Install ffmpeg for failure videos
|
|
43
|
+
run: bundle exec raider u video install
|
|
44
|
+
# Video is optional: a failed download is visible here but never fails the build.
|
|
45
|
+
continue-on-error: true
|
|
46
|
+
|
|
36
47
|
- name: Run tests
|
|
37
48
|
run: <%- if framework == 'cucumber' -%>bundle exec raider-reporter exec -- bundle exec cucumber features --format pretty<%- else -%>bundle exec raider-reporter exec -- bundle exec rspec spec --format documentation<%- end %>
|
|
49
|
+
# RAIDER_ENDPOINT: your Raider Hideout URL (repository variable); RAIDER_TOKEN: a project token (secret).
|
|
38
50
|
env:
|
|
39
51
|
HEADLESS: true
|
|
40
52
|
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
@@ -50,7 +62,7 @@ jobs:
|
|
|
50
62
|
if-no-files-found: ignore
|
|
51
63
|
|
|
52
64
|
raider-comment:
|
|
53
|
-
needs: [build]
|
|
65
|
+
needs: [build, raider-gate]
|
|
54
66
|
if: always() && github.event_name == 'pull_request'
|
|
55
67
|
runs-on: ubuntu-latest
|
|
56
68
|
permissions:
|
|
@@ -67,3 +79,21 @@ jobs:
|
|
|
67
79
|
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
68
80
|
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
69
81
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
82
|
+
|
|
83
|
+
raider-gate:
|
|
84
|
+
needs: [build]
|
|
85
|
+
if: always() && vars.RAIDER_GATE == '1'
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
permissions:
|
|
88
|
+
contents: read
|
|
89
|
+
statuses: write
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v4
|
|
92
|
+
- uses: ruby/setup-ruby@v1
|
|
93
|
+
with: { ruby-version: 3.4.0, bundler-cache: true }
|
|
94
|
+
- name: Wait for the Raider quality gate
|
|
95
|
+
run: bundle exec raider-reporter gate
|
|
96
|
+
env:
|
|
97
|
+
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
98
|
+
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
99
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -44,17 +44,33 @@ jobs:
|
|
|
44
44
|
appium &
|
|
45
45
|
sleep 5
|
|
46
46
|
|
|
47
|
+
- name: Cache ffmpeg for failure videos
|
|
48
|
+
uses: actions/cache@v4
|
|
49
|
+
with:
|
|
50
|
+
path: ~/.cache/raider
|
|
51
|
+
key: raider-ffmpeg-<%= RubyRaider::Ffmpeg::MANIFEST[:version] %>-${{ runner.os }}-${{ hashFiles('**/Gemfile.lock') }}
|
|
52
|
+
|
|
53
|
+
- name: Install ffmpeg for failure videos
|
|
54
|
+
run: bundle exec raider u video install
|
|
55
|
+
# Video is optional: a failed download is visible here but never fails the build.
|
|
56
|
+
continue-on-error: true
|
|
57
|
+
|
|
47
58
|
- name: Run tests
|
|
48
59
|
<%- if cucumber? -%>
|
|
49
60
|
run: bundle exec raider-reporter exec -- bundle exec cucumber features/ --format pretty
|
|
50
61
|
<%- else -%>
|
|
51
62
|
run: bundle exec raider-reporter exec -- bundle exec rspec spec/ --format documentation
|
|
52
63
|
<%- end -%>
|
|
64
|
+
# RAIDER_ENDPOINT: your Raider Hideout URL (repository variable); RAIDER_TOKEN: a project token (secret).
|
|
53
65
|
env:
|
|
54
66
|
APP_PATH: ${{ github.workspace }}/apps/RaiderTestApp.app
|
|
55
67
|
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
56
68
|
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
57
69
|
RAIDER_QUARANTINE: "1"
|
|
70
|
+
<%- if cross_platform? -%>
|
|
71
|
+
RAIDER_SHARD_INDEX: "1"
|
|
72
|
+
RAIDER_SHARD_TOTAL: "2"
|
|
73
|
+
<%- end -%>
|
|
58
74
|
|
|
59
75
|
- name: Upload Raider results
|
|
60
76
|
uses: actions/upload-artifact@v4
|
|
@@ -97,6 +113,17 @@ jobs:
|
|
|
97
113
|
appium &
|
|
98
114
|
sleep 5
|
|
99
115
|
|
|
116
|
+
- name: Cache ffmpeg for failure videos
|
|
117
|
+
uses: actions/cache@v4
|
|
118
|
+
with:
|
|
119
|
+
path: ~/.cache/raider
|
|
120
|
+
key: raider-ffmpeg-<%= RubyRaider::Ffmpeg::MANIFEST[:version] %>-${{ runner.os }}-${{ hashFiles('**/Gemfile.lock') }}
|
|
121
|
+
|
|
122
|
+
- name: Install ffmpeg for failure videos
|
|
123
|
+
run: bundle exec raider u video install
|
|
124
|
+
# Video is optional: a failed download is visible here but never fails the build.
|
|
125
|
+
continue-on-error: true
|
|
126
|
+
|
|
100
127
|
- name: Run Android Emulator and Tests
|
|
101
128
|
uses: reactivecircus/android-emulator-runner@v2
|
|
102
129
|
with:
|
|
@@ -115,6 +142,10 @@ jobs:
|
|
|
115
142
|
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
116
143
|
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
117
144
|
RAIDER_QUARANTINE: "1"
|
|
145
|
+
<%- if cross_platform? -%>
|
|
146
|
+
RAIDER_SHARD_INDEX: "2"
|
|
147
|
+
RAIDER_SHARD_TOTAL: "2"
|
|
148
|
+
<%- end -%>
|
|
118
149
|
|
|
119
150
|
- name: Upload Raider results
|
|
120
151
|
uses: actions/upload-artifact@v4
|
|
@@ -126,7 +157,7 @@ jobs:
|
|
|
126
157
|
<%- end -%>
|
|
127
158
|
|
|
128
159
|
raider-comment:
|
|
129
|
-
needs: [<%= [('ios-tests' if ios? || cross_platform?), ('android-tests' if android? || cross_platform?)].compact.join(', ')
|
|
160
|
+
needs: [<%= [('ios-tests' if ios? || cross_platform?), ('android-tests' if android? || cross_platform?)].compact.join(', ') %>, raider-gate]
|
|
130
161
|
if: always() && github.event_name == 'pull_request'
|
|
131
162
|
runs-on: ubuntu-latest
|
|
132
163
|
permissions:
|
|
@@ -143,3 +174,21 @@ jobs:
|
|
|
143
174
|
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
144
175
|
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
145
176
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
177
|
+
|
|
178
|
+
raider-gate:
|
|
179
|
+
needs: [<%= [('ios-tests' if ios? || cross_platform?), ('android-tests' if android? || cross_platform?)].compact.join(', ') %>]
|
|
180
|
+
if: always() && vars.RAIDER_GATE == '1'
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
permissions:
|
|
183
|
+
contents: read
|
|
184
|
+
statuses: write
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/checkout@v4
|
|
187
|
+
- uses: ruby/setup-ruby@v1
|
|
188
|
+
with: { ruby-version: 3.4.0, bundler-cache: true }
|
|
189
|
+
- name: Wait for the Raider quality gate
|
|
190
|
+
run: bundle exec raider-reporter gate
|
|
191
|
+
env:
|
|
192
|
+
RAIDER_TOKEN: ${{ secrets.RAIDER_TOKEN }}
|
|
193
|
+
RAIDER_ENDPOINT: ${{ vars.RAIDER_ENDPOINT }}
|
|
194
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'thor'
|
|
1
2
|
require_relative 'infrastructure/github_generator'
|
|
2
3
|
require_relative 'infrastructure/azure_generator'
|
|
3
4
|
require_relative 'automation/automation_generator'
|
|
@@ -7,11 +8,15 @@ require_relative 'helper_generator'
|
|
|
7
8
|
require_relative 'rspec/rspec_generator'
|
|
8
9
|
|
|
9
10
|
module InvokeGenerators
|
|
10
|
-
|
|
11
|
+
FRAMEWORKS = %w[rspec cucumber].freeze
|
|
12
|
+
AUTOMATIONS = %w[selenium watir ios android cross_platform].freeze
|
|
11
13
|
|
|
12
14
|
module_function
|
|
13
15
|
|
|
14
16
|
def generate_framework(structure = {})
|
|
17
|
+
structure = normalise_options(structure)
|
|
18
|
+
validate_option(:framework, structure[:framework], FRAMEWORKS)
|
|
19
|
+
validate_option(:automation, structure[:automation], AUTOMATIONS)
|
|
15
20
|
validate_ci(structure[:ci])
|
|
16
21
|
generators = %w[Automation Common Helpers]
|
|
17
22
|
framework = structure[:framework]
|
|
@@ -30,10 +35,23 @@ module InvokeGenerators
|
|
|
30
35
|
print_mobile_azure_notice(structure)
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
def normalise_options(structure)
|
|
39
|
+
structure.merge(
|
|
40
|
+
framework: structure[:framework].to_s.downcase,
|
|
41
|
+
automation: structure[:automation].to_s.downcase
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate_option(key, value, allowed)
|
|
46
|
+
return if allowed.include?(value.to_s)
|
|
47
|
+
|
|
48
|
+
raise Thor::Error, "Invalid #{key} #{value.to_s.inspect}. Allowed values: #{allowed.join(', ')}"
|
|
49
|
+
end
|
|
50
|
+
|
|
33
51
|
def validate_ci(provider)
|
|
34
52
|
return if [nil, '', 'github', 'azure'].include?(provider&.to_s)
|
|
35
53
|
|
|
36
|
-
raise
|
|
54
|
+
raise Thor::Error, 'ci must be github or azure'
|
|
37
55
|
end
|
|
38
56
|
|
|
39
57
|
def print_mobile_azure_notice(structure)
|
|
@@ -93,11 +93,13 @@ Every run writes machine-readable results to `raider-results/` (including `run.j
|
|
|
93
93
|
`features/support/env.rb` requires `raider_reporter/cucumber`, so no extra Cucumber formatter flag is needed.
|
|
94
94
|
<%- end -%>
|
|
95
95
|
|
|
96
|
-
To upload runs to Raider, set these environment variables before running the suite
|
|
96
|
+
To upload runs to Raider, set these environment variables before running the suite.
|
|
97
|
+
`RAIDER_TOKEN` and `RAIDER_ENDPOINT` come from your Raider Hideout server (its setup page, or `bin/rails hideout:setup` /
|
|
98
|
+
`hideout:token`):
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
101
|
export RAIDER_TOKEN=your-token
|
|
100
|
-
export RAIDER_ENDPOINT=https://
|
|
102
|
+
export RAIDER_ENDPOINT=https://hideout.example.com
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
<%- if rspec? -%>
|
|
@@ -108,6 +110,62 @@ Without `RAIDER_TOKEN`, nothing is uploaded and the suite output is unchanged. F
|
|
|
108
110
|
report and to Raider. See the [raider_reporter README](https://github.com/RaiderHQ/raider_reporter) for details.
|
|
109
111
|
<%- end -%>
|
|
110
112
|
|
|
113
|
+
### Video replay
|
|
114
|
+
|
|
115
|
+
Every test is recorded, and the video is kept only for failing tests<%= rspec? ? ' (on the last retry attempt)' : '' %>. It is attached
|
|
116
|
+
to the report as `video` and saved under `raider-results/attachments/`, next to the failure screenshot.
|
|
117
|
+
<%- if mobile? -%>
|
|
118
|
+
Appium records the device screen and the video is attached as `video.mp4`.
|
|
119
|
+
<%- else -%>
|
|
120
|
+
The video is a `video.webm` built from page screenshots with ffmpeg.
|
|
121
|
+
<%- end -%>
|
|
122
|
+
|
|
123
|
+
<%- if single_platform? -%>
|
|
124
|
+
- Turn video off for one run with `VIDEO=off bundle exec <%= rspec? ? 'rspec' : 'cucumber' %>`, or for the project with
|
|
125
|
+
`raider u video off`, which creates `config/config.yml` with a `video:` section (`raider u video on` turns it back
|
|
126
|
+
on).
|
|
127
|
+
<%- else -%>
|
|
128
|
+
- Video is on by default: `video:` in `config/config.yml` (`enabled: true`, `fps: 5`, `max_width: 1280`).
|
|
129
|
+
- Turn it off for one run with `VIDEO=off bundle exec <%= rspec? ? 'rspec' : 'cucumber' %>`, or for the project with
|
|
130
|
+
`raider u video off` (`raider u video on` turns it back on).
|
|
131
|
+
<%- end -%>
|
|
132
|
+
<%- unless mobile? -%>
|
|
133
|
+
- ffmpeg is downloaded on first use, checked against a pinned SHA-256 and cached in `~/.cache/raider`
|
|
134
|
+
(`%LOCALAPPDATA%\raider` on Windows). `raider u video install` downloads it ahead of time (CI does this before the
|
|
135
|
+
tests and caches the folder), and `raider u video status` prints the binary in use.
|
|
136
|
+
- `RAIDER_FFMPEG_PATH` uses your own ffmpeg (it needs libvpx) instead of the download, and `RAIDER_CACHE_DIR` moves
|
|
137
|
+
the cache.
|
|
138
|
+
<%- if azure? -%>
|
|
139
|
+
- `azure-pipelines.yml` sets `RAIDER_CACHE_DIR` to `$(Pipeline.Workspace)/.cache/raider`, the folder its `Cache@2`
|
|
140
|
+
step keeps between runs.
|
|
141
|
+
<%- end -%>
|
|
142
|
+
- The RaiderHQ `ffmpeg-7.1` release that the download comes from is not published yet. Until it is, set
|
|
143
|
+
`RAIDER_FFMPEG_PATH` to get videos; without it the suite runs as usual and prints one `[video]` line.
|
|
144
|
+
- If ffmpeg is missing or recording fails, the suite still runs and its exit code is unchanged; you get one `[video]`
|
|
145
|
+
line on stderr and no video.
|
|
146
|
+
- Known limits: a frame is taken after each page load, click and text change, plus one at the end of a failing test,
|
|
147
|
+
so changes the page makes on its own between actions show up in the next frame. There is one recorded driver per
|
|
148
|
+
process.
|
|
149
|
+
<%- else -%>
|
|
150
|
+
- Appium's screen recording needs `ffmpeg` on the Appium server host's `PATH` (this machine only when Appium runs
|
|
151
|
+
here): always for iOS (XCUITest), and for Android when a recording is longer than 180 s. Without it, recording
|
|
152
|
+
fails with one `[video]` line and the tests run as usual.
|
|
153
|
+
<%- if cross_platform? -%>
|
|
154
|
+
- Each test records for at most 1800 s on iOS and 180 s on Android, so Android needs no ffmpeg on the Appium host.
|
|
155
|
+
<%- elsif ios? -%>
|
|
156
|
+
- Each test records for at most 1800 s.
|
|
157
|
+
<%- else -%>
|
|
158
|
+
- Each test records for at most 180 s, so the Appium host needs no ffmpeg; longer tests keep only their first 180 s.
|
|
159
|
+
<%- end -%>
|
|
160
|
+
Set `time_limit` (seconds) under `video:` in `config/config.yml`<%= single_platform? ? ' (create it if it is missing)' : '' %> to change it; on Android, more
|
|
161
|
+
than 180 s needs ffmpeg on the Appium host.
|
|
162
|
+
- `raider u video install` and `raider u video status` manage the ffmpeg that web projects use, cached in
|
|
163
|
+
`~/.cache/raider` (override with `RAIDER_FFMPEG_PATH` or `RAIDER_CACHE_DIR`); they do not install ffmpeg on the
|
|
164
|
+
Appium host. The RaiderHQ `ffmpeg-7.1` release it comes from is not published yet.
|
|
165
|
+
- If recording fails, the suite still runs and its exit code is unchanged; you get one `[video]` line on stderr and
|
|
166
|
+
no video.
|
|
167
|
+
<%- end -%>
|
|
168
|
+
|
|
111
169
|
<%- unless azure? && mobile? -%>
|
|
112
170
|
### Quarantine
|
|
113
171
|
|
|
@@ -115,6 +173,26 @@ CI runs the suite through `bundle exec raider-reporter exec -- …` with `RAIDER
|
|
|
115
173
|
Raider still run and are still reported (as `quarantined`), and a build whose only failures are quarantined stays green.
|
|
116
174
|
Local runs keep quarantine off. To turn it off in CI, remove `RAIDER_QUARANTINE` from the pipeline.
|
|
117
175
|
|
|
176
|
+
<%- end -%>
|
|
177
|
+
<%- unless azure? && mobile? -%>
|
|
178
|
+
### Quality gate
|
|
179
|
+
|
|
180
|
+
The `<%= azure? ? 'raider_gate' : 'raider-gate' %>` CI job runs `bundle exec raider-reporter gate` after the tests and
|
|
181
|
+
fails the pipeline when the Raider quality gate fails. It is off until you turn it on:
|
|
182
|
+
|
|
183
|
+
- set the CI variable `RAIDER_GATE=1` (<%= azure? ? 'a pipeline variable' : 'a repository variable under Settings → Secrets and variables → Actions → Variables' %>);
|
|
184
|
+
- turn on rules under the project's Gate page in Raider: a minimum pass rate, no new failures against the default
|
|
185
|
+
branch, no new flaky tests;
|
|
186
|
+
- optionally require the `raider/gate` check in branch protection.
|
|
187
|
+
|
|
188
|
+
`raider-reporter gate` exits 1 when the gate fails, naming the failed rules, and 2 when it gets no verdict (timeout,
|
|
189
|
+
Raider unreachable, token rejected, build not found). `RAIDER_GATE_TIMEOUT` sets how long it waits, in seconds
|
|
190
|
+
(default 600). The job stays off by default because a project without `RAIDER_TOKEN`, or a pull request from a fork,
|
|
191
|
+
would exit 2 on every run.
|
|
192
|
+
|
|
193
|
+
Parallel test jobs that upload to one build must each set `RAIDER_SHARD_TOTAL` (the number of jobs) and
|
|
194
|
+
`RAIDER_SHARD_INDEX` (1 to that number), so the gate waits for all of them instead of judging the first one to finish.
|
|
195
|
+
|
|
118
196
|
<%- end -%>
|
|
119
197
|
<%- if azure? && mobile? -%>
|
|
120
198
|
## CI
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
|
4
4
|
require 'yaml'
|
|
5
5
|
require 'watir'
|
|
6
|
+
require_relative 'video_helper'
|
|
6
7
|
|
|
7
8
|
module BrowserHelper
|
|
8
9
|
|
|
@@ -23,6 +24,9 @@ module BrowserHelper
|
|
|
23
24
|
args += ['--headless'] if (ENV['HEADLESS'] || config['headless']) && !args.include?('--headless')
|
|
24
25
|
capitalized = browser_name == 'ie' ? browser_name.upcase : browser_name.capitalize
|
|
25
26
|
browser_options = "Selenium::WebDriver::#{capitalized}::Options".constantize.new(args:)
|
|
26
|
-
|
|
27
|
+
arguments = { options: browser_options }
|
|
28
|
+
listener = VideoHelper.listener
|
|
29
|
+
arguments[:listener] = listener if listener
|
|
30
|
+
Watir::Browser.new(browser_name.to_sym, **arguments)
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
def create_driver(*opts)
|
|
2
2
|
@config = YAML.load_file('config/config.yml')
|
|
3
3
|
browser = @config['browser']
|
|
4
|
-
|
|
4
|
+
arguments = { options: create_webdriver_options(*opts) }
|
|
5
|
+
listener = VideoHelper.listener
|
|
6
|
+
arguments[:listener] = listener if listener
|
|
7
|
+
driver = Selenium::WebDriver.for(browser.to_sym, **arguments)
|
|
5
8
|
driver.manage.timeouts.implicit_wait = @config.fetch('timeout', 10)
|
|
6
9
|
driver
|
|
7
10
|
end
|
|
@@ -12,6 +12,7 @@ require_relative 'browser_helper'
|
|
|
12
12
|
<%- else -%>
|
|
13
13
|
require_relative 'driver_helper'
|
|
14
14
|
<%- end -%>
|
|
15
|
+
require_relative 'video_helper'
|
|
15
16
|
|
|
16
17
|
module SpecHelper
|
|
17
18
|
RSpec.configure do |config|
|
|
@@ -45,10 +46,14 @@ module SpecHelper
|
|
|
45
46
|
browser.window.maximize
|
|
46
47
|
end
|
|
47
48
|
<%- end -%>
|
|
49
|
+
@video_recorder = VideoHelper.recorder_for(<%= watir? ? 'browser' : 'driver' %>)
|
|
50
|
+
@video_recorder.start
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
config.after(:each) do |example|
|
|
51
54
|
example_name = example.description
|
|
55
|
+
keep_video = example.exception ? VideoHelper.final_attempt?(example) : false
|
|
56
|
+
video_path = @video_recorder&.stop(keep: keep_video)
|
|
52
57
|
if example.exception
|
|
53
58
|
@_exception = example.exception
|
|
54
59
|
Dir.mktmpdir do |temp_folder|
|
|
@@ -57,6 +62,9 @@ module SpecHelper
|
|
|
57
62
|
RaiderReporter.attach('screenshot', screenshot_path, type: :png)
|
|
58
63
|
end
|
|
59
64
|
end
|
|
65
|
+
RaiderReporter.attach('video', video_path, type: <%= mobile? ? ':mp4' : ':webm' %>) if video_path
|
|
66
|
+
ensure
|
|
67
|
+
@video_recorder&.cleanup
|
|
60
68
|
<%= partial('quit_driver', strip: true) %>
|
|
61
69
|
end
|
|
62
70
|
end
|