1# frozen_string_literal: true
2
3require 'spec_helper'
4
5RSpec.describe 'Runners' do
6  let(:user) { create(:user) }
7
8  before do
9    sign_in(user)
10  end
11
12  context 'when user opens runners page' do
13    let(:project) { create(:project) }
14
15    before do
16      project.add_maintainer(user)
17    end
18
19    it 'user can see a link with instructions on how to install GitLab Runner' do
20      visit project_runners_path(project)
21
22      expect(page).to have_link('Install GitLab Runner and ensure it\'s running.', href: "https://docs.gitlab.com/runner/install/")
23    end
24  end
25
26  context 'when a project has enabled shared_runners' do
27    let(:project) { create(:project) }
28
29    before do
30      project.add_maintainer(user)
31    end
32
33    context 'when a project_type runner is activated on the project' do
34      let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
35
36      it 'user sees the specific runner' do
37        visit project_runners_path(project)
38
39        within '.activated-specific-runners' do
40          expect(page).to have_content(specific_runner.display_name)
41        end
42
43        click_on specific_runner.short_sha
44
45        expect(page).to have_content(specific_runner.platform)
46      end
47
48      it 'user can pause and resume the specific runner' do
49        visit project_runners_path(project)
50
51        within '.activated-specific-runners' do
52          expect(page).to have_link('Pause')
53        end
54
55        click_on 'Pause'
56
57        within '.activated-specific-runners' do
58          expect(page).to have_link('Resume')
59        end
60
61        click_on 'Resume'
62
63        within '.activated-specific-runners' do
64          expect(page).to have_link('Pause')
65        end
66      end
67
68      it 'user removes an activated specific runner if this is last project for that runners' do
69        visit project_runners_path(project)
70
71        within '.activated-specific-runners' do
72          click_on 'Remove runner'
73        end
74
75        expect(page).not_to have_content(specific_runner.display_name)
76      end
77
78      it 'user edits the runner to be protected' do
79        visit project_runners_path(project)
80
81        within '.activated-specific-runners' do
82          first('[data-testid="edit-runner-link"]').click
83        end
84
85        expect(page.find_field('runner[access_level]')).not_to be_checked
86
87        check 'runner_access_level'
88        click_button 'Save changes'
89
90        expect(page).to have_content 'Protected Yes'
91      end
92
93      context 'when a runner has a tag' do
94        before do
95          specific_runner.update!(tag_list: ['tag'])
96        end
97
98        it 'user edits runner not to run untagged jobs' do
99          visit project_runners_path(project)
100
101          within '.activated-specific-runners' do
102            first('[data-testid="edit-runner-link"]').click
103          end
104
105          expect(page.find_field('runner[run_untagged]')).to be_checked
106
107          uncheck 'runner_run_untagged'
108          click_button 'Save changes'
109
110          expect(page).to have_content 'Can run untagged jobs No'
111        end
112      end
113
114      context 'when a shared runner is activated on the project' do
115        let!(:shared_runner) { create(:ci_runner, :instance) }
116
117        it 'user sees CI/CD setting page' do
118          visit project_runners_path(project)
119
120          expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
121        end
122      end
123    end
124
125    context 'when multiple runners are configured' do
126      let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
127      let!(:specific_runner_2) { create(:ci_runner, :project, projects: [project]) }
128
129      it 'adds pagination to the runner list' do
130        stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
131
132        visit project_runners_path(project)
133
134        expect(find('.pagination')).not_to be_nil
135      end
136    end
137
138    context 'when a specific runner exists in another project' do
139      let(:another_project) { create(:project) }
140      let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
141
142      before do
143        another_project.add_maintainer(user)
144      end
145
146      it 'user enables and disables a specific runner' do
147        visit project_runners_path(project)
148
149        within '.available-specific-runners' do
150          click_on 'Enable for this project'
151        end
152
153        expect(page.find('.activated-specific-runners')).to have_content(specific_runner.display_name)
154
155        within '.activated-specific-runners' do
156          click_on 'Disable for this project'
157        end
158
159        expect(page.find('.available-specific-runners')).to have_content(specific_runner.display_name)
160      end
161    end
162
163    context 'shared runner text' do
164      context 'when application settings have no shared_runners_text' do
165        it 'user sees default shared runners description' do
166          visit project_runners_path(project)
167
168          page.within("[data-testid='shared-runners-description']") do
169            expect(page).to have_content('The same shared runner executes code from multiple projects')
170          end
171        end
172      end
173
174      context 'when application settings have shared_runners_text' do
175        let(:shared_runners_text) { 'custom **shared** runners description' }
176        let(:shared_runners_html) { 'custom shared runners description' }
177
178        before do
179          stub_application_setting(shared_runners_text: shared_runners_text)
180        end
181
182        it 'user sees shared runners description' do
183          visit project_runners_path(project)
184
185          page.within("[data-testid='shared-runners-description']") do
186            expect(page).not_to have_content('The same shared runner executes code from multiple projects')
187            expect(page).to have_content(shared_runners_html)
188          end
189        end
190      end
191
192      context 'when application settings have an unsafe link in shared_runners_text' do
193        let(:shared_runners_text) { '<a href="javascript:alert(\'xss\')">link</a>' }
194
195        before do
196          stub_application_setting(shared_runners_text: shared_runners_text)
197        end
198
199        it 'user sees no link' do
200          visit project_runners_path(project)
201
202          page.within("[data-testid='shared-runners-description']") do
203            expect(page).to have_content('link')
204            expect(page).not_to have_link('link')
205          end
206        end
207      end
208
209      context 'when application settings have an unsafe image in shared_runners_text' do
210        let(:shared_runners_text) { '<img src="404.png" onerror="alert(\'xss\')"/>' }
211
212        before do
213          stub_application_setting(shared_runners_text: shared_runners_text)
214        end
215
216        it 'user sees image safely' do
217          visit project_runners_path(project)
218
219          page.within("[data-testid='shared-runners-description']") do
220            expect(page).to have_css('img')
221            expect(page).not_to have_css('img[onerror]')
222          end
223        end
224      end
225    end
226  end
227
228  context 'enable shared runners in project settings', :js do
229    before do
230      project.add_maintainer(user)
231
232      visit project_runners_path(project)
233    end
234
235    context 'when a project has enabled shared_runners' do
236      let(:project) { create(:project, shared_runners_enabled: true) }
237
238      it 'shared runners toggle is on' do
239        expect(page).to have_selector('[data-testid="toggle-shared-runners"]')
240        expect(page).to have_selector('[data-testid="toggle-shared-runners"] .is-checked')
241      end
242    end
243
244    context 'when a project has disabled shared_runners' do
245      let(:project) { create(:project, shared_runners_enabled: false) }
246
247      it 'shared runners toggle is off' do
248        expect(page).not_to have_selector('[data-testid="toggle-shared-runners"] .is-checked')
249      end
250    end
251  end
252
253  context 'group runners in project settings' do
254    before do
255      project.add_maintainer(user)
256    end
257
258    let(:group) { create :group }
259
260    context 'as project and group maintainer' do
261      before do
262        group.add_maintainer(user)
263      end
264
265      context 'project with a group but no group runner' do
266        let(:project) { create :project, group: group }
267
268        it 'group runners are not available' do
269          visit project_runners_path(project)
270
271          expect(page).to have_content 'This group does not have any group runners yet.'
272
273          expect(page).to have_content 'Group maintainers can register group runners in the group\'s CI/CD settings.'
274          expect(page).not_to have_content 'Ask your group maintainer to set up a group runner'
275        end
276      end
277    end
278
279    context 'as project maintainer' do
280      context 'project without a group' do
281        let(:project) { create :project }
282
283        it 'group runners are not available' do
284          visit project_runners_path(project)
285
286          expect(page).to have_content 'This project does not belong to a group and cannot make use of group runners.'
287        end
288      end
289
290      context 'project with a group but no group runner' do
291        let(:group) { create(:group) }
292        let(:project) { create(:project, group: group) }
293
294        it 'group runners are not available' do
295          visit project_runners_path(project)
296
297          expect(page).to have_content 'This group does not have any group runners yet.'
298
299          expect(page).not_to have_content 'Group maintainers can register group runners in the group\'s CI/CD settings.'
300          expect(page).to have_content 'Ask your group maintainer to set up a group runner.'
301        end
302      end
303
304      context 'project with a group and a group runner' do
305        let(:group) { create(:group) }
306        let(:project) { create(:project, group: group) }
307        let!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
308
309        it 'group runners are available' do
310          visit project_runners_path(project)
311
312          expect(page).to have_content 'Available group runners: 1'
313          expect(page).to have_content 'group-runner'
314        end
315
316        it 'group runners may be disabled for a project' do
317          visit project_runners_path(project)
318
319          click_on 'Disable group runners'
320
321          expect(page).to have_content 'Enable group runners'
322          expect(project.reload.group_runners_enabled).to be false
323
324          click_on 'Enable group runners'
325
326          expect(page).to have_content 'Disable group runners'
327          expect(project.reload.group_runners_enabled).to be true
328        end
329      end
330    end
331  end
332
333  context 'group runners in group settings' do
334    let(:group) { create(:group) }
335
336    before do
337      group.add_owner(user)
338    end
339
340    context 'group with no runners' do
341      it 'there are no runners displayed' do
342        visit group_settings_ci_cd_path(group)
343
344        expect(page).to have_content 'No runners found'
345      end
346    end
347
348    context 'group with a runner' do
349      let!(:runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
350
351      it 'the runner is visible' do
352        visit group_settings_ci_cd_path(group)
353
354        expect(page).not_to have_content 'No runners found'
355        expect(page).to have_content 'Available runners: 1'
356        expect(page).to have_content 'group-runner'
357      end
358
359      it 'user can pause and resume the group runner' do
360        visit group_settings_ci_cd_path(group)
361
362        expect(page).to have_link href: pause_group_runner_path(group, runner)
363        expect(page).not_to have_link href: resume_group_runner_path(group, runner)
364
365        click_link href: pause_group_runner_path(group, runner)
366
367        expect(page).not_to have_link href: pause_group_runner_path(group, runner)
368        expect(page).to have_link href: resume_group_runner_path(group, runner)
369
370        click_link href: resume_group_runner_path(group, runner)
371
372        expect(page).to have_link href: pause_group_runner_path(group, runner)
373        expect(page).not_to have_link href: resume_group_runner_path(group, runner)
374      end
375
376      it 'user can view runner details' do
377        visit group_settings_ci_cd_path(group)
378
379        expect(page).to have_content(runner.display_name)
380
381        click_on runner.short_sha
382
383        expect(page).to have_content(runner.platform)
384      end
385
386      it 'user can remove a group runner' do
387        visit group_settings_ci_cd_path(group)
388
389        all(:link, href: group_runner_path(group, runner))[1].click
390
391        expect(page).not_to have_content(runner.display_name)
392      end
393
394      it 'user edits the runner to be protected' do
395        visit group_settings_ci_cd_path(group)
396
397        click_link href: edit_group_runner_path(group, runner)
398
399        expect(page.find_field('runner[access_level]')).not_to be_checked
400
401        check 'runner_access_level'
402        click_button 'Save changes'
403
404        expect(page).to have_content 'Protected Yes'
405      end
406
407      context 'when a runner has a tag' do
408        before do
409          runner.update!(tag_list: ['tag'])
410        end
411
412        it 'user edits runner not to run untagged jobs' do
413          visit group_settings_ci_cd_path(group)
414
415          click_link href: edit_group_runner_path(group, runner)
416
417          expect(page.find_field('runner[run_untagged]')).to be_checked
418
419          uncheck 'runner_run_untagged'
420          click_button 'Save changes'
421
422          expect(page).to have_content 'Can run untagged jobs No'
423        end
424      end
425    end
426
427    context 'group with a project runner' do
428      let(:project) { create(:project, group: group) }
429      let!(:runner) { create(:ci_runner, :project, projects: [project], description: 'project-runner') }
430
431      it 'the runner is visible' do
432        visit group_settings_ci_cd_path(group)
433
434        expect(page).not_to have_content 'No runners found'
435        expect(page).to have_content 'Available runners: 1'
436        expect(page).to have_content 'project-runner'
437      end
438
439      it 'user can pause and resume the project runner' do
440        visit group_settings_ci_cd_path(group)
441
442        expect(page).to have_link href: pause_group_runner_path(group, runner)
443        expect(page).not_to have_link href: resume_group_runner_path(group, runner)
444
445        click_link href: pause_group_runner_path(group, runner)
446
447        expect(page).not_to have_link href: pause_group_runner_path(group, runner)
448        expect(page).to have_link href: resume_group_runner_path(group, runner)
449
450        click_link href: resume_group_runner_path(group, runner)
451
452        expect(page).to have_link href: pause_group_runner_path(group, runner)
453        expect(page).not_to have_link href: resume_group_runner_path(group, runner)
454      end
455
456      it 'user can view runner details' do
457        visit group_settings_ci_cd_path(group)
458
459        expect(page).to have_content(runner.display_name)
460
461        click_on runner.short_sha
462
463        expect(page).to have_content(runner.platform)
464      end
465
466      it 'user can remove a project runner' do
467        visit group_settings_ci_cd_path(group)
468
469        all(:link, href: group_runner_path(group, runner))[1].click
470
471        expect(page).not_to have_content(runner.display_name)
472      end
473
474      it 'user edits the runner to be protected' do
475        visit group_settings_ci_cd_path(group)
476
477        click_link href: edit_group_runner_path(group, runner)
478
479        expect(page.find_field('runner[access_level]')).not_to be_checked
480
481        check 'runner_access_level'
482        click_button 'Save changes'
483
484        expect(page).to have_content 'Protected Yes'
485      end
486
487      context 'when a runner has a tag' do
488        before do
489          runner.update!(tag_list: ['tag'])
490        end
491
492        it 'user edits runner not to run untagged jobs' do
493          visit group_settings_ci_cd_path(group)
494
495          click_link href: edit_group_runner_path(group, runner)
496
497          expect(page.find_field('runner[run_untagged]')).to be_checked
498
499          uncheck 'runner_run_untagged'
500          click_button 'Save changes'
501
502          expect(page).to have_content 'Can run untagged jobs No'
503        end
504      end
505    end
506
507    context 'group with a multi-project runner' do
508      let(:project) { create(:project, group: group) }
509      let(:project_2) { create(:project, group: group) }
510      let!(:runner) { create(:ci_runner, :project, projects: [project, project_2], description: 'group-runner') }
511
512      it 'user cannot remove the project runner' do
513        visit group_settings_ci_cd_path(group)
514
515        expect(all(:link, href: group_runner_path(group, runner)).length).to eq(1)
516      end
517    end
518
519    context 'filtered search' do
520      it 'allows user to search by status and type', :js do
521        visit group_settings_ci_cd_path(group)
522
523        find('.filtered-search').click
524
525        page.within('#js-dropdown-hint') do
526          expect(page).to have_content('Status')
527          expect(page).to have_content('Type')
528          expect(page).not_to have_content('Tag')
529        end
530      end
531    end
532  end
533end
534