Commit ee294618 authored by Matthias Käppler's avatar Matthias Käppler

Merge branch 'blob-show-spec' into 'master'

Blob refactor: Fix/update blob show specs

See merge request gitlab-org/gitlab!78541
parents 84ff263c 9e1369c1
...@@ -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
......
...@@ -36578,6 +36578,9 @@ msgstr "" ...@@ -36578,6 +36578,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,10 +29,7 @@ RSpec.describe 'File blob', :js do ...@@ -29,10 +29,7 @@ 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
end
context 'Ruby file' do context 'Ruby file' do
before do before do
visit_blob('files/ruby/popen.rb') visit_blob('files/ruby/popen.rb')
...@@ -79,7 +76,7 @@ RSpec.describe 'File blob', :js do ...@@ -79,7 +76,7 @@ RSpec.describe 'File blob', :js do
it 'displays the blob using the rich viewer' do it 'displays the blob using the rich viewer' 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).to have_selector('.blob-viewer[data-type="rich"]')
# shows rendered Markdown # shows rendered Markdown
...@@ -107,7 +104,7 @@ RSpec.describe 'File blob', :js do ...@@ -107,7 +104,7 @@ RSpec.describe 'File blob', :js do
aggregate_failures do aggregate_failures do
# hides the rich viewer # hides the rich viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]') expect(page).to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false) expect(page).not_to have_selector('.blob-viewer[data-type="rich"]')
# shows highlighted Markdown code # shows highlighted Markdown code
expect(page).to have_css(".js-syntax-highlight") expect(page).to have_css(".js-syntax-highlight")
...@@ -128,11 +125,11 @@ RSpec.describe 'File blob', :js do ...@@ -128,11 +125,11 @@ RSpec.describe 'File blob', :js do
it 'displays the blob using the rich viewer' do it 'displays the blob using the rich viewer' 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).to have_selector('.blob-viewer[data-type="rich"]')
# shows an enabled copy button # shows a disabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
end end
end end
end end
...@@ -149,28 +146,6 @@ RSpec.describe 'File blob', :js do ...@@ -149,28 +146,6 @@ RSpec.describe 'File blob', :js do
end end
end end
it 'displays single highlighted line number of different ref' do
visit_blob('files/js/application.js', anchor: 'L1')
switch_ref_to('feature')
page.within '.blob-content' do
expect(find_by_id('LC1')[:class]).to include("hll")
end
end
it 'displays multiple highlighted line numbers of different ref' do
visit_blob('files/js/application.js', anchor: 'L1-3')
switch_ref_to('feature')
page.within '.blob-content' do
expect(find_by_id('LC1')[:class]).to include("hll")
expect(find_by_id('LC2')[:class]).to include("hll")
expect(find_by_id('LC3')[:class]).to include("hll")
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,
...@@ -231,30 +206,6 @@ RSpec.describe 'File blob', :js do ...@@ -231,30 +206,6 @@ RSpec.describe 'File blob', :js do
end end
end end
end end
context 'visiting with a line number anchor' do
before do
visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1')
end
it 'displays the blob using the simple viewer' do
aggregate_failures do
# hides the rich viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
# highlights the line in question
expect(page).to have_selector('#LC1.hll')
# shows highlighted Markdown code
expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
end
end
end
end end
context 'Markdown rendering' do context 'Markdown rendering' do
...@@ -316,14 +267,14 @@ RSpec.describe 'File blob', :js do ...@@ -316,14 +267,14 @@ RSpec.describe 'File blob', :js do
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')
...@@ -332,28 +283,6 @@ RSpec.describe 'File blob', :js do ...@@ -332,28 +283,6 @@ RSpec.describe 'File blob', :js do
expect(page).to have_link('Download') expect(page).to have_link('Download')
end end
end end
context 'switching to the simple viewer' do
before do
find('.js-blob-viewer-switcher .js-blob-viewer-switch-btn[data-viewer=simple]').click
wait_for_requests
end
it 'displays an error' do
aggregate_failures do
# hides the rich viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]')
expect(page).to have_selector('.blob-viewer[data-type="rich"]', visible: false)
# shows an error message
expect(page).to have_content('The source could not be displayed because it is stored in LFS. You can download it instead.')
# does not show a copy button
expect(page).not_to have_selector('.js-copy-blob-source-btn')
end
end
end
end end
context 'when LFS is disabled on the project' do context 'when LFS is disabled on the project' do
...@@ -473,7 +402,7 @@ RSpec.describe 'File blob', :js do ...@@ -473,7 +402,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 (1.50 MiB)')
# 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')
...@@ -522,7 +451,7 @@ RSpec.describe 'File blob', :js do ...@@ -522,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 (2.11 KB)') 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')
...@@ -573,42 +502,7 @@ RSpec.describe 'File blob', :js do ...@@ -573,42 +502,7 @@ RSpec.describe 'File blob', :js do
end end
end end
context 'binary file that appears to be text in the first 1024 bytes' do
before do
visit_blob('encoding/binary-1.bin', ref: 'binary-encoding')
end
it 'displays the blob' do
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')
end
end
end
context 'files with auxiliary viewers' do context 'files with auxiliary viewers' do
before do
stub_feature_flags(refactor_blob_viewer: true)
end
describe '.gitlab-ci.yml' do describe '.gitlab-ci.yml' do
before do before do
project.add_maintainer(project.creator) project.add_maintainer(project.creator)
...@@ -1054,12 +948,221 @@ RSpec.describe 'File blob', :js do ...@@ -1054,12 +948,221 @@ RSpec.describe 'File blob', :js do
end 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 'when refactor_blob_viewer is disabled' do 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
context 'with refactor_blob_viewer feature flag disabled' do
before do before do
stub_feature_flags(refactor_blob_viewer: false) stub_feature_flags(refactor_blob_viewer: false)
end end
context 'when ref switch' do
# We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
# This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351558
def switch_ref_to(ref_name)
first('.qa-branches-select').click # rubocop:disable QA/SelectorUsage
page.within '.project-refs-form' do
click_link ref_name
wait_for_requests
end
end
context 'when highlighting lines' do
it 'displays single highlighted line number of different ref' do
visit_blob('files/js/application.js', anchor: 'L1')
switch_ref_to('feature')
page.within '.blob-content' do
expect(find_by_id('LC1')[:class]).to include("hll")
end
end
it 'displays multiple highlighted line numbers of different ref' do
visit_blob('files/js/application.js', anchor: 'L1-3')
switch_ref_to('feature')
page.within '.blob-content' do
expect(find_by_id('LC1')[:class]).to include("hll")
expect(find_by_id('LC2')[:class]).to include("hll")
expect(find_by_id('LC3')[:class]).to include("hll")
end
end
end
end
context 'visiting with a line number anchor' do
# We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
# This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351558
before do
visit_blob('files/markdown/ruby-style-guide.md', anchor: 'L1')
end
it 'displays the blob using the simple viewer' do
aggregate_failures do
# hides the rich viewer
expect(page).to have_selector('.blob-viewer[data-type="simple"]')
expect(page).not_to have_selector('.blob-viewer[data-type="rich"]')
# highlights the line in question
expect(page).to have_selector('#LC1.hll')
# shows highlighted Markdown code
expect(page).to have_css(".js-syntax-highlight")
expect(page).to have_content("[PEP-8](http://www.python.org/dev/peps/pep-0008/)")
# shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
end
end
end
context '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
before do
visit_blob('encoding/binary-1.bin', ref: 'binary-encoding')
end
it 'displays the blob' do
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')
end
end
end
context 'when static objects external storage is enabled' do
# We need to unsre that this test runs with the refactor_blob_viewer feature flag enabled
# This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/351555
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
end
context 'files with auxiliary viewers' do
# This context is the same as the other 'files with auxiliary viewers' in this file, we just ensure that the auxiliary viewers still work this the refactor_blob_viewer disabled
# It should be safe to remove once we rollout the refactored blob viewer
describe '.gitlab-ci.yml' do describe '.gitlab-ci.yml' do
before do before do
project.add_maintainer(project.creator) project.add_maintainer(project.creator)
...@@ -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