ruby_raider 3.5.0 → 3.7.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/README.md +10 -1
- data/RELEASE.md +17 -0
- data/lib/generators/common_generator.rb +0 -6
- data/lib/generators/generator.rb +9 -0
- data/lib/generators/helper_generator.rb +0 -7
- data/lib/generators/infrastructure/azure_generator.rb +11 -0
- data/lib/generators/infrastructure/github_generator.rb +2 -2
- data/lib/generators/infrastructure/templates/azure_pipelines.tt +44 -0
- data/lib/generators/infrastructure/templates/github.tt +0 -33
- data/lib/generators/invoke_generators.rb +19 -1
- data/lib/generators/menu_generator.rb +16 -6
- data/lib/generators/templates/common/gemfile.tt +1 -5
- data/lib/generators/templates/common/git_ignore.tt +0 -3
- data/lib/generators/templates/common/read_me.tt +26 -7
- data/lib/generators/templates/helpers/partials/screenshot.tt +3 -3
- data/lib/generators/templates/helpers/spec_helper.tt +0 -6
- data/lib/ruby_raider.rb +8 -1
- data/lib/version +1 -1
- data/sig/generators/common_generator.rbs +0 -1
- data/sig/generators/generator.rbs +2 -0
- data/sig/generators/helper_generator.rbs +0 -2
- data/sig/generators/infrastructure/azure_generator.rbs +4 -0
- data/sig/generators/invoke_generators.rbs +5 -0
- data/sig/generators/menu_generator.rbs +2 -1
- data/sig/ruby_raider.rbs +1 -0
- data/spec/integration/content/azure_content_spec.rb +109 -0
- data/spec/integration/content/ci_content_spec.rb +6 -10
- data/spec/integration/content/common_content_spec.rb +2 -2
- data/spec/integration/content/gemfile_content_spec.rb +6 -6
- data/spec/integration/content/helper_content_spec.rb +3 -42
- data/spec/integration/content/readme_content_spec.rb +3 -2
- data/spec/integration/content/rspec_reporter_content_spec.rb +26 -0
- data/spec/integration/end_to_end_spec.rb +93 -4
- data/spec/integration/generators/azure_generator_spec.rb +100 -0
- data/spec/integration/generators/helpers_generator_spec.rb +2 -6
- data/spec/menus/menu_generator_spec.rb +49 -0
- metadata +9 -9
- data/lib/generators/templates/helpers/allure_helper.tt +0 -32
- data/lib/generators/templates/helpers/partials/allure_imports.tt +0 -7
- data/lib/generators/templates/helpers/partials/allure_requirements.tt +0 -8
- data/lib/generators/templates/helpers/partials/video_start.tt +0 -9
- data/lib/generators/templates/helpers/partials/video_stop.tt +0 -4
- data/lib/generators/templates/helpers/test_helper.tt +0 -88
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require_relative 'content_helper'
|
|
6
|
+
|
|
7
|
+
# UseRubyVersion@0 versionSpec '3.4' resolves against the hosted tool cache, which lists Ruby 3.4.x
|
|
8
|
+
# (3.2, 3.3, 3.4, 4.0): https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
|
|
9
|
+
describe 'Azure Pipelines content' do
|
|
10
|
+
combos = FRAMEWORKS.product(%w[selenium watir], [false, true])
|
|
11
|
+
|
|
12
|
+
before(:all) do # rubocop:disable RSpec/BeforeAfterAll
|
|
13
|
+
combos.each do |framework, automation, axe|
|
|
14
|
+
name = "azurec_#{framework}_#{automation}#{'_axe' if axe}"
|
|
15
|
+
next if File.exist?(name)
|
|
16
|
+
|
|
17
|
+
InvokeGenerators.generate_framework(automation:, framework:, name:, accessibility: axe, ci: 'azure')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
after(:all) { FileUtils.rm_rf(Dir.glob('azurec_*')) } # rubocop:disable RSpec/BeforeAfterAll
|
|
22
|
+
|
|
23
|
+
combos.each do |framework, automation, axe|
|
|
24
|
+
context "with #{framework}, #{automation}#{', axe' if axe}" do
|
|
25
|
+
let(:name) { "azurec_#{framework}_#{automation}#{'_axe' if axe}" }
|
|
26
|
+
let(:raw) { read_generated(name, 'azure-pipelines.yml') }
|
|
27
|
+
let(:yml) { YAML.safe_load(raw) }
|
|
28
|
+
let(:run_command) do
|
|
29
|
+
if framework == 'cucumber'
|
|
30
|
+
'bundle exec cucumber features/ --format pretty'
|
|
31
|
+
else
|
|
32
|
+
'bundle exec rspec spec/ --format documentation'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'has a tests job with ruby, bundle install, run command and an always-published artifact' do
|
|
37
|
+
tests = yml['jobs'][0]
|
|
38
|
+
expect(tests['job']).to eq('tests')
|
|
39
|
+
steps = tests['steps']
|
|
40
|
+
expect(steps.first).to eq('task' => 'UseRubyVersion@0', 'inputs' => { 'versionSpec' => '3.4' })
|
|
41
|
+
expect(steps.map { |s| s['script'] }).to include('bundle install', run_command)
|
|
42
|
+
publish = steps.find { |s| s['task'] == 'PublishPipelineArtifact@1' }
|
|
43
|
+
expect(publish['condition']).to eq('always()')
|
|
44
|
+
expect(publish['inputs']).to eq(
|
|
45
|
+
'targetPath' => 'raider-results',
|
|
46
|
+
'artifact' => 'raider-results-$(System.JobPositionInPhase)-$(System.JobAttempt)'
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'passes exactly the Raider settings to the test step' do
|
|
51
|
+
step = yml['jobs'][0]['steps'].find { |s| s['script'] == run_command }
|
|
52
|
+
expect(step['env']).to eq('RAIDER_TOKEN' => '$(RAIDER_TOKEN)', 'RAIDER_ENDPOINT' => '$(RAIDER_ENDPOINT)')
|
|
53
|
+
expect(yml['variables']['HEADLESS']).to eq('true')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'has a raider_comment job for pull requests' do
|
|
57
|
+
comment = yml['jobs'][1]
|
|
58
|
+
expect(comment['job']).to eq('raider_comment')
|
|
59
|
+
expect(comment['dependsOn']).to eq('tests')
|
|
60
|
+
expect(comment['condition']).to eq("and(always(), eq(variables['Build.Reason'], 'PullRequest'))")
|
|
61
|
+
last = comment['steps'].last
|
|
62
|
+
expect(last['script']).to eq('bundle exec raider-reporter comment')
|
|
63
|
+
expect(last['env']).to include('SYSTEM_ACCESSTOKEN' => '$(System.AccessToken)')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'has triggers, pool and the self-hosted hint' do
|
|
67
|
+
expect(yml['trigger']['branches']['include']).to include('main', 'master')
|
|
68
|
+
expect(yml['pr']['branches']['include']).to include('*')
|
|
69
|
+
expect(yml['pool']).to eq('vmImage' => 'ubuntu-latest')
|
|
70
|
+
expect(raw).to include('# Self-hosted agent? Replace vmImage with: pool: { name: Default }')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'explains that Azure Repos ignores pr: directly above the pr block' do
|
|
74
|
+
expect(raw).to include('# Azure Repos ignores pr: — add this pipeline as a Build validation branch policy ' \
|
|
75
|
+
"so pull request builds (and the Raider comment) run.\npr:")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'has no Allure' do
|
|
79
|
+
expect(raw).not_to match(/allure/i)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'documents Azure Pipelines in the README and no GitHub Actions setup' do
|
|
83
|
+
readme = read_generated(name, 'Readme.md')
|
|
84
|
+
expect(readme).to include('## Azure Pipelines', 'RAIDER_TOKEN', 'Contribute to pull requests',
|
|
85
|
+
'pool: { name: Default }', 'Build validation branch policy',
|
|
86
|
+
'same stage')
|
|
87
|
+
expect(readme).not_to match(/GitHub Actions/i)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'with a mobile project and ci azure' do
|
|
93
|
+
let(:readme) { read_generated('azurec_mobile', 'Readme.md') }
|
|
94
|
+
|
|
95
|
+
before(:all) do # rubocop:disable RSpec/BeforeAfterAll
|
|
96
|
+
InvokeGenerators.generate_framework(automation: 'ios', framework: 'rspec', name: 'azurec_mobile', ci: 'azure')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'says the template is not available and does not mention azure-pipelines.yml' do
|
|
100
|
+
expect(readme).to include('## CI', 'Azure Pipelines template for mobile projects is not available yet. ' \
|
|
101
|
+
'Use GitHub Actions (`ci:github`)')
|
|
102
|
+
expect(readme).not_to include('azure-pipelines.yml', '## Azure Pipelines')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'does not generate azure-pipelines.yml for default projects' do
|
|
107
|
+
expect(File).not_to exist("#{project('rspec', 'selenium')}/azure-pipelines.yml")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -128,20 +128,16 @@ describe 'CI pipeline content' do
|
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
shared_examples 'github allure steps' do |project_name|
|
|
132
|
-
subject(:pipeline) { read_generated(project_name, '.github/workflows/test_pipeline.yml') }
|
|
133
|
-
|
|
134
|
-
it 'keeps the Allure steps' do
|
|
135
|
-
expect(pipeline).to include('simple-elf/allure-report-action', 'peaceiris/actions-gh-pages')
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
131
|
shared_examples 'github without allure steps' do |project_name|
|
|
140
132
|
subject(:pipeline) { read_generated(project_name, '.github/workflows/test_pipeline.yml') }
|
|
141
133
|
|
|
142
134
|
it 'has no Allure report or gh-pages steps' do
|
|
143
135
|
expect(pipeline).not_to match(/allure|gh-pages/i)
|
|
144
136
|
end
|
|
137
|
+
|
|
138
|
+
it 'still uploads raider-results' do
|
|
139
|
+
expect(pipeline).to include('name: raider-results', 'path: raider-results/')
|
|
140
|
+
end
|
|
145
141
|
end
|
|
146
142
|
|
|
147
143
|
context 'with rspec and selenium' do
|
|
@@ -151,7 +147,7 @@ describe 'CI pipeline content' do
|
|
|
151
147
|
include_examples 'github triggers', name, 'test_pipeline.yml'
|
|
152
148
|
include_examples 'github raider wiring', name, 'test_pipeline.yml'
|
|
153
149
|
include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
|
|
154
|
-
include_examples 'github allure steps', name
|
|
150
|
+
include_examples 'github without allure steps', name
|
|
155
151
|
end
|
|
156
152
|
|
|
157
153
|
context 'with cucumber and selenium' do
|
|
@@ -171,7 +167,7 @@ describe 'CI pipeline content' do
|
|
|
171
167
|
include_examples 'github triggers', name, 'test_pipeline.yml'
|
|
172
168
|
include_examples 'github raider wiring', name, 'test_pipeline.yml'
|
|
173
169
|
include_examples 'github raider-comment job', name, 'test_pipeline.yml', %w[build]
|
|
174
|
-
include_examples 'github allure steps', name
|
|
170
|
+
include_examples 'github without allure steps', name
|
|
175
171
|
end
|
|
176
172
|
|
|
177
173
|
context 'with cucumber and watir' do
|
|
@@ -106,8 +106,8 @@ describe 'Common file content' do
|
|
|
106
106
|
shared_examples 'rspec gitignore' do |project_name|
|
|
107
107
|
subject(:gitignore) { read_generated(project_name, '.gitignore') }
|
|
108
108
|
|
|
109
|
-
it '
|
|
110
|
-
expect(gitignore).
|
|
109
|
+
it 'does not include allure-results' do
|
|
110
|
+
expect(gitignore).not_to include('allure-results')
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
it 'includes spec/examples.txt' do
|
|
@@ -34,8 +34,8 @@ describe 'Gemfile content' do
|
|
|
34
34
|
expect(gemfile).to include("gem 'rspec'")
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
it '
|
|
38
|
-
expect(gemfile).
|
|
37
|
+
it 'does not include allure gems' do
|
|
38
|
+
expect(gemfile).not_to match(/allure/i)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
it 'includes rubocop-rspec' do
|
|
@@ -127,11 +127,11 @@ 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.
|
|
130
|
+
expect(gemfile).to include("gem 'raider_reporter', '~> 0.7'")
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
-
it '
|
|
134
|
-
expect(gemfile).
|
|
133
|
+
it 'has no allure gems' do
|
|
134
|
+
expect(gemfile).not_to match(/allure/i)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
it 'pins ruby_raider to the generator major.minor' do
|
|
@@ -143,7 +143,7 @@ describe 'Gemfile content' do
|
|
|
143
143
|
let(:gemfile) { read_generated("#{FrameworkIndex::CUCUMBER}_#{automation}", 'Gemfile') }
|
|
144
144
|
|
|
145
145
|
it 'includes raider_reporter' do
|
|
146
|
-
expect(gemfile).to include("gem 'raider_reporter', '~> 0.
|
|
146
|
+
expect(gemfile).to include("gem 'raider_reporter', '~> 0.7'")
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
it 'drops allure-cucumber' do
|
|
@@ -103,10 +103,6 @@ describe 'Helper file content' do
|
|
|
103
103
|
it 'configures RSpec' do
|
|
104
104
|
expect(helper).to include('RSpec.configure')
|
|
105
105
|
end
|
|
106
|
-
|
|
107
|
-
it 'requires allure_helper' do
|
|
108
|
-
expect(helper).to include("require_relative 'allure_helper'")
|
|
109
|
-
end
|
|
110
106
|
end
|
|
111
107
|
|
|
112
108
|
shared_examples 'raider reporter spec helper' do |project_name, screenshot_call|
|
|
@@ -116,10 +112,6 @@ describe 'Helper file content' do
|
|
|
116
112
|
expect(helper).to include("require 'raider_reporter/rspec'")
|
|
117
113
|
end
|
|
118
114
|
|
|
119
|
-
it 'requires digest, which allure-ruby-commons uses without requiring' do
|
|
120
|
-
expect(helper).to include("require 'digest'")
|
|
121
|
-
end
|
|
122
|
-
|
|
123
115
|
it 'adds the raider formatter in spec_helper so a command-line --format cannot drop it' do
|
|
124
116
|
expect(helper).to include('config.add_formatter(RaiderReporter::RSpec::Formatter)')
|
|
125
117
|
end
|
|
@@ -128,10 +120,6 @@ describe 'Helper file content' do
|
|
|
128
120
|
expect(helper).not_to match(/config\.formatter\s*=/)
|
|
129
121
|
end
|
|
130
122
|
|
|
131
|
-
it 'adds the allure formatter' do
|
|
132
|
-
expect(helper).to include('config.add_formatter(AllureHelper.formatter)')
|
|
133
|
-
end
|
|
134
|
-
|
|
135
123
|
it 'screenshots only on failure' do
|
|
136
124
|
lines = helper.lines
|
|
137
125
|
start = lines.index { |line| line.include?('if example.exception') }
|
|
@@ -140,8 +128,9 @@ describe 'Helper file content' do
|
|
|
140
128
|
body = lines[(start + 1)..].take_while { |line| line.strip.empty? || line[/\A */].size > indent }
|
|
141
129
|
guarded = body.join
|
|
142
130
|
|
|
143
|
-
expect(guarded).to include('
|
|
144
|
-
expect(
|
|
131
|
+
expect(guarded).to include('RaiderReporter.attach', screenshot_call)
|
|
132
|
+
expect(helper).not_to include('AllureHelper')
|
|
133
|
+
expect(helper).not_to match(/screenshot\s*=/)
|
|
145
134
|
expect(lines.select { |line| line.include?(screenshot_call) }.size).to eq(1)
|
|
146
135
|
end
|
|
147
136
|
|
|
@@ -182,20 +171,6 @@ describe 'Helper file content' do
|
|
|
182
171
|
end
|
|
183
172
|
end
|
|
184
173
|
|
|
185
|
-
# --- Allure helper ---
|
|
186
|
-
|
|
187
|
-
shared_examples 'valid allure helper' do |project_name|
|
|
188
|
-
subject(:helper) { read_generated(project_name, 'helpers/allure_helper.rb') }
|
|
189
|
-
|
|
190
|
-
it 'has valid Ruby syntax' do
|
|
191
|
-
expect(helper).to have_valid_ruby_syntax
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
it 'defines AllureHelper' do
|
|
195
|
-
expect(helper).to match(/AllureHelper|module AllureHelper|class AllureHelper/)
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
174
|
# --- RSpec + automation contexts ---
|
|
200
175
|
|
|
201
176
|
context 'with rspec and selenium' do
|
|
@@ -204,7 +179,6 @@ describe 'Helper file content' do
|
|
|
204
179
|
it_behaves_like 'selenium driver helper', name
|
|
205
180
|
it_behaves_like 'valid spec helper', name
|
|
206
181
|
it_behaves_like 'selenium spec helper', name
|
|
207
|
-
it_behaves_like 'valid allure helper', name
|
|
208
182
|
end
|
|
209
183
|
|
|
210
184
|
context 'with rspec and watir' do
|
|
@@ -212,7 +186,6 @@ describe 'Helper file content' do
|
|
|
212
186
|
it_behaves_like 'valid browser helper', name
|
|
213
187
|
it_behaves_like 'valid spec helper', name
|
|
214
188
|
it_behaves_like 'watir spec helper', name
|
|
215
|
-
it_behaves_like 'valid allure helper', name
|
|
216
189
|
end
|
|
217
190
|
|
|
218
191
|
# --- Cucumber + automation contexts (no spec_helper/test_helper) ---
|
|
@@ -228,18 +201,6 @@ describe 'Helper file content' do
|
|
|
228
201
|
it_behaves_like 'valid browser helper', name
|
|
229
202
|
end
|
|
230
203
|
|
|
231
|
-
# --- Mobile contexts (with AppiumRecorder) ---
|
|
232
|
-
|
|
233
|
-
context 'with cucumber and appium android' do
|
|
234
|
-
name = "#{FrameworkIndex::CUCUMBER}_#{AutomationIndex::ANDROID}"
|
|
235
|
-
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
context 'with rspec and appium ios' do
|
|
239
|
-
name = "#{FrameworkIndex::RSPEC}_#{AutomationIndex::IOS}"
|
|
240
|
-
|
|
241
|
-
end
|
|
242
|
-
|
|
243
204
|
# --- Raider reporter wiring (every rspec automation) ---
|
|
244
205
|
|
|
245
206
|
AUTOMATION_TYPES.each do |automation|
|
|
@@ -16,8 +16,9 @@ describe 'Generated Readme content' do
|
|
|
16
16
|
'https://github.com/RaiderHQ/raider_reporter')
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
it '
|
|
20
|
-
expect(readme).to
|
|
19
|
+
it 'points to the local HTML report' do
|
|
20
|
+
expect(readme).to include('raider-results/index.html')
|
|
21
|
+
expect(readme).not_to match(/allure/i)
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'content_helper'
|
|
4
|
+
|
|
5
|
+
describe 'RSpec projects without Allure' do
|
|
6
|
+
AUTOMATION_TYPES.each do |automation|
|
|
7
|
+
context "with rspec and #{automation}" do
|
|
8
|
+
let(:project_name) { "#{FrameworkIndex::RSPEC}_#{automation}" }
|
|
9
|
+
|
|
10
|
+
it 'does not mention Allure in any generated file' do
|
|
11
|
+
files = Dir.glob("#{project_name}/**/*", File::FNM_DOTMATCH).select { |f| File.file?(f) }
|
|
12
|
+
files = files.reject { |f| File.binread(f, 512).include?("\x00") }
|
|
13
|
+
expect(files.select { |f| File.read(f).match?(/allure/i) }).to be_empty
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'does not generate the allure helper or the allure-results folder' do
|
|
17
|
+
expect(File).not_to exist(File.join(project_name, 'helpers/allure_helper.rb'))
|
|
18
|
+
expect(File).not_to exist(File.join(project_name, 'allure-results'))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'pins raider_reporter 0.7' do
|
|
22
|
+
expect(read_generated(project_name, 'Gemfile')).to include("gem 'raider_reporter', '~> 0.7'")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -104,10 +104,10 @@ 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.
|
|
107
|
+
it 'resolves raider_reporter 0.5 from RubyGems and ships the raider-reporter executable', :aggregate_failures do
|
|
108
108
|
skip 'Bundle install failed' unless bundle_install(project)
|
|
109
109
|
|
|
110
|
-
expect(File.read(File.join(project, 'Gemfile.lock'))).to match(/^ raider_reporter \(0\.
|
|
110
|
+
expect(File.read(File.join(project, 'Gemfile.lock'))).to match(/^ raider_reporter \(0\.5\.\d+\)$/)
|
|
111
111
|
result = run_in_project(project, 'bundle exec raider-reporter --help', timeout: 60)
|
|
112
112
|
expect(result[:exit_code]).to eq(0), "STDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}"
|
|
113
113
|
expect(result[:stdout] + result[:stderr]).to include('raider-reporter comment')
|
|
@@ -163,11 +163,11 @@ describe 'End-to-End Project Generation and Execution' do
|
|
|
163
163
|
"STDERR: #{result[:stderr]}"
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
it 'resolves raider_reporter 0.
|
|
166
|
+
it 'resolves raider_reporter 0.5 from RubyGems and requires its Cucumber plugin', :aggregate_failures do
|
|
167
167
|
skip 'Bundle install failed' unless bundle_install(project)
|
|
168
168
|
|
|
169
169
|
lockfile = File.read(File.join(project, 'Gemfile.lock'))
|
|
170
|
-
expect(lockfile).to match(/^ raider_reporter \(0\.
|
|
170
|
+
expect(lockfile).to match(/^ raider_reporter \(0\.5\.\d+\)$/)
|
|
171
171
|
expect(File.read(File.join(project, 'features/support/env.rb'))).to include("require 'raider_reporter/cucumber'")
|
|
172
172
|
end
|
|
173
173
|
|
|
@@ -262,6 +262,95 @@ describe 'End-to-End Project Generation and Execution' do
|
|
|
262
262
|
end
|
|
263
263
|
end
|
|
264
264
|
|
|
265
|
+
# Recorded Azure Pipelines env of a push (IndividualCI) build, see raider_reporter's CiDetector specs
|
|
266
|
+
AZURE_PUSH_ENV = {
|
|
267
|
+
'TF_BUILD' => 'True',
|
|
268
|
+
'SYSTEM_COLLECTIONURI' => 'https://dev.azure.com/raider-test/',
|
|
269
|
+
'SYSTEM_TEAMPROJECT' => 'My Project',
|
|
270
|
+
'SYSTEM_TEAMPROJECTID' => '6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c',
|
|
271
|
+
'BUILD_REPOSITORY_NAME' => 'web tests',
|
|
272
|
+
'BUILD_REPOSITORY_ID' => '1c2bd1a0-3f63-4d93-9c3c-2b4f2b1d8f11',
|
|
273
|
+
'BUILD_REPOSITORY_PROVIDER' => 'TfsGit',
|
|
274
|
+
'BUILD_BUILDID' => '42',
|
|
275
|
+
'SYSTEM_JOBDISPLAYNAME' => 'tests',
|
|
276
|
+
'BUILD_REASON' => 'IndividualCI',
|
|
277
|
+
'BUILD_SOURCEBRANCH' => 'refs/heads/release/1.2',
|
|
278
|
+
'BUILD_SOURCEVERSION' => 'abc'
|
|
279
|
+
}.freeze
|
|
280
|
+
|
|
281
|
+
# Generates a ci: azure web project (the shared before(:all) only builds the default github ones)
|
|
282
|
+
def generate_azure_project(framework)
|
|
283
|
+
name = "azure_#{framework}_selenium"
|
|
284
|
+
unless LazyProjectGenerator::GENERATED.include?(name)
|
|
285
|
+
generate_framework(framework:, automation: 'selenium', name:, ci: 'azure')
|
|
286
|
+
LazyProjectGenerator::GENERATED.add(name)
|
|
287
|
+
end
|
|
288
|
+
name
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def azure_env_prefix
|
|
292
|
+
"GITHUB_ACTIONS= HEADLESS=true #{AZURE_PUSH_ENV.map { |k, v| "#{k}='#{v}'" }.join(' ')}"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def expect_azure_ci_block(project)
|
|
296
|
+
run = JSON.parse(File.read(File.join(project, 'raider-results', 'run.json')))
|
|
297
|
+
ci = run.dig('run', 'ci') || run['ci']
|
|
298
|
+
expect(ci).not_to be_nil, "No ci block in run.json: #{run.keys}"
|
|
299
|
+
expect(ci['provider']).to eq('azure_pipelines')
|
|
300
|
+
expect(ci['build_key']).to start_with('azure_pipelines:')
|
|
301
|
+
expect(ci['build_key']).to eq(
|
|
302
|
+
'azure_pipelines:dev.azure.com/raider-test/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c/' \
|
|
303
|
+
'1c2bd1a0-3f63-4d93-9c3c-2b4f2b1d8f11:42:1'
|
|
304
|
+
)
|
|
305
|
+
expect(ci['build_url']).to eq('https://dev.azure.com/raider-test/My%20Project/_build/results?buildId=42')
|
|
306
|
+
puts "\nrun.json ci block: #{JSON.pretty_generate(ci)}"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
context 'Azure Pipelines projects (ci: azure)' do
|
|
310
|
+
describe 'Selenium + RSpec' do
|
|
311
|
+
let(:project) { generate_azure_project('rspec') }
|
|
312
|
+
|
|
313
|
+
it 'ships azure-pipelines.yml and resolves raider_reporter 0.5', :aggregate_failures do
|
|
314
|
+
expect(File).to exist(File.join(project, 'azure-pipelines.yml'))
|
|
315
|
+
expect(File).not_to exist(File.join(project, '.github/workflows'))
|
|
316
|
+
skip 'Bundle install failed' unless bundle_install(project)
|
|
317
|
+
|
|
318
|
+
expect(File.read(File.join(project, 'Gemfile.lock'))).to match(/^ raider_reporter \(0\.5\.\d+\)$/)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it 'records provider azure_pipelines and an azure_pipelines: build key in run.json', :aggregate_failures, :slow do
|
|
322
|
+
skip 'Bundle install failed' unless bundle_install(project)
|
|
323
|
+
|
|
324
|
+
FileUtils.rm_rf(File.join(project, 'raider-results'))
|
|
325
|
+
result = run_in_project(project, "#{azure_env_prefix} bundle exec rspec spec/ --format documentation",
|
|
326
|
+
timeout: 120)
|
|
327
|
+
expect(result[:success]).to be(true), "STDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}"
|
|
328
|
+
expect_azure_ci_block(project)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
describe 'Selenium + Cucumber' do
|
|
333
|
+
let(:project) { generate_azure_project('cucumber') }
|
|
334
|
+
|
|
335
|
+
it 'ships azure-pipelines.yml and resolves raider_reporter 0.5', :aggregate_failures do
|
|
336
|
+
expect(File).to exist(File.join(project, 'azure-pipelines.yml'))
|
|
337
|
+
skip 'Bundle install failed' unless bundle_install(project)
|
|
338
|
+
|
|
339
|
+
expect(File.read(File.join(project, 'Gemfile.lock'))).to match(/^ raider_reporter \(0\.5\.\d+\)$/)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it 'records provider azure_pipelines and an azure_pipelines: build key in run.json', :aggregate_failures, :slow do
|
|
343
|
+
skip 'Bundle install failed' unless bundle_install(project)
|
|
344
|
+
|
|
345
|
+
FileUtils.rm_rf(File.join(project, 'raider-results'))
|
|
346
|
+
result = run_in_project(project, "#{azure_env_prefix} bundle exec cucumber features/ --format pretty",
|
|
347
|
+
timeout: 120)
|
|
348
|
+
expect(result[:success]).to be(true), "STDOUT: #{result[:stdout]}\nSTDERR: #{result[:stderr]}"
|
|
349
|
+
expect_azure_ci_block(project)
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
265
354
|
# Mobile and Visual frameworks require external services, so we only verify structure
|
|
266
355
|
context 'Mobile Automation Frameworks (structure validation only)' do
|
|
267
356
|
describe 'iOS + RSpec' do
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require_relative '../../../lib/generators/infrastructure/azure_generator'
|
|
6
|
+
require_relative '../spec_helper'
|
|
7
|
+
|
|
8
|
+
describe AzureGenerator do
|
|
9
|
+
mobile_notice = "Azure Pipelines template for mobile projects is not available yet; see the README.\n"
|
|
10
|
+
web = FRAMEWORKS.product(%w[selenium watir], [false, true])
|
|
11
|
+
mobile = FRAMEWORKS.product(%w[ios android cross_platform])
|
|
12
|
+
|
|
13
|
+
after(:all) do # rubocop:disable RSpec/BeforeAfterAll
|
|
14
|
+
FileUtils.rm_rf(Dir.glob('azure_*'))
|
|
15
|
+
FileUtils.rm_rf(Dir.glob('plain_*'))
|
|
16
|
+
FileUtils.rm_rf(Dir.glob('ghci_*'))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
web.each do |framework, automation, axe|
|
|
20
|
+
context "with ci azure, #{framework}, #{automation}#{', axe' if axe}" do
|
|
21
|
+
let(:name) { "azure_#{framework}_#{automation}#{'_axe' if axe}" }
|
|
22
|
+
|
|
23
|
+
before do
|
|
24
|
+
next if File.exist?(name)
|
|
25
|
+
|
|
26
|
+
InvokeGenerators.generate_framework(automation:, framework:, name:, accessibility: axe, ci: 'azure')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'creates azure-pipelines.yml and no .github directory' do
|
|
30
|
+
expect(File).to exist("#{name}/azure-pipelines.yml")
|
|
31
|
+
expect(File).not_to exist("#{name}/.github")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'is parseable YAML' do
|
|
35
|
+
expect { YAML.safe_load_file("#{name}/azure-pipelines.yml") }.not_to raise_error
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
mobile.each do |framework, automation|
|
|
41
|
+
context "with ci azure, #{framework}, #{automation}" do
|
|
42
|
+
let(:name) { "azure_#{framework}_#{automation}" }
|
|
43
|
+
|
|
44
|
+
it 'generates no pipeline, no .github and prints the notice' do
|
|
45
|
+
expect do
|
|
46
|
+
InvokeGenerators.generate_framework(automation:, framework:, name:, ci: 'azure')
|
|
47
|
+
end.to output(/#{Regexp.escape(mobile_notice)}/).to_stdout
|
|
48
|
+
expect(File).not_to exist("#{name}/azure-pipelines.yml")
|
|
49
|
+
expect(File).not_to exist("#{name}/.github")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'with the default ci and ci github' do
|
|
55
|
+
it 'generates GitHub files and no azure-pipelines.yml' do
|
|
56
|
+
InvokeGenerators.generate_framework(automation: 'selenium', framework: 'rspec', name: 'plain_default')
|
|
57
|
+
InvokeGenerators.generate_framework(automation: 'selenium', framework: 'rspec', name: 'ghci_github', ci: 'github')
|
|
58
|
+
%w[plain_default ghci_github].each do |name|
|
|
59
|
+
expect(File).to exist("#{name}/.github/workflows/test_pipeline.yml")
|
|
60
|
+
expect(File).not_to exist("#{name}/azure-pipelines.yml")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'with an invalid ci' do
|
|
66
|
+
it 'raises an ArgumentError and generates nothing' do
|
|
67
|
+
expect do
|
|
68
|
+
InvokeGenerators.generate_framework(automation: 'selenium', framework: 'rspec', name: 'azure_bad', ci: 'gitlab')
|
|
69
|
+
end.to raise_error(ArgumentError, 'ci must be github or azure')
|
|
70
|
+
expect(File).not_to exist('azure_bad')
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'with raider new and ci:gitlab' do
|
|
75
|
+
it 'prints the error, generates nothing and exits non-zero' do
|
|
76
|
+
root = File.expand_path('../../..', __dir__)
|
|
77
|
+
out = IO.popen(['ruby', "#{root}/bin/raider", 'new', 'azure_cli_bad', '-p', 'framework:rspec',
|
|
78
|
+
'automation:selenium', 'ci:gitlab', { err: %i[child out] }], &:read)
|
|
79
|
+
expect($CHILD_STATUS.exitstatus).not_to eq(0)
|
|
80
|
+
expect(out).to include('Invalid parameters: ci must be github or azure')
|
|
81
|
+
expect(File).not_to exist('azure_cli_bad')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'with a project named azure' do
|
|
86
|
+
after { FileUtils.rm_rf(%w[azure azure_named_ci]) }
|
|
87
|
+
|
|
88
|
+
it 'generates the GitHub workflow with the default ci' do
|
|
89
|
+
InvokeGenerators.generate_framework(automation: 'selenium', framework: 'rspec', name: 'azure')
|
|
90
|
+
expect(File).to exist('azure/.github/workflows')
|
|
91
|
+
expect(File).not_to exist('azure/azure-pipelines.yml')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'generates the Azure pipeline with ci azure' do
|
|
95
|
+
InvokeGenerators.generate_framework(automation: 'selenium', framework: 'rspec', name: 'azure', ci: 'azure')
|
|
96
|
+
expect(File).to exist('azure/azure-pipelines.yml')
|
|
97
|
+
expect(File).not_to exist('azure/.github')
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -5,12 +5,8 @@ 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
|
|
9
|
-
|
|
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
|
|
8
|
+
it 'never creates an allure helper file' do
|
|
9
|
+
expect(File).not_to exist("#{name}/helpers/allure_helper.rb")
|
|
14
10
|
end
|
|
15
11
|
end
|
|
16
12
|
|
|
@@ -157,4 +157,53 @@ RSpec.describe MenuGenerator do
|
|
|
157
157
|
)
|
|
158
158
|
end
|
|
159
159
|
end
|
|
160
|
+
|
|
161
|
+
describe 'CI provider question' do
|
|
162
|
+
let(:menu) { double('menu') } # rubocop:disable RSpec/VerifiedDoubles
|
|
163
|
+
|
|
164
|
+
before do
|
|
165
|
+
allow(menu).to receive(:choice)
|
|
166
|
+
allow(menu_generator).to receive(:generate_framework)
|
|
167
|
+
allow(menu_generator).to receive(:system)
|
|
168
|
+
allow(prompt).to receive(:say)
|
|
169
|
+
allow(prompt).to receive(:yes?).and_return(false)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'is asked after accessibility for web automations' do
|
|
173
|
+
allow(prompt).to receive(:select).with('Which CI provider?').and_yield(menu)
|
|
174
|
+
|
|
175
|
+
menu_generator.send(:select_ci, 'Rspec', 'selenium', accessibility: false)
|
|
176
|
+
|
|
177
|
+
expect(menu).to have_received(:choice).with('GitHub Actions', anything)
|
|
178
|
+
expect(menu).to have_received(:choice).with('Azure Pipelines', anything)
|
|
179
|
+
expect(menu).to have_received(:choice).with(:Quit, anything)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'follows the accessibility answer' do
|
|
183
|
+
allow(prompt).to receive(:select).with('Add accessibility testing (axe)?').and_yield(menu)
|
|
184
|
+
allow(menu).to receive(:choice).with(:No, anything) { |_, action| action.call }
|
|
185
|
+
allow(menu_generator).to receive(:select_ci)
|
|
186
|
+
|
|
187
|
+
menu_generator.send(:select_accessibility, 'Rspec', 'selenium')
|
|
188
|
+
|
|
189
|
+
expect(menu_generator).to have_received(:select_ci).with('Rspec', 'selenium', accessibility: false)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it 'is skipped for mobile' do
|
|
193
|
+
allow(prompt).to receive(:select)
|
|
194
|
+
|
|
195
|
+
menu_generator.send(:select_accessibility, 'Rspec', 'ios')
|
|
196
|
+
|
|
197
|
+
expect(prompt).not_to have_received(:select)
|
|
198
|
+
expect(menu_generator).to have_received(:generate_framework).with(hash_including(automation: 'ios'))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'passes ci azure through to generate_framework' do
|
|
202
|
+
menu_generator.send(:create_framework, 'Rspec', 'selenium', ci_provider: 'azure')
|
|
203
|
+
|
|
204
|
+
expect(menu_generator).to have_received(:generate_framework).with(
|
|
205
|
+
hash_including(automation: 'selenium', ci: 'azure')
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
160
209
|
end
|