Commit 9e1369c1 authored by Jacques Erasmus's avatar Jacques Erasmus Committed by Matthias Käppler

Fix blob show specs

Fixed the blob show specs for the blob refactor
parent 48249300
...@@ -42,6 +42,11 @@ export default { ...@@ -42,6 +42,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
showPath: {
type: Boolean,
required: false,
default: true,
},
}, },
data() { data() {
return { return {
...@@ -55,6 +60,9 @@ export default { ...@@ -55,6 +60,9 @@ export default {
showDefaultActions() { showDefaultActions() {
return !this.hideDefaultActions; return !this.hideDefaultActions;
}, },
isEmpty() {
return this.blob.rawSize === 0;
},
}, },
watch: { watch: {
viewer(newVal, oldVal) { viewer(newVal, oldVal) {
...@@ -74,7 +82,7 @@ export default { ...@@ -74,7 +82,7 @@ export default {
<div class="js-file-title file-title-flex-parent"> <div class="js-file-title file-title-flex-parent">
<div class="gl-display-flex"> <div class="gl-display-flex">
<table-of-contents class="gl-pr-2" /> <table-of-contents class="gl-pr-2" />
<blob-filepath :blob="blob"> <blob-filepath :blob="blob" :show-path="showPath">
<template #filepath-prepend> <template #filepath-prepend>
<slot name="prepend"></slot> <slot name="prepend"></slot>
</template> </template>
...@@ -88,12 +96,13 @@ export default { ...@@ -88,12 +96,13 @@ export default {
<default-actions <default-actions
v-if="showDefaultActions" v-if="showDefaultActions"
:raw-path="blob.rawPath" :raw-path="blob.externalStorageUrl || blob.rawPath"
:active-viewer="viewer" :active-viewer="viewer"
:has-render-error="hasRenderError" :has-render-error="hasRenderError"
:is-binary="isBinary" :is-binary="isBinary"
:environment-name="blob.environmentFormattedExternalUrl" :environment-name="blob.environmentFormattedExternalUrl"
:environment-path="blob.environmentExternalUrlForRouteMap" :environment-path="blob.environmentExternalUrlForRouteMap"
:is-empty="isEmpty"
@copy="proxyCopyRequest" @copy="proxyCopyRequest"
/> />
</div> </div>
......
...@@ -48,6 +48,11 @@ export default { ...@@ -48,6 +48,11 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
isEmpty: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
downloadUrl() { downloadUrl() {
...@@ -87,6 +92,7 @@ export default { ...@@ -87,6 +92,7 @@ export default {
icon="copy-to-clipboard" icon="copy-to-clipboard"
category="primary" category="primary"
variant="default" variant="default"
class="js-copy-blob-source-btn"
/> />
<gl-button <gl-button
v-if="!isBinary" v-if="!isBinary"
...@@ -100,6 +106,7 @@ export default { ...@@ -100,6 +106,7 @@ export default {
variant="default" variant="default"
/> />
<gl-button <gl-button
v-if="!isEmpty"
v-gl-tooltip.hover v-gl-tooltip.hover
:aria-label="$options.BTN_DOWNLOAD_TITLE" :aria-label="$options.BTN_DOWNLOAD_TITLE"
:title="$options.BTN_DOWNLOAD_TITLE" :title="$options.BTN_DOWNLOAD_TITLE"
......
...@@ -15,6 +15,11 @@ export default { ...@@ -15,6 +15,11 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
showPath: {
type: Boolean,
required: false,
default: true,
},
}, },
computed: { computed: {
blobSize() { blobSize() {
...@@ -26,6 +31,13 @@ export default { ...@@ -26,6 +31,13 @@ export default {
showLfsBadge() { showLfsBadge() {
return this.blob.storedExternally && this.blob.externalStorage === 'lfs'; return this.blob.storedExternally && this.blob.externalStorage === 'lfs';
}, },
fileName() {
if (this.showPath) {
return this.blob.path;
}
return this.blob.name;
},
}, },
}; };
</script> </script>
...@@ -33,12 +45,12 @@ export default { ...@@ -33,12 +45,12 @@ export default {
<div class="file-header-content d-flex align-items-center lh-100"> <div class="file-header-content d-flex align-items-center lh-100">
<slot name="filepath-prepend"></slot> <slot name="filepath-prepend"></slot>
<template v-if="blob.path"> <template v-if="fileName">
<file-icon :file-name="blob.path" :size="16" aria-hidden="true" css-classes="mr-2" /> <file-icon :file-name="fileName" :size="16" aria-hidden="true" css-classes="mr-2" />
<strong <strong
class="file-title-name mr-1 js-blob-header-filepath" class="file-title-name mr-1 js-blob-header-filepath"
data-qa-selector="file_title_content" data-qa-selector="file_title_content"
>{{ blob.path }}</strong >{{ fileName }}</strong
> >
</template> </template>
......
...@@ -153,7 +153,7 @@ export default { ...@@ -153,7 +153,7 @@ export default {
}, },
blobViewer() { blobViewer() {
const { fileType } = this.viewer; const { fileType } = this.viewer;
return loadViewer(fileType); return loadViewer(fileType, this.isUsingLfs);
}, },
viewerProps() { viewerProps() {
const { fileType } = this.viewer; const { fileType } = this.viewer;
...@@ -185,6 +185,9 @@ export default { ...@@ -185,6 +185,9 @@ export default {
? this.blobInfo.ideForkAndEditPath ? this.blobInfo.ideForkAndEditPath
: this.blobInfo.forkAndEditPath; : this.blobInfo.forkAndEditPath;
}, },
isUsingLfs() {
return this.blobInfo.storedExternally && this.blobInfo.externalStorage === 'lfs';
},
}, },
methods: { methods: {
loadLegacyViewer(type) { loadLegacyViewer(type) {
...@@ -245,10 +248,11 @@ export default { ...@@ -245,10 +248,11 @@ export default {
<div v-if="blobInfo && !isLoading" class="file-holder"> <div v-if="blobInfo && !isLoading" class="file-holder">
<blob-header <blob-header
:blob="blobInfo" :blob="blobInfo"
:hide-viewer-switcher="!hasRichViewer || isBinaryFileType" :hide-viewer-switcher="!hasRichViewer || isBinaryFileType || isUsingLfs"
:is-binary="isBinaryFileType" :is-binary="isBinaryFileType"
:active-viewer-type="viewer.type" :active-viewer-type="viewer.type"
:has-render-error="hasRenderError" :has-render-error="hasRenderError"
:show-path="false"
@viewer-changed="switchViewer" @viewer-changed="switchViewer"
> >
<template #actions> <template #actions>
......
export const loadViewer = (type) => { const viewers = {
switch (type) { download: () => import('./download_viewer.vue'),
case 'empty': image: () => import('./image_viewer.vue'),
return () => import(/* webpackChunkName: 'blob_empty_viewer' */ './empty_viewer.vue'); video: () => import('./video_viewer.vue'),
case 'text': empty: () => import('./empty_viewer.vue'),
return gon.features.highlightJs text: () => import('~/vue_shared/components/source_viewer.vue'),
? () => pdf: () => import('./pdf_viewer.vue'),
import( lfs: () => import('./lfs_viewer.vue'),
/* webpackChunkName: 'blob_text_viewer' */ '~/vue_shared/components/source_viewer.vue' };
)
: null; export const loadViewer = (type, isUsingLfs) => {
case 'download': let viewer = viewers[type];
return () => import(/* webpackChunkName: 'blob_download_viewer' */ './download_viewer.vue');
case 'image': if (!viewer && isUsingLfs) {
return () => import(/* webpackChunkName: 'blob_image_viewer' */ './image_viewer.vue'); viewer = viewers.lfs;
case 'video':
return () => import(/* webpackChunkName: 'blob_video_viewer' */ './video_viewer.vue');
case 'pdf':
return () => import(/* webpackChunkName: 'blob_pdf_viewer' */ './pdf_viewer.vue');
default:
return null;
} }
return viewer;
}; };
export const viewerProps = (type, blob) => { export const viewerProps = (type, blob) => {
return { const props = {
text: { text: {
content: blob.rawTextBlob, content: blob.rawTextBlob,
autoDetect: true, // We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145) autoDetect: true, // We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145)
...@@ -44,5 +40,11 @@ export const viewerProps = (type, blob) => { ...@@ -44,5 +40,11 @@ export const viewerProps = (type, blob) => {
url: blob.rawPath, url: blob.rawPath,
fileSize: blob.rawSize, fileSize: blob.rawSize,
}, },
}[type]; lfs: {
fileName: blob.name,
filePath: blob.rawPath,
},
};
return props[type] || props[blob.externalStorage];
}; };
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
i18n: {
lfsText: __(
'This content could not be displayed because it is stored in LFS. You can %{linkStart}download it%{linkEnd} instead.',
),
},
components: {
GlLink,
GlSprintf,
},
props: {
fileName: {
type: String,
required: true,
},
filePath: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="gl-text-center gl-py-13 gl-bg-gray-50" data-type="lfs">
<gl-sprintf :message="$options.i18n.lfsText">
<template #link="{ content }">
<gl-link :href="filePath" :download="fileName" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
...@@ -32,6 +32,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) { ...@@ -32,6 +32,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
archived archived
storedExternally storedExternally
externalStorage externalStorage
externalStorageUrl
rawPath rawPath
replacePath replacePath
pipelineEditorPath pipelineEditorPath
......
...@@ -115,7 +115,12 @@ export default { ...@@ -115,7 +115,12 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="file-content code js-syntax-highlight" :class="$options.userColorScheme"> <div
class="file-content code js-syntax-highlight blob-content"
:class="$options.userColorScheme"
data-type="simple"
data-qa-selector="blob_viewer_file_content"
>
<line-numbers :lines="lineNumbers" /> <line-numbers :lines="lineNumbers" />
<pre class="code"><code v-safe-html="highlightedContent"></code> <pre class="code"><code v-safe-html="highlightedContent"></code>
</pre> </pre>
......
...@@ -125,7 +125,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated ...@@ -125,7 +125,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
def external_storage_url def external_storage_url
return unless static_objects_external_storage_enabled? return unless static_objects_external_storage_enabled?
external_storage_url_or_path(url_helpers.project_raw_url(project, ref_qualified_path)) external_storage_url_or_path(url_helpers.project_raw_url(project, ref_qualified_path), project)
end end
private private
......
...@@ -36563,6 +36563,9 @@ msgstr "" ...@@ -36563,6 +36563,9 @@ msgstr ""
msgid "This content could not be displayed because %{reason}. You can %{options} instead." msgid "This content could not be displayed because %{reason}. You can %{options} instead."
msgstr "" msgstr ""
msgid "This content could not be displayed because it is stored in LFS. You can %{linkStart}download it%{linkEnd} instead."
msgstr ""
msgid "This credential has expired" msgid "This credential has expired"
msgstr "" msgstr ""
......
...@@ -22,6 +22,10 @@ module QA ...@@ -22,6 +22,10 @@ module QA
element :copy_contents_button element :copy_contents_button
end end
base.view 'app/assets/javascripts/vue_shared/components/source_viewer.vue' do
element :blob_viewer_file_content
end
base.view 'app/views/projects/blob/_header_content.html.haml' do base.view 'app/views/projects/blob/_header_content.html.haml' do
element :file_name_content element :file_name_content
end end
......
...@@ -29,443 +29,421 @@ RSpec.describe 'File blob', :js do ...@@ -29,443 +29,421 @@ RSpec.describe 'File blob', :js do
).execute ).execute
end end
before do context 'with refactor_blob_viewer feature flag enabled' do
stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350455 context 'Ruby file' do
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
before do before do
visit_blob('files/markdown/ruby-style-guide.md') visit_blob('files/ruby/popen.rb')
wait_for_requests wait_for_requests
end end
it 'displays the blob using the rich viewer' do it 'displays the blob' do
aggregate_failures do aggregate_failures do
# hides the simple viewer # shows highlighted Ruby code
expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_selector('.blob-viewer[data-type="rich"]') expect(page).to have_content("require 'fileutils'")
# shows rendered Markdown
expect(page).to have_link("PEP-8")
# shows a viewer switcher # does not show a viewer switcher
expect(page).to have_selector('.js-blob-viewer-switcher') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# shows a disabled copy button # shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn.disabled') expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
# shows a raw button # shows a raw button
expect(page).to have_link('Open raw') expect(page).to have_link('Open raw')
end end
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 before do
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click visit_blob('files/markdown/ruby-style-guide.md')
wait_for_requests wait_for_requests
end end
it 'displays the blob using the simple viewer' do it 'displays the blob using the rich viewer' do
aggregate_failures do aggregate_failures do
# hides the rich viewer # hides the simple viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]') expect(page).not_to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) expect(page).to have_selector('.blob-viewer[data-type="rich"]')
# shows highlighted Markdown code # shows rendered Markdown
expect(page).to have_css(".js-syntax-highlight") expect(page).to have_link("PEP-8")
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button # shows a viewer switcher
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') 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
end end
context 'switching to the rich viewer again' do context 'switching to the simple viewer' do
before 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 wait_for_requests
end end
it 'displays the blob using the rich viewer' do it 'displays the blob using the simple viewer' do
aggregate_failures do aggregate_failures do
# hides the simple viewer # hides the rich viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) expect(page).to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]') 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 # shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
end end
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 context 'switching to the rich viewer again' do
click_link ref_name before do
wait_for_requests find('.js-blob-viewer-switch-btn[data-viewer=rich]').click
end
end
it 'displays single highlighted line number of different ref' do wait_for_requests
visit_blob('files/js/application.js', anchor: 'L1') 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 # shows a disabled copy button
expect(find_by_id('LC1')[:class]).to include("hll") expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
end
end
end
end end
end end
it 'displays multiple highlighted line numbers of different ref' do context 'when ref switch' do
visit_blob('files/js/application.js', anchor: 'L1-3') def switch_ref_to(ref_name)
first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage
switch_ref_to('feature')
page.within '.blob-content' do page.within '.project-refs-form' do
expect(find_by_id('LC1')[:class]).to include("hll") click_link ref_name
expect(find_by_id('LC2')[:class]).to include("hll") wait_for_requests
expect(find_by_id('LC3')[:class]).to include("hll") end
end end
end
it 'displays no highlighted number of different ref' do it 'displays no highlighted number of different ref' do
Files::UpdateService.new( Files::UpdateService.new(
project, project,
project.first_owner, project.first_owner,
commit_message: 'Update', commit_message: 'Update',
start_branch: 'feature', start_branch: 'feature',
branch_name: 'feature', branch_name: 'feature',
file_path: 'files/js/application.js', file_path: 'files/js/application.js',
file_content: 'new content' file_content: 'new content'
).execute ).execute
project.commit('feature').diffs.diff_files.first project.commit('feature').diffs.diff_files.first
visit_blob('files/js/application.js', anchor: 'L3') visit_blob('files/js/application.js', anchor: 'L3')
switch_ref_to('feature') switch_ref_to('feature')
page.within '.blob-content' do page.within '.blob-content' do
expect(page).not_to have_css('.hll') expect(page).not_to have_css('.hll')
end
end end
end
context 'successfully change ref of similar name' do context 'successfully change ref of similar name' do
before do before do
project.repository.create_branch('dev') project.repository.create_branch('dev')
project.repository.create_branch('development') project.repository.create_branch('development')
end end
it 'switch ref from longer to shorter ref name' do it 'switch ref from longer to shorter ref name' do
visit_blob('files/js/application.js', ref: 'development') visit_blob('files/js/application.js', ref: 'development')
switch_ref_to('dev') switch_ref_to('dev')
aggregate_failures do aggregate_failures do
expect(page.find('.file-title-name').text).to eq('application.js') expect(page.find('.file-title-name').text).to eq('application.js')
expect(page).not_to have_css('flash-container') expect(page).not_to have_css('flash-container')
end
end end
end
it 'switch ref from shorter to longer ref name' do it 'switch ref from shorter to longer ref name' do
visit_blob('files/js/application.js', ref: 'dev') visit_blob('files/js/application.js', ref: 'dev')
switch_ref_to('development') switch_ref_to('development')
aggregate_failures do aggregate_failures do
expect(page.find('.file-title-name').text).to eq('application.js') expect(page.find('.file-title-name').text).to eq('application.js')
expect(page).not_to have_css('flash-container') expect(page).not_to have_css('flash-container')
end
end end
end end
end
it 'successfully changes ref when the ref name matches the project name' do it 'successfully changes ref when the ref name matches the project name' do
project.repository.create_branch(project.name) project.repository.create_branch(project.name)
visit_blob('files/js/application.js', ref: project.name) visit_blob('files/js/application.js', ref: project.name)
switch_ref_to('master') switch_ref_to('master')
aggregate_failures do aggregate_failures do
expect(page.find('.file-title-name').text).to eq('application.js') expect(page.find('.file-title-name').text).to eq('application.js')
expect(page).not_to have_css('flash-container') expect(page).not_to have_css('flash-container')
end
end end
end end
end end
context 'visiting with a line number anchor' do context 'Markdown rendering' do
before do before do
visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1') project.add_maintainer(project.creator)
end
it 'displays the blob using the simple viewer' do Files::CreateService.new(
aggregate_failures do project,
# hides the rich viewer project.creator,
expect(page).to have_selector('.blob-viewer[data-type="simple"]') start_branch: 'master',
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) 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 context 'when rendering default markdown' do
expect(page).to have_selector('#LC1.hll') before do
visit_blob('files/commonmark/file.md')
# shows highlighted Markdown code wait_for_requests
expect(page).to have_css(".js-syntax-highlight") end
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button it 'renders using CommonMark' do
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') aggregate_failures do
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
end end
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 before do
visit_blob('files/commonmark/file.md') project.add_maintainer(project.creator)
wait_for_requests
end
it 'renders using CommonMark' do Files::CreateService.new(
aggregate_failures do project,
expect(page).to have_content("sublist") project.creator,
expect(page).not_to have_xpath("//ol//li//ul") start_branch: 'master',
end 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
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 context 'when LFS is enabled on the project' do
before do before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
project.update_attribute(:lfs_enabled, true) project.update_attribute(:lfs_enabled, true)
visit_blob('files/lfs/file.md') visit_blob('files/lfs/file.md')
wait_for_requests wait_for_requests
end end
it 'displays an error' do it 'displays an error' do
aggregate_failures do aggregate_failures do
# hides the simple viewer # hides the simple viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]', visible: false) expect(page).not_to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]') expect(page).not_to have_selector('.blob-viewer[data-type="rich"]')
# shows an error message # 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.') 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 # does not show a viewer switcher
expect(page).to have_selector('.js-blob-viewer-switcher') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# does not show a copy button # does not show a copy button
expect(page).not_to have_selector('.js-copy-blob-source-btn') expect(page).not_to have_selector('.js-copy-blob-source-btn')
# shows a download button # shows a download button
expect(page).to have_link('Download') expect(page).to have_link('Download')
end
end end
end end
context 'switching to the simple viewer' do context 'when LFS is disabled on the project' do
before 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 wait_for_requests
end end
it 'displays an error' do it 'displays the blob' do
aggregate_failures do aggregate_failures do
# hides the rich viewer # shows text
expect(page).to have_selector('.blob-viewer[data-type="simple"]') expect(page).to have_content('size 1575078')
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
# shows an error message # does not show a viewer switcher
expect(page).to have_content('The source could not be displayed because it is stored in LFS. You can download it instead.') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# does not show a copy button # shows an enabled copy button
expect(page).not_to have_selector('.js-copy-blob-source-btn') 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
end end
end end
context 'when LFS is disabled on the project' do context 'PDF file' do
before 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 wait_for_requests
end end
it 'displays the blob' do it 'displays the blob' do
aggregate_failures do aggregate_failures do
# shows text # shows rendered PDF
expect(page).to have_content('size 1575078') expect(page).to have_selector('.js-pdf-viewer')
# does not show a viewer switcher # does not show a viewer switcher
expect(page).not_to have_selector('.js-blob-viewer-switcher') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# shows an enabled copy button # does not show a copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).not_to have_selector('.js-copy-blob-source-btn')
# shows a raw button # shows a download button
expect(page).to have_link('Open raw') expect(page).to have_link('Download')
end end
end end
end end
end
context 'PDF file' do context 'Jupiter Notebook file' do
before do before do
project.add_maintainer(project.creator) 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
Files::CreateService.new( visit_blob('files/basic.ipynb')
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
wait_for_requests it 'displays the blob' do
end 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')
it 'displays the blob' do # show a disabled copy button
aggregate_failures do expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
# shows rendered PDF
expect(page).to have_selector('.js-pdf-viewer')
# does not show a viewer switcher # shows a raw button
expect(page).not_to have_selector('.js-blob-viewer-switcher') expect(page).to have_link('Open raw')
# does not show a copy button # shows a download button
expect(page).not_to have_selector('.js-copy-blob-source-btn') expect(page).to have_link('Download')
# shows a download button # shows the rendered notebook
expect(page).to have_link('Download') expect(page).to have_content('test')
end
end end
end end
end
context 'Jupiter Notebook file' do context 'ISO file (stored in LFS)' do
before do context 'when LFS is enabled on the project' do
project.add_maintainer(project.creator) before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
project.update_attribute(:lfs_enabled, true)
Files::CreateService.new( visit_blob('files/lfs/lfs_object.iso')
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') wait_for_requests
end
wait_for_requests it 'displays the blob' do
end 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 context 'when LFS is disabled on the project' do
aggregate_failures do before do
# shows rendered notebook visit_blob('files/lfs/lfs_object.iso')
expect(page).to have_selector('.js-notebook-viewer-mounted')
# does show a viewer switcher wait_for_requests
expect(page).to have_selector('.js-blob-viewer-switcher') end
# show a disabled copy button it 'displays the blob' do
expect(page).to have_selector('.js-copy-blob-source-btn.disabled') aggregate_failures do
# shows text
expect(page).to have_content('size 1575078')
# shows a raw button # does not show a viewer switcher
expect(page).to have_link('Open raw') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# shows a download button # shows an enabled copy button
expect(page).to have_link('Download') expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
# shows the rendered notebook # shows a raw button
expect(page).to have_content('test') expect(page).to have_link('Open raw')
end
end
end end
end end
end
context 'ISO file (stored in LFS)' do context 'ZIP file' do
context 'when LFS is enabled on the project' do
before do before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) visit_blob('Gemfile.zip')
project.update_attribute(:lfs_enabled, true)
visit_blob('files/lfs/lfs_object.iso')
wait_for_requests wait_for_requests
end end
...@@ -473,7 +451,7 @@ RSpec.describe 'File blob', :js do ...@@ -473,7 +451,7 @@ RSpec.describe 'File blob', :js do
it 'displays the blob' do it 'displays the blob' do
aggregate_failures do aggregate_failures do
# shows a download link # 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 # does not show a viewer switcher
expect(page).not_to have_selector('.js-blob-viewer-switcher') expect(page).not_to have_selector('.js-blob-viewer-switcher')
...@@ -487,578 +465,703 @@ RSpec.describe 'File blob', :js do ...@@ -487,578 +465,703 @@ RSpec.describe 'File blob', :js do
end end
end end
context 'when LFS is disabled on the project' do context 'empty file' do
before 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 wait_for_requests
end end
it 'displays the blob' do it 'displays an error' do
aggregate_failures do aggregate_failures do
# shows text # shows an error message
expect(page).to have_content('size 1575078') expect(page).to have_content('Empty file')
# does not show a viewer switcher # does not show a viewer switcher
expect(page).not_to have_selector('.js-blob-viewer-switcher') expect(page).not_to have_selector('.js-blob-viewer-switcher')
# shows an enabled copy button # does not show a copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).not_to have_selector('.js-copy-blob-source-btn')
# shows a raw button # does not show a download or raw button
expect(page).to have_link('Open raw') expect(page).not_to have_link('Download')
expect(page).not_to have_link('Open raw')
end end
end end
end end
end
context 'ZIP file' do
before do
visit_blob('Gemfile.zip')
wait_for_requests
end
it 'displays the blob' do
aggregate_failures do
# shows a download link
expect(page).to have_link('Download (2.11 KB)')
# does not show a viewer switcher
expect(page).not_to have_selector('.js-blob-viewer-switcher')
# does not show a copy button context 'files with auxiliary viewers' do
expect(page).not_to have_selector('.js-copy-blob-source-btn') describe '.gitlab-ci.yml' do
before do
project.add_maintainer(project.creator)
# shows a download button Files::CreateService.new(
expect(page).to have_link('Download') project,
end project.creator,
end start_branch: 'master',
end 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
context 'empty file' do visit_blob('.gitlab-ci.yml')
before do end
project.add_maintainer(project.creator)
Files::CreateService.new( it 'displays an auxiliary viewer' do
project, aggregate_failures do
project.creator, # shows that configuration is valid
start_branch: 'master', expect(page).to have_content('This GitLab CI configuration is valid.')
branch_name: 'master',
commit_message: "Add empty file",
file_path: 'files/empty.md',
file_content: ''
).execute
visit_blob('files/empty.md') # shows a learn more link
expect(page).to have_link('Learn more')
end
end
end
wait_for_requests describe '.gitlab/route-map.yml' do
end before do
project.add_maintainer(project.creator)
it 'displays an error' do Files::CreateService.new(
aggregate_failures do project,
# shows an error message project.creator,
expect(page).to have_content('Empty file') 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
# does not show a viewer switcher visit_blob('.gitlab/route-map.yml')
expect(page).not_to have_selector('.js-blob-viewer-switcher') end
# does not show a copy button it 'displays an auxiliary viewer' do
expect(page).not_to have_selector('.js-copy-blob-source-btn') aggregate_failures do
# shows that map is valid
expect(page).to have_content('This Route Map is valid.')
# does not show a download or raw button # shows a learn more link
expect(page).not_to have_link('Download') expect(page).to have_link('Learn more')
expect(page).not_to have_link('Open raw') end
end
end end
end
end
context 'binary file that appears to be text in the first 1024 bytes' do describe '.gitlab/dashboards/custom-dashboard.yml' do
before do before do
visit_blob('encoding/binary-1.bin', ref: 'binary-encoding') project.add_maintainer(project.creator)
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
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')) }
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
end
end
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")
# 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
it 'displays the blob' do context 'valid dashboard file' do
aggregate_failures do let(:file_content) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) }
# shows a download link
expect(page).to have_link('Download (23.8 KB)')
# does not show a viewer switcher it 'displays an auxiliary viewer' do
expect(page).not_to have_selector('.js-blob-viewer-switcher') aggregate_failures do
# shows that dashboard yaml is valid
expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
# The specs below verify an arguably incorrect result, but since we only # shows a learn more link
# learn that the file is not actually text once the text viewer content expect(page).to have_link('Learn more')
# is loaded asynchronously, there is no straightforward way to get these end
# synchronously loaded elements to display correctly. end
# end
# 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 context 'invalid dashboard file' do
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') 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")
# shows a raw button, incorrectly # shows a learn more link
expect(page).to have_link('Open raw') expect(page).to have_link('Learn more')
end
end
end
end
end end
end
end
context 'files with auxiliary viewers' do context 'LICENSE' do
before do before do
stub_feature_flags(refactor_blob_viewer: true) visit_blob('LICENSE')
end end
describe '.gitlab-ci.yml' do it 'displays an auxiliary viewer' do
before do aggregate_failures do
project.add_maintainer(project.creator) # shows license
expect(page).to have_content('This project is licensed under the MIT License.')
Files::CreateService.new( # shows a learn more link
project, expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/')
project.creator, end
start_branch: 'master', end
branch_name: 'master', end
commit_message: "Add .gitlab-ci.yml",
file_path: '.gitlab-ci.yml', context '*.gemspec' do
file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) before do
).execute project.add_maintainer(project.creator)
visit_blob('.gitlab-ci.yml') 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
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.')
# shows a learn more link
expect(page).to have_link('Learn more', href: 'https://rubygems.org/')
end
end
end end
it 'displays an auxiliary viewer' do context 'CONTRIBUTING.md' do
aggregate_failures do before do
# shows that configuration is valid file_name = 'CONTRIBUTING.md'
expect(page).to have_content('This GitLab CI configuration is valid.')
# shows a learn more link create_file(file_name, '## Contribution guidelines')
expect(page).to have_link('Learn more') 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
end end
end
describe '.gitlab/route-map.yml' do context 'CHANGELOG.md' do
before do before do
project.add_maintainer(project.creator) file_name = 'CHANGELOG.md'
Files::CreateService.new( create_file(file_name, '## Changelog for v1.0.0')
project, visit_blob(file_name)
project.creator, end
start_branch: 'master',
branch_name: 'master', it 'displays an auxiliary viewer' do
commit_message: "Add .gitlab/route-map.yml", aggregate_failures do
file_path: '.gitlab/route-map.yml', 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.")
file_content: <<-MAP.strip_heredoc end
# Team data end
- source: 'data/team.yml' end
public: 'team/'
MAP context 'Cargo.toml' do
).execute 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
visit_blob('.gitlab/route-map.yml') 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'
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 '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
end
end
context 'Gemfile' do
before do
file_name = 'Gemfile'
create_file(file_name, '
source "https://rubygems.org"
# 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
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
end
end
context 'go.mod' do
before do
file_name = 'go.mod'
create_file(file_name, '
module example.com/mymodule
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
end
end end
it 'displays an auxiliary viewer' do context 'package.json' do
aggregate_failures do before do
# shows that map is valid file_name = 'package.json'
expect(page).to have_content('This Route Map is valid.')
# shows a learn more link create_file(file_name, '
expect(page).to have_link('Learn more') {
"name": "my-awesome-package",
"version": "1.0.0"
}
')
visit_blob(file_name)
end end
end
end
describe '.gitlab/dashboards/custom-dashboard.yml' do
before do
project.add_maintainer(project.creator)
Files::CreateService.new( it 'displays an auxiliary viewer' do
project, aggregate_failures do
project.creator, expect(page).to have_content("This project manages its dependencies using npm.")
start_branch: 'master', end
branch_name: 'master', end
commit_message: "Add .gitlab/dashboards/custom-dashboard.yml",
file_path: '.gitlab/dashboards/custom-dashboard.yml',
file_content: file_content
).execute
end end
context 'with metrics_dashboard_exhaustive_validations feature flag off' do context 'podfile' do
before do before do
stub_feature_flags(metrics_dashboard_exhaustive_validations: false) file_name = 'podfile'
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')) }
it 'displays an auxiliary viewer' do create_file(file_name, 'platform :ios, "8.0"')
aggregate_failures do visit_blob(file_name)
# shows that dashboard yaml is valid end
expect(page).to have_content('Metrics Dashboard YAML definition is valid.')
# shows a learn more link it 'displays an auxiliary viewer' do
expect(page).to have_link('Learn more') aggregate_failures do
end expect(page).to have_content("This project manages its dependencies using CocoaPods.")
end end
end end
end
context 'invalid dashboard file' do context 'test.podspec' do
let(:file_content) { "dashboard: 'invalid'" } before do
file_name = 'test.podspec'
it 'displays an auxiliary viewer' do create_file(file_name, '
aggregate_failures do Pod::Spec.new do |s|
# shows that dashboard yaml is invalid s.name = "TensorFlowLiteC"
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") visit_blob(file_name)
end
# shows a learn more link it 'displays an auxiliary viewer' do
expect(page).to have_link('Learn more') aggregate_failures do
end expect(page).to have_content("This project manages its dependencies using CocoaPods.")
end end
end end
end end
context 'with metrics_dashboard_exhaustive_validations feature flag on' do context 'JSON.podspec.json' do
before do before do
stub_feature_flags(metrics_dashboard_exhaustive_validations: true) file_name = 'JSON.podspec.json'
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')) }
it 'displays an auxiliary viewer' do create_file(file_name, '
aggregate_failures do {
# shows that dashboard yaml is valid "name": "JSON"
expect(page).to have_content('Metrics Dashboard YAML definition is valid.') }
')
visit_blob(file_name)
end
# shows a learn more link it 'displays an auxiliary viewer' do
expect(page).to have_link('Learn more') aggregate_failures do
end expect(page).to have_content("This project manages its dependencies using CocoaPods.")
end end
end end
end
context 'invalid dashboard file' do context 'requirements.txt' do
let(:file_content) { "dashboard: 'invalid'" } before do
file_name = 'requirements.txt'
it 'displays an auxiliary viewer' do create_file(file_name, 'Project requirements')
aggregate_failures do visit_blob(file_name)
# shows that dashboard yaml is invalid end
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 it 'displays an auxiliary viewer' do
expect(page).to have_link('Learn more') aggregate_failures do
end expect(page).to have_content("This project manages its dependencies using pip.")
end end
end end
end end
end
context 'LICENSE' do context 'yarn.lock' do
before do before do
visit_blob('LICENSE') file_name = 'yarn.lock'
end
it 'displays an auxiliary viewer' do create_file(file_name, '
aggregate_failures do # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# shows license # yarn lockfile v1
expect(page).to have_content('This project is licensed under the MIT License.') ')
visit_blob(file_name)
end
# shows a learn more link it 'displays an auxiliary viewer' do
expect(page).to have_link('Learn more', href: 'http://choosealicense.com/licenses/mit/') aggregate_failures do
expect(page).to have_content("This project manages its dependencies using Yarn.")
end
end end
end end
end end
context '*.gemspec' do context 'realtime pipelines' do
before do before do
project.add_maintainer(project.creator)
Files::CreateService.new( Files::CreateService.new(
project, project,
project.creator, project.creator,
start_branch: 'master', start_branch: 'feature',
branch_name: 'master', branch_name: 'feature',
commit_message: "Add activerecord.gemspec", commit_message: "Add ruby file",
file_path: 'activerecord.gemspec', file_path: 'files/ruby/test.rb',
file_content: <<-SPEC.strip_heredoc file_content: "# Awesome content"
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "activerecord"
end
SPEC
).execute ).execute
visit_blob('activerecord.gemspec') create(:ci_pipeline, status: 'running', project: project, ref: 'feature', sha: project.commit('feature').sha)
visit_blob('files/ruby/test.rb', ref: 'feature')
end end
it 'displays an auxiliary viewer' do it 'shows the realtime pipeline status' do
aggregate_failures do page.within('.commit-actions') do
# shows names of dependency manager and package expect(page).to have_css('.ci-status-icon')
expect(page).to have_content('This project manages its dependencies using RubyGems.') expect(page).to have_css('.ci-status-icon-running')
expect(page).to have_css('.js-ci-status-icon-running')
# shows a learn more link
expect(page).to have_link('Learn more', href: 'https://rubygems.org/')
end end
end end
end end
context 'CONTRIBUTING.md' do context 'for subgroups' do
before do let(:group) { create(:group) }
file_name = 'CONTRIBUTING.md' let(:subgroup) { create(:group, parent: group) }
let(:project) { create(:project, :public, :repository, group: subgroup) }
create_file(file_name, '## Contribution guidelines') it 'renders tree table without errors' do
visit_blob(file_name) visit_blob('README.md')
end
it 'displays an auxiliary viewer' do expect(page).to have_selector('.file-content')
aggregate_failures do expect(page).not_to have_selector('.flash-alert')
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
end
context 'CHANGELOG.md' do it 'displays a GPG badge' do
before do visit_blob('CONTRIBUTING.md', ref: '33f3729a45c02fc67d00adb1b8bca394b0e761d9')
file_name = 'CHANGELOG.md'
create_file(file_name, '## Changelog for v1.0.0')
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
aggregate_failures do expect(page).to have_selector '.gpg-status-box.invalid'
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
end end
context 'Cargo.toml' do context 'on signed merge commit' do
before do it 'displays a GPG badge' do
file_name = 'Cargo.toml' visit_blob('conflicting-file.md', ref: '6101e87e575de14b38b4e1ce180519a813671e10')
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 expect(page).not_to have_selector '.gpg-status-box.js-loading-gpg-badge'
aggregate_failures do expect(page).to have_selector '.gpg-status-box.invalid'
expect(page).to have_content("This project manages its dependencies using Cargo.")
end
end end
end end
context 'Cartfile' do context 'when static objects external storage is enabled' do
before do before do
file_name = 'Cartfile' stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com')
create_file(file_name, '
gitlab "Alamofire/Alamofire" == 4.9.0
gitlab "Alamofire/AlamofireImage" ~> 3.4
')
visit_blob(file_name)
end end
it 'displays an auxiliary viewer' do context 'public project' do
aggregate_failures do before do
expect(page).to have_content("This project manages its dependencies using Carthage.") visit_blob('README.md')
end end
end
end
context 'composer.json' do
before do
file_name = 'composer.json'
create_file(file_name, ' it 'shows open raw and download buttons with external storage URL prepended to their href' do
{ path = project_raw_path(project, 'master/README.md')
"license": "MIT" raw_uri = "https://cdn.gitlab.com#{path}"
} download_uri = "https://cdn.gitlab.com#{path}?inline=false"
')
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do aggregate_failures do
aggregate_failures do expect(page).to have_link 'Open raw', href: raw_uri
expect(page).to have_content("This project manages its dependencies using Composer.") expect(page).to have_link 'Download', href: download_uri
end
end end
end end
end end
end
context 'Gemfile' do context 'with refactor_blob_viewer feature flag disabled' do
before do before do
file_name = 'Gemfile' stub_feature_flags(refactor_blob_viewer: false)
end
create_file(file_name, ' context 'when ref switch' do
source "https://rubygems.org" # 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
# Gems here def switch_ref_to(ref_name)
') first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do page.within '.project-refs-form' do
aggregate_failures do click_link ref_name
expect(page).to have_content("This project manages its dependencies using Bundler.") wait_for_requests
end end
end end
end
context 'Godeps.json' do context 'when highlighting lines' do
before do it 'displays single highlighted line number of different ref' do
file_name = 'Godeps.json' visit_blob('files/js/application.js', anchor: 'L1')
create_file(file_name, ' switch_ref_to('feature')
{
"GoVersion": "go1.6"
}
')
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do page.within '.blob-content' do
aggregate_failures do expect(find_by_id('LC1')[:class]).to include("hll")
expect(page).to have_content("This project manages its dependencies using godep.") end
end end
end
end
context 'go.mod' do it 'displays multiple highlighted line numbers of different ref' do
before do visit_blob('files/js/application.js', anchor: 'L1-3')
file_name = 'go.mod'
create_file(file_name, '
module example.com/mymodule
go 1.14 switch_ref_to('feature')
')
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do page.within '.blob-content' do
aggregate_failures do expect(find_by_id('LC1')[:class]).to include("hll")
expect(page).to have_content("This project manages its dependencies using Go Modules.") expect(find_by_id('LC2')[:class]).to include("hll")
expect(find_by_id('LC3')[:class]).to include("hll")
end
end end
end end
end end
context 'package.json' do context 'visiting with a line number anchor' do
before do # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
file_name = 'package.json' # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351558
create_file(file_name, ' before do
{ visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1')
"name": "my-awesome-package",
"version": "1.0.0"
}
')
visit_blob(file_name)
end end
it 'displays an auxiliary viewer' do it 'displays the blob using the simple viewer' do
aggregate_failures do aggregate_failures do
expect(page).to have_content("This project manages its dependencies using npm.") # hides the rich viewer
end expect(page).to have_selector('.blob-viewer[data-type="simple"]')
end expect(page).not_to have_selector('.blob-viewer[data-type="rich"]')
end
context 'podfile' do # highlights the line in question
before do expect(page).to have_selector('#LC1.hll')
file_name = 'podfile'
create_file(file_name, 'platform :ios, "8.0"') # shows highlighted Markdown code
visit_blob(file_name) expect(page).to have_css(".js-syntax-highlight")
end expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
it 'displays an auxiliary viewer' do # shows an enabled copy button
aggregate_failures do expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
expect(page).to have_content("This project manages its dependencies using CocoaPods.")
end end
end end
end end
context 'test.podspec' do context 'binary file that appears to be text in the first 1024 bytes' do
before do # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
file_name = 'test.podspec' # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351559
create_file(file_name, ' before do
Pod::Spec.new do |s| visit_blob('encoding/binary-1.bin', ref: 'binary-encoding')
s.name = "TensorFlowLiteC"
')
visit_blob(file_name)
end end
it 'displays the blob' do
it 'displays an auxiliary viewer' do
aggregate_failures do aggregate_failures do
expect(page).to have_content("This project manages its dependencies using CocoaPods.") # 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 end
end end
context 'JSON.podspec.json' do context 'when static objects external storage is enabled' do
before do # We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
file_name = 'JSON.podspec.json' # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351555
create_file(file_name, '
{
"name": "JSON"
}
')
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do before do
aggregate_failures do stub_application_setting(static_objects_external_storage_url: 'https://cdn.gitlab.com')
expect(page).to have_content("This project manages its dependencies using CocoaPods.")
end
end end
end
context 'requirements.txt' do context 'private project' do
before do let_it_be(:project) { create(:project, :repository, :private) }
file_name = 'requirements.txt' let_it_be(:user) { create(:user) }
create_file(file_name, 'Project requirements') before do
visit_blob(file_name) project.add_developer(user)
end
it 'displays an auxiliary viewer' do sign_in(user)
aggregate_failures do visit_blob('README.md')
expect(page).to have_content("This project manages its dependencies using pip.")
end end
end
end
context 'yarn.lock' do
before do
file_name = 'yarn.lock'
create_file(file_name, ' it 'shows open raw and download buttons with external storage URL prepended and user token appended to their href' do
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. path = project_raw_path(project, 'master/README.md')
# yarn lockfile v1 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}"
visit_blob(file_name)
end
it 'displays an auxiliary viewer' do aggregate_failures do
aggregate_failures do expect(page).to have_link 'Open raw', href: raw_uri
expect(page).to have_content("This project manages its dependencies using Yarn.") expect(page).to have_link 'Download', href: download_uri
end
end end
end end
end end
context 'when refactor_blob_viewer is disabled' do context 'files with auxiliary viewers' do
before 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
stub_feature_flags(refactor_blob_viewer: false) # It should be safe to remove once we rollout the refactored blob viewer
end
describe '.gitlab-ci.yml' do describe '.gitlab-ci.yml' do
before do before do
...@@ -1554,104 +1657,4 @@ RSpec.describe 'File blob', :js do ...@@ -1554,104 +1657,4 @@ RSpec.describe 'File blob', :js do
end end
end 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 end
...@@ -13,6 +13,7 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = ` ...@@ -13,6 +13,7 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = `
<blob-filepath-stub <blob-filepath-stub
blob="[object Object]" blob="[object Object]"
showpath="true"
/> />
</div> </div>
......
...@@ -236,7 +236,7 @@ describe('Blob content viewer component', () => { ...@@ -236,7 +236,7 @@ describe('Blob content viewer component', () => {
await waitForPromises(); await waitForPromises();
expect(loadViewer).toHaveBeenCalledWith(viewer); expect(loadViewer).toHaveBeenCalledWith(viewer, false);
expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true); expect(wrapper.findComponent(loadViewerReturnValue).exists()).toBe(true);
}, },
); );
......
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,
});
});
});
...@@ -17,6 +17,7 @@ export const simpleViewerMock = { ...@@ -17,6 +17,7 @@ export const simpleViewerMock = {
canCurrentUserPushToBranch: true, canCurrentUserPushToBranch: true,
archived: false, archived: false,
storedExternally: false, storedExternally: false,
externalStorageUrl: '',
externalStorage: 'lfs', externalStorage: 'lfs',
rawPath: 'some_file.js', rawPath: 'some_file.js',
replacePath: 'some_file.js/replace', replacePath: 'some_file.js/replace',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment