diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 15:09:14 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 15:09:14 +0000 |
commit | a88c31d0ea1a79ca93fad357c3eb536b5e013e44 (patch) | |
tree | b38023b2be7478fa706c7a95d08cdfad13f8d8c1 /spec/features/projects/blobs | |
parent | affec3ced2d85697d9a21d83e811285b030705f2 (diff) | |
download | gitlab-ce-a88c31d0ea1a79ca93fad357c3eb536b5e013e44.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/features/projects/blobs')
-rw-r--r-- | spec/features/projects/blobs/blob_show_spec.rb | 1539 |
1 files changed, 751 insertions, 788 deletions
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 76baad63cc2..e222f0d34a9 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -29,176 +29,162 @@ RSpec.describe 'File blob', :js do ).execute end - context 'with refactor_blob_viewer feature flag enabled' do - context 'Ruby file' do + 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 before do - visit_blob('files/ruby/popen.rb') + visit_blob('files/markdown/ruby-style-guide.md') wait_for_requests end - it 'displays the blob' do + it 'displays the blob using the rich viewer' do aggregate_failures do - # shows highlighted Ruby code - expect(page).to have_css(".js-syntax-highlight") - expect(page).to have_content("require 'fileutils'") + # 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"]') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + # 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 - 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 'switching to the simple viewer' do before do - visit_blob('files/markdown/ruby-style-guide.md') + 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).not_to have_selector('.blob-viewer[data-type="simple"]') - expect(page).to have_selector('.blob-viewer[data-type="rich"]') - - # shows rendered Markdown - expect(page).to have_link("PEP-8") - - # shows a viewer switcher - expect(page).to have_selector('.js-blob-viewer-switcher') + # 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 a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') + # 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 a raw button - expect(page).to have_link('Open raw') + # shows an enabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') end end - context 'switching to the simple viewer' do + context 'switching to the rich viewer again' do before do - find('.js-blob-viewer-switch-btn[data-viewer=simple]').click + find('.js-blob-viewer-switch-btn[data-viewer=rich]').click 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).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/)") + # 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 an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') - end - end - - context 'switching to the rich viewer again' do - before do - find('.js-blob-viewer-switch-btn[data-viewer=rich]').click - - wait_for_requests - end - - 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"]') - - # shows a disabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn.disabled') - end + # shows a disabled copy button + expect(page).to have_selector('.js-copy-blob-source-btn.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 + 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 + page.within '.project-refs-form' do + click_link ref_name + wait_for_requests 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') - end + page.within '.blob-content' do + expect(page).not_to have_css('.hll') end + 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') - - aggregate_failures do - expect(page.find('.file-title-name').text).to eq('application.js') - expect(page).not_to have_css('flash-container') - end - 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 shorter to longer ref name' do - visit_blob('files/js/application.js', ref: 'dev') - switch_ref_to('development') + 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') - end + aggregate_failures do + expect(page.find('.file-title-name').text).to eq('application.js') + expect(page).not_to have_css('flash-container') end end - 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') + 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') @@ -206,133 +192,216 @@ RSpec.describe 'File blob', :js do end end end + + 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') + + 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 '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 'Markdown rendering' do + context 'when rendering default markdown' do before do - project.add_maintainer(project.creator) + visit_blob('files/commonmark/file.md') - 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 + wait_for_requests end - context 'when rendering default markdown' do - before do - visit_blob('files/commonmark/file.md') - - wait_for_requests + 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 - it 'renders using CommonMark' do - aggregate_failures do - expect(page).to have_content("sublist") - expect(page).not_to have_xpath("//ol//li//ul") - 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) + + visit_blob('files/lfs/file.md') + + wait_for_requests + end + + 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('This content 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 a download button + expect(page).to have_link('Download') end end end - context 'Markdown file (stored in LFS)' do + context 'when LFS is disabled on the project' do before do - project.add_maintainer(project.creator) + visit_blob('files/lfs/file.md') - 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 + wait_for_requests 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) + it 'displays the blob' do + aggregate_failures do + # shows text + expect(page).to have_content('size 1575078') - visit_blob('files/lfs/file.md') + # does not show a viewer switcher + expect(page).not_to have_selector('.js-blob-viewer-switcher') - wait_for_requests + # 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 - 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"]') + context 'PDF file' do + before do + project.add_maintainer(project.creator) - # 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.') + 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 - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + visit_blob('files/test.pdf') - # does not show a copy button - expect(page).not_to have_selector('.js-copy-blob-source-btn') + wait_for_requests + end - # shows a download button - expect(page).to have_link('Download') - end - end + it 'displays the blob' do + aggregate_failures do + # 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') + + # 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 - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/file.md') + context 'Jupiter Notebook file' do + before do + project.add_maintainer(project.creator) - wait_for_requests - end + 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 - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + visit_blob('files/basic.ipynb') - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + wait_for_requests + end - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') + it 'displays the blob' do + aggregate_failures do + # shows rendered notebook + expect(page).to have_selector('.js-notebook-viewer-mounted') - # shows a raw button - expect(page).to have_link('Open raw') - end - end + # 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') + + # shows a raw button + expect(page).to have_link('Open raw') + + # shows a download button + expect(page).to have_link('Download') + + # shows the rendered notebook + expect(page).to have_content('test') end end + end - context 'PDF file' do + context 'ISO file (stored in LFS)' do + context 'when LFS is enabled on the project' do before do - project.add_maintainer(project.creator) + 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 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') + visit_blob('files/lfs/lfs_object.iso') wait_for_requests end it 'displays the blob' do aggregate_failures do - # shows rendered PDF - expect(page).to have_selector('.js-pdf-viewer') + # 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') @@ -346,126 +415,122 @@ RSpec.describe 'File blob', :js do end end - context 'Jupiter Notebook file' do + context 'when LFS is disabled on the project' do before do - project.add_maintainer(project.creator) - - 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/basic.ipynb') + visit_blob('files/lfs/lfs_object.iso') wait_for_requests end it 'displays the blob' do aggregate_failures do - # shows rendered notebook - expect(page).to have_selector('.js-notebook-viewer-mounted') + # shows text + expect(page).to have_content('size 1575078') - # does show 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') - # show 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') - - # 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 '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) - - visit_blob('files/lfs/lfs_object.iso') + context 'ZIP file' do + before do + visit_blob('Gemfile.zip') - 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)') + it 'displays the blob' do + aggregate_failures do + # shows a download link + 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') + # 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') - end - end + # shows a download button + expect(page).to have_link('Download') end + end + end - context 'when LFS is disabled on the project' do - before do - visit_blob('files/lfs/lfs_object.iso') + context 'empty file' do + before do + project.add_maintainer(project.creator) - wait_for_requests - end + 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 - it 'displays the blob' do - aggregate_failures do - # shows text - expect(page).to have_content('size 1575078') + visit_blob('files/empty.md') + + wait_for_requests + end - # does not show a viewer switcher - expect(page).not_to have_selector('.js-blob-viewer-switcher') + it 'displays an error' do + aggregate_failures do + # shows an error message + expect(page).to have_content('Empty file') - # shows an enabled copy button - expect(page).to have_selector('.js-copy-blob-source-btn:not(.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') - end - end + # does not show a copy button + expect(page).not_to have_selector('.js-copy-blob-source-btn') + + # 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 'ZIP file' do + context 'files with auxiliary viewers' do + describe '.gitlab-ci.yml' do before do - visit_blob('Gemfile.zip') + project.add_maintainer(project.creator) - wait_for_requests + 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 + + visit_blob('.gitlab-ci.yml') end - it 'displays the blob' do + it 'displays an auxiliary viewer' do aggregate_failures do - # shows a download link - 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') + # shows that configuration is valid + expect(page).to have_content('This GitLab CI configuration is valid.') - # 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 learn more link + expect(page).to have_link('Learn more') end end end - context 'empty file' do + describe '.gitlab/route-map.yml' do before do project.add_maintainer(project.creator) @@ -474,660 +539,558 @@ RSpec.describe 'File blob', :js do project.creator, start_branch: 'master', branch_name: 'master', - commit_message: "Add empty file", - file_path: 'files/empty.md', - file_content: '' + 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 - visit_blob('files/empty.md') - - wait_for_requests + visit_blob('.gitlab/route-map.yml') end - it 'displays an error' do + it 'displays an auxiliary viewer' do aggregate_failures do - # shows an error message - expect(page).to have_content('Empty file') + # shows that map is valid + expect(page).to have_content('This Route Map is valid.') - # 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 download or raw button - expect(page).not_to have_link('Download') - expect(page).not_to have_link('Open raw') + # shows a learn more link + expect(page).to have_link('Learn more') end end end - context 'files with auxiliary viewers' do - describe '.gitlab-ci.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-ci.yml", - file_path: '.gitlab-ci.yml', - file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - ).execute - - visit_blob('.gitlab-ci.yml') - end - - 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 learn more link - expect(page).to have_link('Learn more') - end - end - end - - 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 - - visit_blob('.gitlab/route-map.yml') - 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.') + describe '.gitlab/dashboards/custom-dashboard.yml' do + before do + project.add_maintainer(project.creator) - # shows a learn more link - expect(page).to have_link('Learn more') - end - end + 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 - describe '.gitlab/dashboards/custom-dashboard.yml' do + context 'with metrics_dashboard_exhaustive_validations feature flag off' do before do - project.add_maintainer(project.creator) - - 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 + stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + visit_blob('.gitlab/dashboards/custom-dashboard.yml') end - 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 - - context 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + 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 - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + 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.') - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - 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") + 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 a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end 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 '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 'valid dashboard file' do - let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + 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 - # shows that dashboard yaml is valid - expect(page).to have_content('Metrics Dashboard YAML definition is valid.') + 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.') - # shows a learn more link - expect(page).to have_link('Learn more') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end + end - context 'invalid dashboard file' do - let(:file_content) { "dashboard: 'invalid'" } + context 'invalid dashboard file' do + let(:file_content) { "dashboard: 'invalid'" } - 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") + 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') - end + # shows a learn more link + expect(page).to have_link('Learn more') end end end end + end - context 'LICENSE' do - before do - visit_blob('LICENSE') - end + context 'LICENSE' do + before do + visit_blob('LICENSE') + end - it 'displays an auxiliary viewer' do - aggregate_failures do - # shows license - expect(page).to have_content('This project is licensed under the MIT License.') + it 'displays an auxiliary viewer' do + aggregate_failures do + # shows license + expect(page).to have_content('This project is licensed under the MIT License.') - # shows a learn more link - expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') - end + # shows a learn more link + expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') end end + end - context '*.gemspec' do - before do - project.add_maintainer(project.creator) - - 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 + context '*.gemspec' 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 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 - # shows a learn more link - expect(page).to have_link('Learn more', href: 'https://rubygems.org/') - end - end + visit_blob('activerecord.gemspec') end - context 'CONTRIBUTING.md' do - before do - file_name = 'CONTRIBUTING.md' + 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.') - create_file(file_name, '## Contribution guidelines') - visit_blob(file_name) + # shows a learn more link + expect(page).to have_link('Learn more', href: 'https://rubygems.org/') end + end + 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.") - end + context 'CONTRIBUTING.md' do + before do + file_name = 'CONTRIBUTING.md' + + create_file(file_name, '## Contribution guidelines') + 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.") end end + end - context 'CHANGELOG.md' do - before do - file_name = 'CHANGELOG.md' + context 'CHANGELOG.md' do + before do + file_name = 'CHANGELOG.md' - create_file(file_name, '## Changelog for v1.0.0') - visit_blob(file_name) - 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 + 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 'Cargo.toml' do - before do - file_name = 'Cargo.toml' - - 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 + context 'Cargo.toml' do + before do + file_name = 'Cargo.toml' + + 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 - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using Cargo.") - 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 'Cartfile' do - before do - file_name = 'Cartfile' + context 'Cartfile' do + before do + file_name = 'Cartfile' - create_file(file_name, ' - gitlab "Alamofire/Alamofire" == 4.9.0 - gitlab "Alamofire/AlamofireImage" ~> 3.4 - ') - visit_blob(file_name) - end + 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 + 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 'composer.json' do - before do - file_name = 'composer.json' - - create_file(file_name, ' - { - "license": "MIT" - } - ') - visit_blob(file_name) - end + context 'composer.json' do + before do + file_name = 'composer.json' + + create_file(file_name, ' + { + "license": "MIT" + } + ') + 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 Composer.") - 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 'Gemfile' do - before do - file_name = 'Gemfile' + context 'Gemfile' do + before do + file_name = 'Gemfile' - create_file(file_name, ' - source "https://rubygems.org" + create_file(file_name, ' + source "https://rubygems.org" - # Gems here - ') - visit_blob(file_name) - 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 + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Bundler.") end end + end - context 'Godeps.json' do - before do - file_name = 'Godeps.json' - - create_file(file_name, ' - { - "GoVersion": "go1.6" - } - ') - visit_blob(file_name) - end + context 'Godeps.json' do + before do + file_name = 'Godeps.json' + + 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 + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using godep.") end end + end - context 'go.mod' do - before do - file_name = 'go.mod' + context 'go.mod' do + before do + file_name = 'go.mod' - create_file(file_name, ' - module example.com/mymodule + create_file(file_name, ' + module example.com/mymodule - go 1.14 - ') - visit_blob(file_name) - end + go 1.14 + ') + 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 Go Modules.") - end + 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 - context 'package.json' do - before do - file_name = 'package.json' - - create_file(file_name, ' - { - "name": "my-awesome-package", - "version": "1.0.0" - } - ') - visit_blob(file_name) - end + context 'package.json' do + before do + file_name = 'package.json' + + 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 + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using npm.") end end + end - context 'podfile' do - before do - file_name = 'podfile' + context 'podfile' do + before do + file_name = 'podfile' - create_file(file_name, 'platform :ios, "8.0"') - 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("This project manages its dependencies using CocoaPods.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'test.podspec' do - before do - file_name = 'test.podspec' - - create_file(file_name, ' - Pod::Spec.new do |s| - s.name = "TensorFlowLiteC" - ') - visit_blob(file_name) - end + context 'test.podspec' do + before do + file_name = 'test.podspec' - it 'displays an auxiliary viewer' do - aggregate_failures do - expect(page).to have_content("This project manages its dependencies using CocoaPods.") - end - end + create_file(file_name, ' + Pod::Spec.new do |s| + s.name = "TensorFlowLiteC" + ') + visit_blob(file_name) end - context 'JSON.podspec.json' do - before do - file_name = 'JSON.podspec.json' - - 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 CocoaPods.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'requirements.txt' do - before do - file_name = 'requirements.txt' - - create_file(file_name, 'Project requirements') - visit_blob(file_name) - end + context 'JSON.podspec.json' do + before do + file_name = 'JSON.podspec.json' + + 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 pip.") - end + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using CocoaPods.") end end + end - context 'yarn.lock' do - before do - file_name = 'yarn.lock' + context 'requirements.txt' do + before do + file_name = 'requirements.txt' - create_file(file_name, ' - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 - ') - visit_blob(file_name) - end + 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 Yarn.") - 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 - context 'openapi.yml' do - before do - file_name = 'openapi.yml' - - create_file(file_name, ' - swagger: \'2.0\' - info: - title: Classic API Resource Documentation - description: | - <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> - <h1>Swagger API documentation</h1> - </div> - version: production - basePath: /JSSResource/ - produces: - - application/xml - - application/json - consumes: - - application/xml - - application/json - security: - - basicAuth: [] - paths: - /accounts: - get: - responses: - \'200\': - description: No response was specified - tags: - - accounts - operationId: findAccounts - summary: Finds all accounts - ') - visit_blob(file_name, useUnsafeMarkdown: '1') - click_button('Display rendered file') + context 'yarn.lock' do + before do + file_name = 'yarn.lock' - wait_for_requests - end + create_file(file_name, ' + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + ') + visit_blob(file_name) + end - it 'removes `style`, `class`, and `data-*`` attributes from HTML' do - expect(page).to have_css('h1', text: 'Swagger API documentation') - expect(page).not_to have_css('.foo-bar') - expect(page).not_to have_css('[style="background-color: red;"]') - expect(page).not_to have_css('[data-foo-bar="baz"]') + it 'displays an auxiliary viewer' do + aggregate_failures do + expect(page).to have_content("This project manages its dependencies using Yarn.") end end end - context 'realtime pipelines' do + context 'openapi.yml' 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 + file_name = 'openapi.yml' + + create_file(file_name, ' + swagger: \'2.0\' + info: + title: Classic API Resource Documentation + description: | + <div class="foo-bar" style="background-color: red;" data-foo-bar="baz"> + <h1>Swagger API documentation</h1> + </div> + version: production + basePath: /JSSResource/ + produces: + - application/xml + - application/json + consumes: + - application/xml + - application/json + security: + - basicAuth: [] + paths: + /accounts: + get: + responses: + \'200\': + description: No response was specified + tags: + - accounts + operationId: findAccounts + summary: Finds all accounts + ') + visit_blob(file_name, useUnsafeMarkdown: '1') + click_button('Display rendered file') - create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha) - visit_blob('files/ruby/test.rb', ref: 'feature') + wait_for_requests 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 + it 'removes `style`, `class`, and `data-*`` attributes from HTML' do + expect(page).to have_css('h1', text: 'Swagger API documentation') + expect(page).not_to have_css('.foo-bar') + expect(page).not_to have_css('[style="background-color: red;"]') + expect(page).not_to have_css('[data-foo-bar="baz"]') 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') + 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 - expect(page).to have_selector('.file-content') - expect(page).not_to have_selector('[data-testid="alert-danger"]') + 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 - it 'displays a GPG badge' do - visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9') + context 'for subgroups' do + let(:group) { create(:group) } + let(:subgroup) { create(:group, parent: group) } + let(:project) { create(:project, :public, :repository, group: subgroup) } - expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' - expect(page).to have_selector '.gpg-status-box.invalid' - end + it 'renders tree table without errors' do + visit_blob('README.md') + + expect(page).to have_selector('.file-content') + expect(page).not_to have_selector('[data-testid="alert-danger"]') end - context 'on signed merge commit' do - it 'displays a GPG badge' do - visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') + 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 + 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 'on signed merge commit' do + it 'displays a GPG badge' do + visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10') - context 'private project' do - let_it_be(:project) { create(:project, :repository, :private) } - let_it_be(:user) { create(:user, static_object_token: 'ABCD1234') } + expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge' + expect(page).to have_selector '.gpg-status-box.invalid' + end + end - before do - project.add_developer(user) + context 'when static objects external storage is enabled' do + before do + stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com') + end - sign_in(user) - visit_blob('README.md') - end + context 'private project' do + let_it_be(:project) { create(:project, :repository, :private) } + let_it_be(:user) { create(:user, static_object_token: 'ABCD1234') } - 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}?token=#{user.static_object_token}&inline=false" + before do + project.add_developer(user) - aggregate_failures do - expect(page).to have_link 'Open raw', href: raw_uri - expect(page).to have_link 'Download', href: download_uri - end - end + sign_in(user) + visit_blob('README.md') 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" + 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}?token=#{user.static_object_token}&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 + 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 'with refactor_blob_viewer feature flag disabled' do - before do - stub_feature_flags(refactor_blob_viewer: false) - end - - 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 + context 'public project' do before do - visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') + visit_blob('README.md') end - it 'displays the blob' do + + 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 - # 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') + expect(page).to have_link 'Open raw', href: raw_uri + expect(page).to have_link 'Download', href: download_uri end end end |