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