diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 15:17:55 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-01 15:17:55 +0300 |
commit | 1ae73e5bfcb2dc4a49f40886f729fc4797ba2a65 (patch) | |
tree | 03cb6dac129fb29c9500651a051d1f61966d79b8 /spec | |
parent | 719524126026437133586ce042dfc48c0d166ecd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
17 files changed, 1129 insertions, 1000 deletions
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 988051bca40..c8cbf396098 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -29,443 +29,421 @@ RSpec.describe 'File blob', :js do ).execute end - before do - stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350455 - end - - context 'Ruby file' do - before do - visit_blob('files/ruby/popen.rb') - - wait_for_requests - end - - it 'displays the blob' do - aggregate_failures do - # shows highlighted Ruby code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("require 'fileutils'") - - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') - - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - - # shows a raw button - expect(page).to have_link('Open raw') - end - end - - it 'displays file actions on all screen sizes' do - file_actions_selector = '.file-actions' - - resize_screen_sm - expect(page).to have_selector(file_actions_selector, visible: true) - - resize_screen_xs - expect(page).to have_selector(file_actions_selector, visible: true) - end - end - - context 'Markdown file' do - context 'visiting directly' do + context 'with refactor_blob_viewer feature flag enabled' do + context 'Ruby file' do before do - visit_blob('files/markdown/ruby-style-guide.md') + visit_blob('files/ruby/popen.rb') wait_for_requests end - it 'displays the blob using the rich viewer' do + it 'displays the blob' do aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows rendered Markdown - expect(page).to have_link("PEP-8") + # shows highlighted Ruby code + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("require 'fileutils'") - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # shows a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') # shows a raw button expect(page).to have_link('Open raw') end end - context 'switching to the simple viewer' do + it 'displays file actions on all screen sizes' do + file_actions_selector = '.file-actions' + + resize_screen_sm + expect(page).to have_selector(file_actions_selector, visible: true) + + resize_screen_xs + expect(page).to have_selector(file_actions_selector, visible: true) + end + end + + context 'Markdown file' do + context 'visiting directly' do before do - find('.js-blob-viewer-switch-btn[data-viewer=simple]').click + visit_blob('files/markdown/ruby-style-guide.md') wait_for_requests end - it 'displays the blob using the simple viewer' do + it 'displays the blob using the rich viewer' do aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]') - # shows highlighted Markdown code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + # shows rendered Markdown + expect(page).to have_link("PEP-8") - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # shows a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + + # shows a raw button + expect(page).to have_link('Open raw') end end - context 'switching to the rich viewer again' do + context 'switching to the simple viewer' do before do - find('.js-blob-viewer-switch-btn[data-viewer=rich]').click + find('.js-blob-viewer-switch-btn[data-viewer=simple]').click wait_for_requests end - it 'displays the blob using the rich viewer' do + it 'displays the blob using the simple viewer' do aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') + + # shows highlighted Markdown code + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") # shows an enabled copy button expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') end end - end - end - end - context 'when ref switch' do - def switch_ref_to(ref_name) - first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage - - page.within '.project-refs-form' do - click_link ref_name - wait_for_requests - end - end + context 'switching to the rich viewer again' do + before do + find('.js-blob-viewer-switch-btn[data-viewer=rich]').click - it 'displays single highlighted line number of different ref' do - visit_blob('files/js/application.js', anchor: 'L1') + wait_for_requests + end - switch_ref_to('feature') + it 'displays the blob using the rich viewer' do + aggregate_failures do + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).to have_selector('.blob-viewer[data-type="rich"]') - page.within '.blob-content' do - expect(find_by_id('LC1')[:class]).to include("hll") + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + end + end + end end end - it 'displays multiple highlighted line numbers of different ref' do - visit_blob('files/js/application.js', anchor: 'L1-3') + context 'when ref switch' do + def switch_ref_to(ref_name) + first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage - switch_ref_to('feature') - - page.within '.blob-content' do - expect(find_by_id('LC1')[:class]).to include("hll") - expect(find_by_id('LC2')[:class]).to include("hll") - expect(find_by_id('LC3')[:class]).to include("hll") + page.within '.project-refs-form' do + click_link ref_name + wait_for_requests + end end - end - it 'displays no highlighted number of different ref' do - Files::UpdateService.new( - project, - project.first_owner, - commit_message: 'Update', - start_branch: 'feature', - branch_name: 'feature', - file_path: 'files/js/application.js', - file_content: 'new content' - ).execute + it 'displays no highlighted number of different ref' do + Files::UpdateService.new( + project, + project.first_owner, + commit_message: 'Update', + start_branch: 'feature', + branch_name: 'feature', + file_path: 'files/js/application.js', + file_content: 'new content' + ).execute - project.commit('feature').diffs.diff_files.first + project.commit('feature').diffs.diff_files.first - visit_blob('files/js/application.js', anchor: 'L3') - switch_ref_to('feature') + visit_blob('files/js/application.js', anchor: 'L3') + switch_ref_to('feature') - page.within '.blob-content' do - expect(page).not_to have_css('.hll') + page.within '.blob-content' do + expect(page).not_to have_css('.hll') + end end - end - context 'successfully change ref of similar name' do - before do - project.repository.create_branch('dev') - project.repository.create_branch('development') - end + context 'successfully change ref of similar name' do + before do + project.repository.create_branch('dev') + project.repository.create_branch('development') + end - it 'switch ref from longer to shorter ref name' do - visit_blob('files/js/application.js', ref: 'development') - switch_ref_to('dev') + it 'switch ref from longer to shorter ref name' do + visit_blob('files/js/application.js', ref: 'development') + switch_ref_to('dev') - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') + end end - end - it 'switch ref from shorter to longer ref name' do - visit_blob('files/js/application.js', ref: 'dev') - switch_ref_to('development') + it 'switch ref from shorter to longer ref name' do + visit_blob('files/js/application.js', ref: 'dev') + switch_ref_to('development') - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') + end end end - end - it 'successfully changes ref when the ref name matches the project name' do - project.repository.create_branch(project.name) + it 'successfully changes ref when the ref name matches the project name' do + project.repository.create_branch(project.name) - visit_blob('files/js/application.js', ref: project.name) - switch_ref_to('master') + visit_blob('files/js/application.js', ref: project.name) + switch_ref_to('master') - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') + end end end end - context 'visiting with a line number anchor' do + context 'Markdown rendering' do before do - visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1') - end + project.add_maintainer(project.creator) - it 'displays the blob using the simple viewer' do - aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add RedCarpet and CommonMark Markdown ", + file_path: 'files/commonmark/file.md', + file_content: "1. one\n - sublist\n" + ).execute + end - # highlights the line in question - expect(page).to have_selector('#LC1.hll') + context 'when rendering default markdown' do + before do + visit_blob('files/commonmark/file.md') - # shows highlighted Markdown code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + wait_for_requests + end - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + it 'renders using CommonMark' do + aggregate_failures do + expect(page).to have_content("sublist") + expect(page).not_to have_xpath("//ol//li//ul") + end end end end - end - - context 'Markdown rendering' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add RedCarpet and CommonMark Markdown ", - file_path: 'files/commonmark/file.md', - file_content: "1. one\n - sublist\n" - ).execute - end - context 'when rendering default markdown' do + context 'Markdown file (stored in LFS)' do before do - visit_blob('files/commonmark/file.md') - - wait_for_requests - end + project.add_maintainer(project.creator) - it 'renders using CommonMark' do - aggregate_failures do - expect(page).to have_content("sublist") - expect(page).not_to have_xpath("//ol//li//ul") - end + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add Markdown in LFS", + file_path: 'files/lfs/file.md', + file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data + ).execute end - end - end - context 'Markdown file (stored in LFS)' do - before do - project.add_maintainer(project.creator) - - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Markdown in LFS", - file_path: 'files/lfs/file.md', - file_content: project.repository.blob_at('master', 'files/lfs/lfs_object.iso').data - ).execute - end - - context 'when LFS is enabled on the project' do - before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) + context 'when LFS is enabled on the project' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) - visit_blob('files/lfs/file.md') + visit_blob('files/lfs/file.md') - wait_for_requests - end + wait_for_requests + end - it 'displays an error' do - aggregate_failures do - # hides the simple viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) - expect(page).to have_selector('.blob-viewer[data-type="rich"]') + it 'displays an error' do + aggregate_failures do + # hides the simple viewer + expect(page).not_to have_selector('.blob-viewer[data-type="simple"]') + expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') - # shows an error message - expect(page).to have_content('The rendered file could not be displayed because it is stored in LFS. You can download it instead.') + # shows an error message + expect(page).to have_content('This content could not be displayed because it is stored in LFS. You can download it instead.') - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') - # shows a download button - expect(page).to have_link('Download') + # shows a download button + expect(page).to have_link('Download') + end end end - context 'switching to the simple viewer' do + context 'when LFS is disabled on the project' do before do - find('.js-blob-viewer-switcher .js-blob-viewer-switch-btn[data-viewer=simple]').click + visit_blob('files/lfs/file.md') wait_for_requests end - it 'displays an error' do + it 'displays the blob' do aggregate_failures do - # hides the rich viewer - expect(page).to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) + # shows text + expect(page).to have_content('size 1575078') - # shows an error message - expect(page).to have_content('The source could not be displayed because it is stored in LFS. You can download it instead.') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + + # shows a raw button + expect(page).to have_link('Open raw') end end end end - context 'when LFS is disabled on the project' do + context 'PDF file' do before do - visit_blob('files/lfs/file.md') + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add PDF", + file_path: 'files/test.pdf', + file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data + ).execute + + visit_blob('files/test.pdf') wait_for_requests end it 'displays the blob' do aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + # shows rendered PDF + expect(page).to have_selector('.js-pdf-viewer') # does not show a viewer switcher expect(page).not_to have_selector('.js-blob-viewer-switcher') - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') - # shows a raw button - expect(page).to have_link('Open raw') + # shows a download button + expect(page).to have_link('Download') end end end - end - context 'PDF file' do - before do - project.add_maintainer(project.creator) + context 'Jupiter Notebook file' do + before do + project.add_maintainer(project.creator) - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add PDF", - file_path: 'files/test.pdf', - file_content: project.repository.blob_at('add-pdf-file', 'files/pdf/test.pdf').data - ).execute + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add Jupiter Notebook", + file_path: 'files/basic.ipynb', + file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data + ).execute - visit_blob('files/test.pdf') + visit_blob('files/basic.ipynb') - wait_for_requests - end + wait_for_requests + end - it 'displays the blob' do - aggregate_failures do - # shows rendered PDF - expect(page).to have_selector('.js-pdf-viewer') + it 'displays the blob' do + aggregate_failures do + # shows rendered notebook + expect(page).to have_selector('.js-notebook-viewer-mounted') + + # does show a viewer switcher + expect(page).to have_selector('.js-blob-viewer-switcher') + + # show a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.disabled') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + # shows a raw button + expect(page).to have_link('Open raw') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + # shows a download button + expect(page).to have_link('Download') - # shows a download button - expect(page).to have_link('Download') + # shows the rendered notebook + expect(page).to have_content('test') + end end end - end - context 'Jupiter Notebook file' do - before do - project.add_maintainer(project.creator) + context 'ISO file (stored in LFS)' do + context 'when LFS is enabled on the project' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + project.update_attribute(:lfs_enabled, true) - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add Jupiter Notebook", - file_path: 'files/basic.ipynb', - file_content: project.repository.blob_at('add-ipython-files', 'files/ipython/basic.ipynb').data - ).execute + visit_blob('files/lfs/lfs_object.iso') - visit_blob('files/basic.ipynb') + wait_for_requests + end - wait_for_requests - end + it 'displays the blob' do + aggregate_failures do + # shows a download link + expect(page).to have_link('Download (1.50 MiB)') + + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # shows a download button + expect(page).to have_link('Download') + end + end + end - it 'displays the blob' do - aggregate_failures do - # shows rendered notebook - expect(page).to have_selector('.js-notebook-viewer-mounted') + context 'when LFS is disabled on the project' do + before do + visit_blob('files/lfs/lfs_object.iso') - # does show a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + wait_for_requests + end - # show a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + it 'displays the blob' do + aggregate_failures do + # shows text + expect(page).to have_content('size 1575078') - # shows a raw button - expect(page).to have_link('Open raw') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - # shows a download button - expect(page).to have_link('Download') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - # shows the rendered notebook - expect(page).to have_content('test') + # shows a raw button + expect(page).to have_link('Open raw') + end + end end end - end - context 'ISO file (stored in LFS)' do - context 'when LFS is enabled on the project' do + context 'ZIP file' do before do - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) - project.update_attribute(:lfs_enabled, true) - - visit_blob('files/lfs/lfs_object.iso') + visit_blob('Gemfile.zip') wait_for_requests end @@ -473,7 +451,7 @@ RSpec.describe 'File blob', :js do it 'displays the blob' do aggregate_failures do # shows a download link - expect(page).to have_link('Download (1.5 MB)') + expect(page).to have_link('Download (2.11 KiB)') # does not show a viewer switcher expect(page).not_to have_selector('.js-blob-viewer-switcher') @@ -487,578 +465,703 @@ RSpec.describe 'File blob', :js do end end - context 'when LFS is disabled on the project' do + context 'empty file' do before do - visit_blob('files/lfs/lfs_object.iso') + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add empty file", + file_path: 'files/empty.md', + file_content: '' + ).execute + + visit_blob('files/empty.md') wait_for_requests end - it 'displays the blob' do + it 'displays an error' do aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + # shows an error message + expect(page).to have_content('Empty file') # does not show a viewer switcher expect(page).not_to have_selector('.js-blob-viewer-switcher') - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') - # shows a raw button - expect(page).to have_link('Open raw') + # does not show a download or raw button + expect(page).not_to have_link('Download') + expect(page).not_to have_link('Open raw') end end end - end - - context 'ZIP file' do - before do - visit_blob('Gemfile.zip') - wait_for_requests - end + context 'files with auxiliary viewers' do + describe '.gitlab-ci.yml' do + before do + project.add_maintainer(project.creator) - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (2.11 KB)') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab-ci.yml", + file_path: '.gitlab-ci.yml', + file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + ).execute - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + visit_blob('.gitlab-ci.yml') + end - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that configuration is valid + expect(page).to have_content('This GitLab CI configuration is valid.') - # shows a download button - expect(page).to have_link('Download') + # shows a learn more link + expect(page).to have_link('Learn more') + end + end end - end - end - context 'empty file' do - before do - project.add_maintainer(project.creator) + describe '.gitlab/route-map.yml' do + before do + project.add_maintainer(project.creator) + + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/route-map.yml", + file_path: '.gitlab/route-map.yml', + file_content: <<-MAP.strip_heredoc + # Team data + - source: 'data/team.yml' + public: 'team/' + MAP + ).execute - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add empty file", - file_path: 'files/empty.md', - file_content: '' - ).execute + visit_blob('.gitlab/route-map.yml') + end - visit_blob('files/empty.md') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that map is valid + expect(page).to have_content('This Route Map is valid.') - wait_for_requests - end + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end - it 'displays an error' do - aggregate_failures do - # shows an error message - expect(page).to have_content('Empty file') + describe '.gitlab/dashboards/custom-dashboard.yml' do + before do + project.add_maintainer(project.creator) - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", + file_path: '.gitlab/dashboards/custom-dashboard.yml', + file_content: file_content + ).execute + end - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + context 'with metrics_dashboard_exhaustive_validations feature flag off' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end - # does not show a download or raw button - expect(page).not_to have_link('Download') - expect(page).not_to have_link('Open raw') - end - end - end + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - context 'binary file that appears to be text in the first 1024 bytes' do - before do - visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') - end + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - it 'displays the blob' do - aggregate_failures do - # shows a download link - expect(page).to have_link('Download (23.8 KB)') + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - # The specs below verify an arguably incorrect result, but since we only - # learn that the file is not actually text once the text viewer content - # is loaded asynchronously, there is no straightforward way to get these - # synchronously loaded elements to display correctly. - # - # Clicking the copy button will result in nothing being copied. - # Clicking the raw button will result in the binary file being downloaded, - # as expected. + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("panel_groups: should be an array of panel_groups objects") - # shows an enabled copy button, incorrectly - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end + end - # shows a raw button, incorrectly - expect(page).to have_link('Open raw') - end - end - end + context 'with metrics_dashboard_exhaustive_validations feature flag on' do + before do + stub_feature_flags(metrics_dashboard_exhaustive_validations: true) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') + end - context 'files with auxiliary viewers' do - before do - stub_feature_flags(refactor_blob_viewer: true) - end + context 'valid dashboard file' do + let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } - describe '.gitlab-ci.yml' do - before do - project.add_maintainer(project.creator) + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is valid + expect(page).to have_content('Metrics Dashboard YAML definition is valid.') - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab-ci.yml", - file_path: '.gitlab-ci.yml', - file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - ).execute + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end - visit_blob('.gitlab-ci.yml') - end + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that configuration is valid - expect(page).to have_content('This GitLab CI configuration is valid.') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows that dashboard yaml is invalid + expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') + expect(page).to have_content("root is missing required keys: panel_groups") - # shows a learn more link - expect(page).to have_link('Learn more') + # shows a learn more link + expect(page).to have_link('Learn more') + end + end + end end end - end - describe '.gitlab/route-map.yml' do - before do - project.add_maintainer(project.creator) + context 'LICENSE' do + before do + visit_blob('LICENSE') + end - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/route-map.yml", - file_path: '.gitlab/route-map.yml', - file_content: <<-MAP.strip_heredoc - # Team data - - source: 'data/team.yml' - public: 'team/' - MAP - ).execute + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows license + expect(page).to have_content('This project is licensed under the MIT License.') - visit_blob('.gitlab/route-map.yml') + # shows a learn more link + expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') + end + end end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that map is valid - expect(page).to have_content('This Route Map is valid.') + context '*.gemspec' do + before do + project.add_maintainer(project.creator) - # shows a learn more link - expect(page).to have_link('Learn more') + Files::CreateService.new( + project, + project.creator, + start_branch: 'master', + branch_name: 'master', + commit_message: "Add activerecord.gemspec", + file_path: 'activerecord.gemspec', + file_content: <<-SPEC.strip_heredoc + Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = "activerecord" + end + SPEC + ).execute + + visit_blob('activerecord.gemspec') end - end - end - describe '.gitlab/dashboards/custom-dashboard.yml' do - before do - project.add_maintainer(project.creator) + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows names of dependency manager and package + expect(page).to have_content('This project manages its dependencies using RubyGems.') - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add .gitlab/dashboards/custom-dashboard.yml", - file_path: '.gitlab/dashboards/custom-dashboard.yml', - file_content: file_content - ).execute + # shows a learn more link + expect(page).to have_link('Learn more', href: 'https://rubygems.org/') + end + end end - context 'with metrics_dashboard_exhaustive_validations feature flag off' do + context 'CONTRIBUTING.md' do before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: false) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') + file_name = 'CONTRIBUTING.md' + + create_file(file_name, '## Contribution guidelines') + visit_blob(file_name) end - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") + end + end + end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + context 'CHANGELOG.md' do + before do + file_name = 'CHANGELOG.md' - # shows a learn more link - expect(page).to have_link('Learn more') - end + create_file(file_name, '## Changelog for v1.0.0') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'Cargo.toml' do + before do + file_name = 'Cargo.toml' - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + create_file(file_name, ' + [package] + name = "hello_world" # the name of the package + version = "0.1.0" # the current version, obeying semver + authors = ["Alice <a@example.com>", "Bob <b@example.com>"] + ') + visit_blob(file_name) + end - # shows a learn more link - expect(page).to have_link('Learn more') - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Cargo.") end end end - context 'with metrics_dashboard_exhaustive_validations feature flag on' do + context 'Cartfile' do before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: true) - visit_blob('.gitlab/dashboards/custom-dashboard.yml') + file_name = 'Cartfile' + + create_file(file_name, ' + gitlab "Alamofire/Alamofire" == 4.9.0 + gitlab "Alamofire/AlamofireImage" ~> 3.4 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Carthage.") + end end + end - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + context 'composer.json' do + before do + file_name = 'composer.json' - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + create_file(file_name, ' + { + "license": "MIT" + } + ') + visit_blob(file_name) + end - # shows a learn more link - expect(page).to have_link('Learn more') - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Composer.") end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'Gemfile' do + before do + file_name = 'Gemfile' - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows that dashboard yaml is invalid - expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("root is missing required keys: panel_groups") + create_file(file_name, ' + source "https://rubygems.org" - # shows a learn more link - expect(page).to have_link('Learn more') - end + # Gems here + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Bundler.") end end end - end - context 'LICENSE' do - before do - visit_blob('LICENSE') - end + context 'Godeps.json' do + before do + file_name = 'Godeps.json' - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows license - expect(page).to have_content('This project is licensed under the MIT License.') + create_file(file_name, ' + { + "GoVersion": "go1.6" + } + ') + visit_blob(file_name) + end - # shows a learn more link - expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using godep.") + end end end - end - context '*.gemspec' do - before do - project.add_maintainer(project.creator) + context 'go.mod' do + before do + file_name = 'go.mod' - Files::CreateService.new( - project, - project.creator, - start_branch: 'master', - branch_name: 'master', - commit_message: "Add activerecord.gemspec", - file_path: 'activerecord.gemspec', - file_content: <<-SPEC.strip_heredoc - Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = "activerecord" - end - SPEC - ).execute + create_file(file_name, ' + module example.com/mymodule + + go 1.14 + ') + visit_blob(file_name) + end - visit_blob('activerecord.gemspec') + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Go Modules.") + end + end end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows names of dependency manager and package - expect(page).to have_content('This project manages its dependencies using RubyGems.') + context 'package.json' do + before do + file_name = 'package.json' - # shows a learn more link - expect(page).to have_link('Learn more', href: 'https://rubygems.org/') + create_file(file_name, ' + { + "name": "my-awesome-package", + "version": "1.0.0" + } + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using npm.") + end end end - end - context 'CONTRIBUTING.md' do - before do - file_name = 'CONTRIBUTING.md' + context 'podfile' do + before do + file_name = 'podfile' - create_file(file_name, '## Contribution guidelines') - visit_blob(file_name) - end + create_file(file_name, 'platform :ios, "8.0"') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("After you've reviewed these contribution guidelines, you'll be all set to contribute to this project.") + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end end end - end - context 'CHANGELOG.md' do - before do - file_name = 'CHANGELOG.md' + context 'test.podspec' do + before do + file_name = 'test.podspec' - create_file(file_name, '## Changelog for v1.0.0') - visit_blob(file_name) - end + create_file(file_name, ' + Pod::Spec.new do |s| + s.name = "TensorFlowLiteC" + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("To find the state of this project's repository at the time of any of these versions, check out the tags.") + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end end end - end - context 'Cargo.toml' do - before do - file_name = 'Cargo.toml' + context 'JSON.podspec.json' do + before do + file_name = 'JSON.podspec.json' - create_file(file_name, ' - [package] - name = "hello_world" # the name of the package - version = "0.1.0" # the current version, obeying semver - authors = ["Alice <a@example.com>", "Bob <b@example.com>"] - ') - visit_blob(file_name) - end + create_file(file_name, ' + { + "name": "JSON" + } + ') + visit_blob(file_name) + end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Cargo.") + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") + end end end - end - context 'Cartfile' do - before do - file_name = 'Cartfile' + context 'requirements.txt' do + before do + file_name = 'requirements.txt' - create_file(file_name, ' - gitlab "Alamofire/Alamofire" == 4.9.0 - gitlab "Alamofire/AlamofireImage" ~> 3.4 - ') - visit_blob(file_name) + create_file(file_name, 'Project requirements') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using pip.") + end + end end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Carthage.") + context 'yarn.lock' do + before do + file_name = 'yarn.lock' + + create_file(file_name, ' + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + ') + visit_blob(file_name) + end + + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Yarn.") + end end end end - context 'composer.json' do + context 'realtime pipelines' do before do - file_name = 'composer.json' + Files::CreateService.new( + project, + project.creator, + start_branch: 'feature', + branch_name: 'feature', + commit_message: "Add ruby file", + file_path: 'files/ruby/test.rb', + file_content: "# Awesome content" + ).execute - create_file(file_name, ' - { - "license": "MIT" - } - ') - visit_blob(file_name) + create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) + visit_blob('files/ruby/test.rb', ref: 'feature') end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Composer.") + it 'shows the realtime pipeline status' do + page.within('.commit-actions') do + expect(page).to have_css('.ci-status-icon') + expect(page).to have_css('.ci-status-icon-running') + expect(page).to have_css('.js-ci-status-icon-running') end end end - context 'Gemfile' do - before do - file_name = 'Gemfile' + context 'for subgroups' do + let(:group) { create(:group) } + let(:subgroup) { create(:group, parent: group) } + let(:project) { create(:project, :public, :repository, group: subgroup) } - create_file(file_name, ' - source "https://rubygems.org" + it 'renders tree table without errors' do + visit_blob('README.md') - # Gems here - ') - visit_blob(file_name) + expect(page).to have_selector('.file-content') + expect(page).not_to have_selector('.flash-alert') end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Bundler.") - end + it 'displays a GPG badge' do + visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') + + expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' + expect(page).to have_selector '.gpg-status-box.invalid' end end - context 'Godeps.json' do - before do - file_name = 'Godeps.json' + context 'on signed merge commit' do + it 'displays a GPG badge' do + visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') - create_file(file_name, ' - { - "GoVersion": "go1.6" - } - ') - visit_blob(file_name) - end - - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using godep.") - end + expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' + expect(page).to have_selector '.gpg-status-box.invalid' end end - context 'go.mod' do + context 'when static objects external storage is enabled' do before do - file_name = 'go.mod' + stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') + end - create_file(file_name, ' - module example.com/mymodule + context 'public project' do + before do + visit_blob('README.md') + end - go 1.14 - ') - visit_blob(file_name) - end + it 'shows open raw and download buttons with external storage URL prepended to their href' do + path = project_raw_path(project, 'master/README.md') + raw_uri = "https://cdn.gitlab.com#{path}" + download_uri = "https://cdn.gitlab.com#{path}?inline=false" - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Go Modules.") + aggregate_failures do + expect(page).to have_link 'Open raw', href: raw_uri + expect(page).to have_link 'Download', href: download_uri + end end end end + end - context 'package.json' do - before do - file_name = 'package.json' + context 'with refactor_blob_viewer feature flag disabled' do + before do + stub_feature_flags(refactor_blob_viewer: false) + end - create_file(file_name, ' - { - "name": "my-awesome-package", - "version": "1.0.0" - } - ') - visit_blob(file_name) - end + context 'when ref switch' do + # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled + # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351558 - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using npm.") + def switch_ref_to(ref_name) + first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage + + page.within '.project-refs-form' do + click_link ref_name + wait_for_requests end end - end - context 'podfile' do - before do - file_name = 'podfile' + context 'when highlighting lines' do + it 'displays single highlighted line number of different ref' do + visit_blob('files/js/application.js', anchor: 'L1') - create_file(file_name, 'platform :ios, "8.0"') - visit_blob(file_name) - end + switch_ref_to('feature') - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") + page.within '.blob-content' do + expect(find_by_id('LC1')[:class]).to include("hll") + end end - end - end - context 'test.podspec' do - before do - file_name = 'test.podspec' + it 'displays multiple highlighted line numbers of different ref' do + visit_blob('files/js/application.js', anchor: 'L1-3') - create_file(file_name, ' - Pod::Spec.new do |s| - s.name = "TensorFlowLiteC" - ') - visit_blob(file_name) - end + switch_ref_to('feature') - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") + page.within '.blob-content' do + expect(find_by_id('LC1')[:class]).to include("hll") + expect(find_by_id('LC2')[:class]).to include("hll") + expect(find_by_id('LC3')[:class]).to include("hll") + end end end end - context 'JSON.podspec.json' do - before do - file_name = 'JSON.podspec.json' + context 'visiting with a line number anchor' do + # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled + # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351558 - create_file(file_name, ' - { - "name": "JSON" - } - ') - visit_blob(file_name) + before do + visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1') end - it 'displays an auxiliary viewer' do + it 'displays the blob using the simple viewer' do aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") + # hides the rich viewer + expect(page).to have_selector('.blob-viewer[data-type="simple"]') + expect(page).not_to have_selector('.blob-viewer[data-type="rich"]') + + # highlights the line in question + expect(page).to have_selector('#LC1.hll') + + # shows highlighted Markdown code + expect(page).to have_css(".js-syntax-highlight") + expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)") + + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') end end end - context 'requirements.txt' do - before do - file_name = 'requirements.txt' + context 'binary file that appears to be text in the first 1024 bytes' do + # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled + # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351559 - create_file(file_name, 'Project requirements') - visit_blob(file_name) + before do + visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') end - - it 'displays an auxiliary viewer' do + it 'displays the blob' do aggregate_failures do - expect(page).to have_content("This project manages its dependencies using pip.") + # shows a download link + expect(page).to have_link('Download (23.8 KB)') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') + # The specs below verify an arguably incorrect result, but since we only + # learn that the file is not actually text once the text viewer content + # is loaded asynchronously, there is no straightforward way to get these + # synchronously loaded elements to display correctly. + # + # Clicking the copy button will result in nothing being copied. + # Clicking the raw button will result in the binary file being downloaded, + # as expected. + # shows an enabled copy button, incorrectly + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + # shows a raw button, incorrectly + expect(page).to have_link('Open raw') end end end - context 'yarn.lock' do - before do - file_name = 'yarn.lock' + context 'when static objects external storage is enabled' do + # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled + # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351555 - create_file(file_name, ' - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 - ') - visit_blob(file_name) + before do + stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') end - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Yarn.") + context 'private project' do + let_it_be(:project) { create(:project, :repository, :private) } + let_it_be(:user) { create(:user) } + + before do + project.add_developer(user) + + sign_in(user) + visit_blob('README.md') + end + + it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do + path = project_raw_path(project, 'master/README.md') + raw_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}" + download_uri = "https://cdn.gitlab.com#{path}?inline=false&token=#{user.static_object_token}" + + aggregate_failures do + expect(page).to have_link 'Open raw', href: raw_uri + expect(page).to have_link 'Download', href: download_uri + end end end end - context 'when refactor_blob_viewer is disabled' do - before do - stub_feature_flags(refactor_blob_viewer: false) - end + context 'files with auxiliary viewers' do + # This context is the same as the other 'files with auxiliary viewers' in this file, we just ensure that the auxiliary viewers still work this the refactor_blob_viewer disabled + # It should be safe to remove once we rollout the refactored blob viewer describe '.gitlab-ci.yml' do before do @@ -1554,104 +1657,4 @@ RSpec.describe 'File blob', :js do end end end - - context 'realtime pipelines' do - before do - Files::CreateService.new( - project, - project.creator, - start_branch: 'feature', - branch_name: 'feature', - commit_message: "Add ruby file", - file_path: 'files/ruby/test.rb', - file_content: "# Awesome content" - ).execute - - create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) - visit_blob('files/ruby/test.rb', ref: 'feature') - end - - it 'shows the realtime pipeline status' do - page.within('.commit-actions') do - expect(page).to have_css('.ci-status-icon') - expect(page).to have_css('.ci-status-icon-running') - expect(page).to have_css('.js-ci-status-icon-running') - end - end - end - - context 'for subgroups' do - let(:group) { create(:group) } - let(:subgroup) { create(:group, parent: group) } - let(:project) { create(:project, :public, :repository, group: subgroup) } - - it 'renders tree table without errors' do - visit_blob('README.md') - - expect(page).to have_selector('.file-content') - expect(page).not_to have_selector('.flash-alert') - end - - it 'displays a GPG badge' do - visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') - - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end - end - - context 'on signed merge commit' do - it 'displays a GPG badge' do - visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') - - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end - end - - context 'when static objects external storage is enabled' do - before do - stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') - end - - context 'private project' do - let_it_be(:project) { create(:project, :repository, :private) } - let_it_be(:user) { create(:user) } - - before do - project.add_developer(user) - - sign_in(user) - visit_blob('README.md') - end - - it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}?token=#{user.static_object_token}" - download_uri = "https://cdn.gitlab.com#{path}?inline=false&token=#{user.static_object_token}" - - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end - end - - context 'public project' do - before do - visit_blob('README.md') - end - - it 'shows open raw and download buttons with external storage URL prepended to their href' do - path = project_raw_path(project, 'master/README.md') - raw_uri = "https://cdn.gitlab.com#{path}" - download_uri = "https://cdn.gitlab.com#{path}?inline=false" - - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end - end - end end diff --git a/spec/frontend/__helpers__/matchers/to_match_interpolated_text.js b/spec/frontend/__helpers__/matchers/to_match_interpolated_text.js index 4ce814a01b4..41e69bffd88 100644 --- a/spec/frontend/__helpers__/matchers/to_match_interpolated_text.js +++ b/spec/frontend/__helpers__/matchers/to_match_interpolated_text.js @@ -1,4 +1,5 @@ -export const toMatchInterpolatedText = (received, match) => { +// Custom matchers are object methods and should be traditional functions to be able to access `utils` on `this` +export function toMatchInterpolatedText(received, match) { let clearReceived; let clearMatch; @@ -15,16 +16,14 @@ export const toMatchInterpolatedText = (received, match) => { const pass = clearReceived === clearMatch; const message = pass ? () => ` - \n\n - Expected: ${this.utils.printExpected(clearReceived)} - To not equal: ${this.utils.printReceived(clearMatch)} + Expected to not be: ${this.utils.printExpected(clearMatch)} + Received: ${this.utils.printReceived(clearReceived)} ` : () => ` - \n\n - Expected: ${this.utils.printExpected(clearReceived)} - To equal: ${this.utils.printReceived(clearMatch)} + Expected to be: ${this.utils.printExpected(clearMatch)} + Received: ${this.utils.printReceived(clearReceived)} `; return { actual: received, message, pass }; -}; +} diff --git a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap index 22bec77276b..b3d93906445 100644 --- a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap +++ b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap @@ -13,6 +13,7 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = ` <blob-filepath-stub blob="[object Object]" + showpath="true" /> </div> diff --git a/spec/frontend/environments/environment_pin_spec.js b/spec/frontend/environments/environment_pin_spec.js index a9a58071e12..669c974ea4f 100644 --- a/spec/frontend/environments/environment_pin_spec.js +++ b/spec/frontend/environments/environment_pin_spec.js @@ -1,5 +1,9 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { GlDropdownItem } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; +import cancelAutoStopMutation from '~/environments/graphql/mutations/cancel_auto_stop.mutation.graphql'; +import createMockApollo from 'helpers/mock_apollo_helper'; import PinComponent from '~/environments/components/environment_pin.vue'; import eventHub from '~/environments/event_hub'; @@ -18,28 +22,66 @@ describe('Pin Component', () => { const autoStopUrl = '/root/auto-stop-env-test/-/environments/38/cancel_auto_stop'; - beforeEach(() => { - factory({ - propsData: { - autoStopUrl, - }, + describe('without graphql', () => { + beforeEach(() => { + factory({ + propsData: { + autoStopUrl, + }, + }); }); - }); - afterEach(() => { - wrapper.destroy(); - }); + afterEach(() => { + wrapper.destroy(); + }); - it('should render the component with descriptive text', () => { - expect(wrapper.text()).toBe('Prevent auto-stopping'); + it('should render the component with descriptive text', () => { + expect(wrapper.text()).toBe('Prevent auto-stopping'); + }); + + it('should emit onPinClick when clicked', () => { + const eventHubSpy = jest.spyOn(eventHub, '$emit'); + const item = wrapper.find(GlDropdownItem); + + item.vm.$emit('click'); + + expect(eventHubSpy).toHaveBeenCalledWith('cancelAutoStop', autoStopUrl); + }); }); - it('should emit onPinClick when clicked', () => { - const eventHubSpy = jest.spyOn(eventHub, '$emit'); - const item = wrapper.find(GlDropdownItem); + describe('with graphql', () => { + Vue.use(VueApollo); + let mockApollo; - item.vm.$emit('click'); + beforeEach(() => { + mockApollo = createMockApollo(); + factory({ + propsData: { + autoStopUrl, + graphql: true, + }, + apolloProvider: mockApollo, + }); + }); - expect(eventHubSpy).toHaveBeenCalledWith('cancelAutoStop', autoStopUrl); + afterEach(() => { + wrapper.destroy(); + }); + + it('should render the component with descriptive text', () => { + expect(wrapper.text()).toBe('Prevent auto-stopping'); + }); + + it('should emit onPinClick when clicked', () => { + jest.spyOn(mockApollo.defaultClient, 'mutate'); + const item = wrapper.find(GlDropdownItem); + + item.vm.$emit('click'); + + expect(mockApollo.defaultClient.mutate).toHaveBeenCalledWith({ + mutation: cancelAutoStopMutation, + variables: { autoStopUrl }, + }); + }); }); }); diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js index 6b53dc24f0f..21d7e09bad5 100644 --- a/spec/frontend/environments/graphql/resolvers_spec.js +++ b/spec/frontend/environments/graphql/resolvers_spec.js @@ -173,9 +173,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { it('should post to the auto stop path', async () => { mock.onPost(ENDPOINT).reply(200); - await mockResolvers.Mutation.cancelAutoStop(null, { - environment: { autoStopPath: ENDPOINT }, - }); + await mockResolvers.Mutation.cancelAutoStop(null, { autoStopUrl: ENDPOINT }); expect(mock.history.post).toContainEqual( expect.objectContaining({ url: ENDPOINT, method: 'post' }), diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js index de36b701a16..0d5d216ba02 100644 --- a/spec/frontend/environments/new_environment_item_spec.js +++ b/spec/frontend/environments/new_environment_item_spec.js @@ -191,9 +191,9 @@ describe('~/environments/components/new_environment_item.vue', () => { }); it('shows the option to pin the environment if there is an autostop date', () => { - const rollback = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); + const pin = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); - expect(rollback.exists()).toBe(true); + expect(pin.exists()).toBe(true); }); it('shows when the environment auto stops', () => { @@ -211,9 +211,9 @@ describe('~/environments/components/new_environment_item.vue', () => { it('does not show the option to pin the environment if there is no autostop date', () => { wrapper = createWrapper({ apolloProvider: createApolloProvider() }); - const rollback = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); + const pin = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); - expect(rollback.exists()).toBe(false); + expect(pin.exists()).toBe(false); }); it('does not show when the environment auto stops', () => { @@ -246,9 +246,9 @@ describe('~/environments/components/new_environment_item.vue', () => { it('does not show the option to pin the environment if there is no autostop date', () => { wrapper = createWrapper({ apolloProvider: createApolloProvider() }); - const rollback = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); + const pin = wrapper.findByRole('menuitem', { name: __('Prevent auto-stopping') }); - expect(rollback.exists()).toBe(false); + expect(pin.exists()).toBe(false); }); it('does not show when the environment auto stops', () => { diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 70808a3c7b1..eb9b443c694 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -236,7 +236,7 @@ describe('Blob content viewer component', () => { await waitForPromises(); - expect(loadViewer).toHaveBeenCalledWith(viewer); + expect(loadViewer).toHaveBeenCalledWith(viewer, false); expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true); }, ); diff --git a/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js new file mode 100644 index 00000000000..59cca23ebfc --- /dev/null +++ b/spec/frontend/repository/components/blob_viewers/lfs_viewer_spec.js @@ -0,0 +1,41 @@ +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import LfsViewer from '~/repository/components/blob_viewers/lfs_viewer.vue'; + +describe('LFS Viewer', () => { + let wrapper; + + const DEFAULT_PROPS = { + fileName: 'file_name.js', + filePath: '/some/file/path', + }; + + const createComponent = () => { + wrapper = shallowMount(LfsViewer, { + propsData: { ...DEFAULT_PROPS }, + stubs: { GlSprintf }, + }); + }; + + const findLink = () => wrapper.findComponent(GlLink); + + beforeEach(() => createComponent()); + + afterEach(() => wrapper.destroy()); + + it('renders the correct text', () => { + expect(wrapper.text()).toBe( + 'This content could not be displayed because it is stored in LFS. You can download it instead.', + ); + }); + + it('renders download link', () => { + const { filePath, fileName } = DEFAULT_PROPS; + + expect(findLink().attributes()).toMatchObject({ + target: '_blank', + href: filePath, + download: fileName, + }); + }); +}); diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index b8ab5cfcef1..2df89965fbe 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -17,6 +17,7 @@ export const simpleViewerMock = { canCurrentUserPushToBranch: true, archived: false, storedExternally: false, + externalStorageUrl: '', externalStorage: 'lfs', rawPath: 'some_file.js', replacePath: 'some_file.js/replace', diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index cc443afee6e..1d78778c757 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -345,6 +345,14 @@ RSpec.describe ProjectsHelper do expect(link).not_to include(user.name) end end + + context 'when user is nil' do + it 'returns "(deleted)"' do + link = helper.link_to_member(project, nil) + + expect(link).to eq("(deleted)") + end + end end describe 'default_clone_protocol' do diff --git a/spec/lib/gitlab/rack_attack/request_spec.rb b/spec/lib/gitlab/rack_attack/request_spec.rb index ecdcc23e588..37db588ce16 100644 --- a/spec/lib/gitlab/rack_attack/request_spec.rb +++ b/spec/lib/gitlab/rack_attack/request_spec.rb @@ -5,6 +5,20 @@ require 'spec_helper' RSpec.describe Gitlab::RackAttack::Request do using RSpec::Parameterized::TableSyntax + let(:path) { '/' } + let(:env) { {} } + let(:session) { {} } + let(:request) do + ::Rack::Attack::Request.new( + env.reverse_merge( + 'REQUEST_METHOD' => 'GET', + 'PATH_INFO' => path, + 'rack.input' => StringIO.new, + 'rack.session' => session + ) + ) + end + describe 'FILES_PATH_REGEX' do subject { described_class::FILES_PATH_REGEX } @@ -16,11 +30,172 @@ RSpec.describe Gitlab::RackAttack::Request do it { is_expected.not_to match('/api/v4/projects/some/nested/repo/repository/files/README') } end + describe '#api_request?' do + subject { request.api_request? } + + where(:path, :expected) do + '/' | false + '/groups' | false + '/foo/api' | false + + '/api' | true + '/api/v4/groups/1' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#api_internal_request?' do + subject { request.api_internal_request? } + + where(:path, :expected) do + '/' | false + '/groups' | false + '/api' | false + '/api/v4/groups/1' | false + '/api/v4/internal' | false + '/foo/api/v4/internal' | false + + '/api/v4/internal/' | true + '/api/v4/internal/foo' | true + '/api/v1/internal/foo' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#health_check_request?' do + subject { request.health_check_request? } + + where(:path, :expected) do + '/' | false + '/groups' | false + '/foo/-/health' | false + + '/-/health' | true + '/-/liveness' | true + '/-/readiness' | true + '/-/metrics' | true + '/-/health/foo' | true + '/-/liveness/foo' | true + '/-/readiness/foo' | true + '/-/metrics/foo' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#container_registry_event?' do + subject { request.container_registry_event? } + + where(:path, :expected) do + '/' | false + '/groups' | false + '/api/v4/container_registry_event' | false + '/foo/api/v4/container_registry_event/' | false + + '/api/v4/container_registry_event/' | true + '/api/v4/container_registry_event/foo' | true + '/api/v1/container_registry_event/foo' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#product_analytics_collector_request?' do + subject { request.product_analytics_collector_request? } + + where(:path, :expected) do + '/' | false + '/groups' | false + '/-/collector' | false + '/-/collector/foo' | false + '/foo/-/collector/i' | false + + '/-/collector/i' | true + '/-/collector/ifoo' | true + '/-/collector/i/foo' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#should_be_skipped?' do + where( + api_internal_request: [true, false], + health_check_request: [true, false], + container_registry_event: [true, false] + ) + + with_them do + it 'returns true if any condition is true' do + allow(request).to receive(:api_internal_request?).and_return(api_internal_request) + allow(request).to receive(:health_check_request?).and_return(health_check_request) + allow(request).to receive(:container_registry_event?).and_return(container_registry_event) + + expect(request.should_be_skipped?).to be(api_internal_request || health_check_request || container_registry_event) + end + end + end + + describe '#web_request?' do + subject { request.web_request? } + + where(:path, :expected) do + '/' | true + '/groups' | true + '/foo/api' | true + + '/api' | false + '/api/v4/groups/1' | false + end + + with_them do + it { is_expected.to eq(expected) } + end + end + + describe '#protected_path?' do + subject { request.protected_path? } + + before do + stub_application_setting(protected_paths: [ + '/protected', + '/secure' + ]) + end + + where(:path, :expected) do + '/' | false + '/groups' | false + '/foo/protected' | false + '/foo/secure' | false + + '/protected' | true + '/secure' | true + '/secure/' | true + '/secure/foo' | true + end + + with_them do + it { is_expected.to eq(expected) } + end + end + describe '#deprecated_api_request?' do - let(:env) { { 'REQUEST_METHOD' => 'GET', 'rack.input' => StringIO.new, 'PATH_INFO' => path, 'QUERY_STRING' => query } } - let(:request) { ::Rack::Attack::Request.new(env) } + subject { request.send(:deprecated_api_request?) } - subject { !!request.__send__(:deprecated_api_request?) } + let(:env) { { 'QUERY_STRING' => query } } where(:path, :query, :expected) do '/' | '' | false diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 725a571e385..3dc38b5bc1c 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -12,8 +12,8 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do stub_database_flavor_check('Cloud SQL for PostgreSQL') end - describe '.uncached_data' do - subject { described_class.uncached_data } + describe '.data' do + subject { described_class.data(force_refresh: true) } it 'includes basic top and second level keys' do is_expected.to include(:counts) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 70dd1cbe240..9da22e15a12 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -3524,7 +3524,7 @@ RSpec.describe MergeRequest, factory_default: :keep do let!(:job) { create(:ci_build, :with_deployment, :start_review_app, pipeline: pipeline, project: project) } it 'returns environments' do - is_expected.to eq(pipeline.environments) + is_expected.to eq(pipeline.environments_in_self_and_descendants.to_a) expect(subject.count).to be(1) end diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index 793438808a5..25eae57a134 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -184,6 +184,7 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac context 'unauthenticated requests' do let(:protected_path_that_does_not_require_authentication) do + # This is one of the default values for `application_settings.protected_paths` '/users/sign_in' end @@ -227,6 +228,20 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac expect_rejection { post protected_path_that_does_not_require_authentication, params: post_params } end + it 'allows non-POST requests to protected paths over the rate limit' do + (1 + requests_per_period).times do + get protected_path_that_does_not_require_authentication + expect(response).to have_gitlab_http_status(:ok) + end + end + + it 'allows POST requests to unprotected paths over the rate limit' do + (1 + requests_per_period).times do + post '/api/graphql' + expect(response).to have_gitlab_http_status(:ok) + end + end + it_behaves_like 'tracking when dry-run mode is set' do let(:throttle_name) { 'throttle_unauthenticated_protected_paths' } end diff --git a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_with_deduplication_spec.rb b/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_with_deduplication_spec.rb deleted file mode 100644 index 6e7212b1002..00000000000 --- a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_with_deduplication_spec.rb +++ /dev/null @@ -1,166 +0,0 @@ -# frozen_string_literal: true - -require 'fast_spec_helper' -require 'rspec-parameterized' -require_relative '../../../../rubocop/cop/sidekiq_load_balancing/worker_data_consistency_with_deduplication' - -RSpec.describe RuboCop::Cop::SidekiqLoadBalancing::WorkerDataConsistencyWithDeduplication do - using RSpec::Parameterized::TableSyntax - - subject(:cop) { described_class.new } - - before do - allow(cop) - .to receive(:in_worker?) - .and_return(true) - end - - where(:data_consistency) { %i[delayed sticky] } - - with_them do - let(:strategy) { described_class::DEFAULT_STRATEGY } - let(:corrected) do - <<~CORRECTED - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - deduplicate #{strategy}, including_scheduled: true - idempotent! - end - CORRECTED - end - - context 'when deduplication strategy is not explicitly set' do - it 'registers an offense and corrects using default strategy' do - expect_offense(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - idempotent! - ^^^^^^^^^^^ Workers that declare either `:sticky` or `:delayed` data consistency [...] - end - CODE - - expect_correction(corrected) - end - - context 'when identation is different' do - let(:corrected) do - <<~CORRECTED - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - deduplicate #{strategy}, including_scheduled: true - idempotent! - end - CORRECTED - end - - it 'registers an offense and corrects with correct identation' do - expect_offense(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - idempotent! - ^^^^^^^^^^^ Workers that declare either `:sticky` or `:delayed` data consistency [...] - end - CODE - - expect_correction(corrected) - end - end - end - - context 'when deduplication strategy does not include including_scheduling option' do - let(:strategy) { ':until_executed' } - - it 'registers an offense and corrects' do - expect_offense(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - deduplicate :until_executed - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Workers that declare either `:sticky` or `:delayed` data consistency [...] - idempotent! - end - CODE - - expect_correction(corrected) - end - end - - context 'when deduplication strategy has including_scheduling option disabled' do - let(:strategy) { ':until_executed' } - - it 'registers an offense and corrects' do - expect_offense(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :#{data_consistency} - - deduplicate :until_executed, including_scheduled: false - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Workers that declare either `:sticky` or `:delayed` data consistency [...] - idempotent! - end - CODE - - expect_correction(corrected) - end - end - - context "when deduplication strategy is :none" do - it 'does not register an offense' do - expect_no_offenses(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :always - - deduplicate :none - idempotent! - end - CODE - end - end - - context "when deduplication strategy has including_scheduling option enabled" do - it 'does not register an offense' do - expect_no_offenses(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :always - - deduplicate :until_executing, including_scheduled: true - idempotent! - end - CODE - end - end - end - - context "data_consistency: :always" do - it 'does not register an offense' do - expect_no_offenses(<<~CODE) - class SomeWorker - include ApplicationWorker - - data_consistency :always - - idempotent! - end - CODE - end - end -end diff --git a/spec/serializers/test_case_entity_spec.rb b/spec/serializers/test_case_entity_spec.rb index cdeefd2fec5..03fb88bc6c3 100644 --- a/spec/serializers/test_case_entity_spec.rb +++ b/spec/serializers/test_case_entity_spec.rb @@ -41,6 +41,18 @@ RSpec.describe TestCaseEntity do end end + context 'when no test name is entered' do + let(:test_case) { build(:report_test_case, name: "") } + + it 'contains correct test case details' do + expect(subject[:status]).to eq('success') + expect(subject[:name]).to eq('(No name)') + expect(subject[:classname]).to eq('trace') + expect(subject[:file]).to eq('spec/trace_spec.rb') + expect(subject[:execution_time]).to eq(1.23) + end + end + context 'when attachment is present' do let(:test_case) { build(:report_test_case, :failed_with_attachment, job: job) } diff --git a/spec/services/environments/stop_service_spec.rb b/spec/services/environments/stop_service_spec.rb index acc9869002f..362071c1c26 100644 --- a/spec/services/environments/stop_service_spec.rb +++ b/spec/services/environments/stop_service_spec.rb @@ -185,7 +185,7 @@ RSpec.describe Environments::StopService do end it 'has active environment at first' do - expect(pipeline.environments.first).to be_available + expect(pipeline.environments_in_self_and_descendants.first).to be_available end context 'when user is a developer' do @@ -196,7 +196,7 @@ RSpec.describe Environments::StopService do it 'stops the active environment' do subject - expect(pipeline.environments.first).to be_stopped + expect(pipeline.environments_in_self_and_descendants.first).to be_stopped end end @@ -208,7 +208,7 @@ RSpec.describe Environments::StopService do it 'does not stop the active environment' do subject - expect(pipeline.environments.first).to be_available + expect(pipeline.environments_in_self_and_descendants.first).to be_available end end @@ -232,7 +232,7 @@ RSpec.describe Environments::StopService do it 'does not stop the active environment' do subject - expect(pipeline.environments.first).to be_available + expect(pipeline.environments_in_self_and_descendants.first).to be_available end end end |