From 4a7e36f410cc0a9466167b94dc76e7c92deddebe Mon Sep 17 00:00:00 2001 From: Paulina Sedlak-Jakubowska <psedlak-jakubowska@gitlab.com> Date: Fri, 17 Jan 2025 15:07:14 +0000 Subject: [PATCH] Remove unused prop The only component that was passing that prop has been refactored to not use BlobHeader component at all in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175292 Changelog: changed --- .../blob/components/blob_header.vue | 7 +- .../components/blob_content_viewer.vue | 1 + .../repository/components/header_area.vue | 3 +- .../components/header_area/blob_controls.vue | 41 +++++- .../header_area/blob_overflow_menu.vue | 34 +++-- .../javascripts/repository/init_header_app.js | 2 + .../queries/blob_controls.query.graphql | 16 ++ app/controllers/projects/tree_controller.rb | 1 + app/helpers/blob_helper.rb | 1 + ee/spec/frontend/repository/mock_data.js | 1 + scripts/frontend/quarantined_vue3_specs.txt | 1 + .../features/projects/blobs/blob_show_spec.rb | 4 + .../projects/files/user_browses_files_spec.rb | 2 + spec/features/projects/view_on_env_spec.rb | 2 + .../blob/components/blob_header_spec.js | 16 +- .../header_area/blob_controls_spec.js | 138 +++++++++++++++--- .../header_area/blob_overflow_menu_spec.js | 27 +++- spec/frontend/repository/mock_data.js | 23 ++- spec/helpers/blob_helper_spec.rb | 1 + 19 files changed, 268 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/blob/components/blob_header.vue b/app/assets/javascripts/blob/components/blob_header.vue index 63f84fd9e5dc7..9793c5854651d 100644 --- a/app/assets/javascripts/blob/components/blob_header.vue +++ b/app/assets/javascripts/blob/components/blob_header.vue @@ -38,7 +38,7 @@ export default { type: Object, required: true, }, - hideDefaultActions: { + isBlobPage: { type: Boolean, required: false, default: false, @@ -121,9 +121,6 @@ export default { }; }, computed: { - showDefaultActions() { - return !this.hideDefaultActions; - }, showWebIdeLink() { return !this.blob.archived && this.blob.editBlobPath; }, @@ -206,7 +203,7 @@ export default { <slot name="actions"></slot> <default-actions - v-if="showDefaultActions" + v-if="!glFeatures.blobOverflowMenu || (glFeatures.blobOverflowMenu && !isBlobPage)" :raw-path="blob.externalStorageUrl || blob.rawPath" :active-viewer="viewer" :has-render-error="hasRenderError" diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index c05cbdaffe3ae..829cc757d67cc 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -335,6 +335,7 @@ export default { <gl-loading-icon v-if="isLoading" size="sm" /> <div v-if="blobInfo && !isLoading" id="fileHolder" class="file-holder"> <blob-header + is-blob-page :blob="blobInfo" :hide-viewer-switcher="isBinaryFileType || isUsingLfs" :is-binary="isBinaryFileType" diff --git a/app/assets/javascripts/repository/components/header_area.vue b/app/assets/javascripts/repository/components/header_area.vue index 5301d789ae74d..1d3b0cbe07161 100644 --- a/app/assets/javascripts/repository/components/header_area.vue +++ b/app/assets/javascripts/repository/components/header_area.vue @@ -78,6 +78,7 @@ export default { 'kerberosUrl', 'downloadLinks', 'downloadArtifacts', + 'isBinary', ], props: { projectPath: { @@ -296,7 +297,7 @@ export default { </div> <!-- Blob controls --> - <blob-controls :project-path="projectPath" :ref-type="getRefType" /> + <blob-controls :project-path="projectPath" :ref-type="getRefType" :is-binary="isBinary" /> </div> </section> </template> diff --git a/app/assets/javascripts/repository/components/header_area/blob_controls.vue b/app/assets/javascripts/repository/components/header_area/blob_controls.vue index b3b8668afe6b0..7fe14d5720e61 100644 --- a/app/assets/javascripts/repository/components/header_area/blob_controls.vue +++ b/app/assets/javascripts/repository/components/header_area/blob_controls.vue @@ -3,6 +3,7 @@ import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import { __ } from '~/locale'; import { createAlert } from '~/alert'; import getRefMixin from '~/repository/mixins/get_ref'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import initSourcegraph from '~/sourcegraph'; import Shortcuts from '~/behaviors/shortcuts/shortcuts'; import { addShortcutsExtension } from '~/behaviors/shortcuts'; @@ -21,6 +22,8 @@ import { FIND_FILE_BUTTON_CLICK } from '~/tracking/constants'; import { updateElementsVisibility } from '~/repository/utils/dom'; import blobControlsQuery from '~/repository/queries/blob_controls.query.graphql'; import { getRefType } from '~/repository/utils/ref_type'; +import { TEXT_FILE_TYPE } from '../../constants'; +import OverflowMenu from './blob_overflow_menu.vue'; export default { i18n: { @@ -33,11 +36,12 @@ export default { buttonClassList: 'sm:gl-w-auto gl-w-full sm:gl-mt-0 gl-mt-3', components: { GlButton, + OverflowMenu, }, directives: { GlTooltip: GlTooltipDirective, }, - mixins: [getRefMixin], + mixins: [getRefMixin, glFeatureFlagMixin()], apollo: { project: { query: blobControlsQuery, @@ -67,6 +71,11 @@ export default { required: false, default: null, }, + isBinary: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -74,6 +83,9 @@ export default { }; }, computed: { + isLoadingRepositoryBlob() { + return this.$apollo.queries.project.loading; + }, filePath() { return this.$route.params.path; }, @@ -86,6 +98,12 @@ export default { showBlameButton() { return !this.blobInfo.storedExternally && this.blobInfo.externalStorage !== 'lfs'; }, + isBinaryFileType() { + return this.isBinary || this.blobInfo.simpleViewer?.fileType !== TEXT_FILE_TYPE; + }, + rawPath() { + return this.blobInfo.externalStorageUrl || this.blobInfo.rawPath; + }, findFileShortcutKey() { return keysFor(START_SEARCH_PROJECT_FILE)[0]; }, @@ -106,6 +124,9 @@ export default { ? null : sanitize(`${description} <kbd class="flat gl-ml-1" aria-hidden=true>${key}</kbd>`); }, + isEmpty() { + return this.blobInfo.rawSize === '0'; + }, }, watch: { showBlobControls(shouldShow) { @@ -136,11 +157,14 @@ export default { InternalEvents.trackEvent(FIND_FILE_BUTTON_CLICK); Shortcuts.focusSearchFile(); }, + onCopy() { + navigator.clipboard.writeText(this.blobInfo.rawTextBlob); + }, }, }; </script> <template> - <div v-if="showBlobControls" class="gl-flex gl-flex-wrap gl-items-baseline gl-gap-3"> + <div v-if="showBlobControls" class="gl-flex gl-flex-wrap gl-items-center gl-gap-3"> <gl-button v-gl-tooltip.html="findFileTooltip" :aria-keyshortcuts="findFileShortcutKey" @@ -170,5 +194,18 @@ export default { > {{ $options.i18n.permalink }} </gl-button> + + <overflow-menu + v-if="!isLoadingRepositoryBlob && glFeatures.blobOverflowMenu" + :raw-path="rawPath" + :rich-viewer="blobInfo.richViewer" + :simple-viewer="blobInfo.simpleViewer" + :is-binary="isBinaryFileType" + :environment-name="blobInfo.environmentFormattedExternalUrl" + :environment-path="blobInfo.environmentExternalUrlForRouteMap" + :is-empty="isEmpty" + :override-copy="true" + @copy="onCopy" + /> </div> </template> diff --git a/app/assets/javascripts/repository/components/header_area/blob_overflow_menu.vue b/app/assets/javascripts/repository/components/header_area/blob_overflow_menu.vue index 1a0795e8d1996..2911170258fed 100644 --- a/app/assets/javascripts/repository/components/header_area/blob_overflow_menu.vue +++ b/app/assets/javascripts/repository/components/header_area/blob_overflow_menu.vue @@ -2,6 +2,7 @@ import { GlDisclosureDropdown, GlDisclosureDropdownItem, GlTooltipDirective } from '@gitlab/ui'; import { sprintf, s__, __ } from '~/locale'; import { setUrlParams, relativePathToAbsolute, getBaseURL } from '~/lib/utils/url_utility'; +import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants'; export const i18n = { dropdownLabel: __('Actions'), @@ -10,12 +11,8 @@ export const i18n = { btnRawTitle: s__('BlobViewer|Open raw'), }; -const RICH_BLOB_VIEWER = 'rich'; -const SIMPLE_BLOB_VIEWER = 'simple'; - export default { i18n, - RICH_BLOB_VIEWER, components: { GlDisclosureDropdown, GlDisclosureDropdownItem, @@ -36,15 +33,15 @@ export default { type: String, required: true, }, - activeViewer: { - type: String, - default: SIMPLE_BLOB_VIEWER, + richViewer: { + type: Object, required: false, + default: () => {}, }, - hasRenderError: { - type: Boolean, + simpleViewer: { + type: Object, required: false, - default: false, + default: () => {}, }, isBinary: { type: Boolean, @@ -73,11 +70,26 @@ export default { }, }, computed: { + activeViewerType() { + if (this.$route?.query?.plain !== '1') { + const richViewer = document.querySelector('.blob-viewer[data-type="rich"]'); + if (richViewer) { + return RICH_BLOB_VIEWER; + } + } + return SIMPLE_BLOB_VIEWER; + }, + viewer() { + return this.activeViewerType === RICH_BLOB_VIEWER ? this.richViewer : this.simpleViewer; + }, + hasRenderError() { + return Boolean(this.viewer.renderError); + }, downloadUrl() { return setUrlParams({ inline: false }, relativePathToAbsolute(this.rawPath, getBaseURL())); }, copyDisabled() { - return this.activeViewer === this.$options.RICH_BLOB_VIEWER; + return this.activeViewerType === RICH_BLOB_VIEWER; }, getBlobHashTarget() { if (this.overrideCopy) { diff --git a/app/assets/javascripts/repository/init_header_app.js b/app/assets/javascripts/repository/init_header_app.js index 9c062c3cd3dfa..6275ca1b18c98 100644 --- a/app/assets/javascripts/repository/init_header_app.js +++ b/app/assets/javascripts/repository/init_header_app.js @@ -61,6 +61,7 @@ export default function initHeaderApp({ router, isReadmeView = false, isBlobView downloadLinks, downloadArtifacts, projectShortPath, + isBinary, } = headerEl.dataset; const { @@ -125,6 +126,7 @@ export default function initHeaderApp({ router, isReadmeView = false, isBlobView downloadLinks: downloadLinks ? JSON.parse(downloadLinks) : null, downloadArtifacts: downloadArtifacts ? JSON.parse(downloadArtifacts) : [], isBlobView, + isBinary: parseBoolean(isBinary), }, apolloProvider, router: router || createRouter(projectPath, escapedRef), diff --git a/app/assets/javascripts/repository/queries/blob_controls.query.graphql b/app/assets/javascripts/repository/queries/blob_controls.query.graphql index 0f714d1f67944..e6e6505932081 100644 --- a/app/assets/javascripts/repository/queries/blob_controls.query.graphql +++ b/app/assets/javascripts/repository/queries/blob_controls.query.graphql @@ -9,6 +9,22 @@ query getBlobControls($projectPath: ID!, $filePath: String!, $ref: String!, $ref permalinkPath storedExternally externalStorage + environmentFormattedExternalUrl + environmentExternalUrlForRouteMap + rawPath + rawTextBlob + simpleViewer { + fileType + tooLarge + type + renderError + } + richViewer { + fileType + tooLarge + type + renderError + } } } } diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 03e9b1ae5e3de..95e40221eb9b1 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -19,6 +19,7 @@ class Projects::TreeController < Projects::ApplicationController before_action do push_frontend_feature_flag(:inline_blame, @project) + push_frontend_feature_flag(:blob_overflow_menu, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 58480d66d43c8..1ae3c63e3e921 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -306,6 +306,7 @@ def vue_blob_app_data(project, blob, ref) def vue_blob_header_app_data(project, blob, ref) { blob_path: blob.path, + is_binary: blob.binary?, breadcrumbs: breadcrumb_data_attributes, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref), history_link: project_commits_path(project, ref), diff --git a/ee/spec/frontend/repository/mock_data.js b/ee/spec/frontend/repository/mock_data.js index 08981030825a0..ac696a07e02f8 100644 --- a/ee/spec/frontend/repository/mock_data.js +++ b/ee/spec/frontend/repository/mock_data.js @@ -65,6 +65,7 @@ export const headerAppInjected = { downloadArtifacts: [ 'https://gitlab.com/example-group/example-project/-/jobs/artifacts/main/download?job=build', ], + isBinary: false, }; export const userPermissionsMock = { diff --git a/scripts/frontend/quarantined_vue3_specs.txt b/scripts/frontend/quarantined_vue3_specs.txt index 85690d5427d3f..92bf9242274e9 100644 --- a/scripts/frontend/quarantined_vue3_specs.txt +++ b/scripts/frontend/quarantined_vue3_specs.txt @@ -248,6 +248,7 @@ spec/frontend/releases/components/app_edit_new_spec.js spec/frontend/releases/components/asset_links_form_spec.js spec/frontend/releases/components/tag_field_exsting_spec.js spec/frontend/repository/components/header_area/blob_controls_spec.js +spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js spec/frontend/repository/components/table/index_spec.js spec/frontend/repository/components/table/row_spec.js spec/frontend/repository/router_spec.js diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 2089c9df145d7..3b4962ee155da 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -29,6 +29,10 @@ def create_file(file_name, content) ).execute end + before do + stub_feature_flags(blob_overflow_menu: false) + end + context 'Ruby file' do before do visit_blob('files/ruby/popen.rb') diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb index 59bf3922865d7..555fb630890ba 100644 --- a/spec/features/projects/files/user_browses_files_spec.rb +++ b/spec/features/projects/files/user_browses_files_spec.rb @@ -17,6 +17,8 @@ before do sign_in(user) + + stub_feature_flags(blob_overflow_menu: false) end it "shows last commit for current directory", :js do diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb index 5f502c0297a2c..db1fff201b3f8 100644 --- a/spec/features/projects/view_on_env_spec.rb +++ b/spec/features/projects/view_on_env_spec.rb @@ -41,6 +41,8 @@ file_path: file_path, file_content: '# Noop' ).execute + + stub_feature_flags(blob_overflow_menu: false) end context 'and an active deployment' do diff --git a/spec/frontend/blob/components/blob_header_spec.js b/spec/frontend/blob/components/blob_header_spec.js index 8d63f2a22e045..70f0b3c315a31 100644 --- a/spec/frontend/blob/components/blob_header_spec.js +++ b/spec/frontend/blob/components/blob_header_spec.js @@ -22,6 +22,7 @@ describe('Blob Header Default Actions', () => { const defaultProvide = { blobHash: 'foo-bar', + glFeatures: { blobOverflowMenu: true }, }; const findDefaultActions = () => wrapper.findComponent(DefaultActions); @@ -126,6 +127,12 @@ describe('Blob Header Default Actions', () => { }); }); + it('does not render DefaultActions when on blob page', () => { + createComponent({ propsData: { isBlobPage: true } }); + + expect(findDefaultActions().exists()).toBe(false); + }); + it.each([[{ showBlameToggle: true }], [{ showBlameToggle: false }]])( 'passes the `showBlameToggle` prop to the viewer switcher', (propsData) => { @@ -153,15 +160,6 @@ describe('Blob Header Default Actions', () => { expect(findViewSwitcher().exists()).toBe(false); }); - it('does not render default actions is corresponding prop is passed', () => { - createComponent({ - propsData: { - hideDefaultActions: true, - }, - }); - expect(findDefaultActions().exists()).toBe(false); - }); - it.each` slotContent | key ${'Foo Prepend'} | ${'prepend'} diff --git a/spec/frontend/repository/components/header_area/blob_controls_spec.js b/spec/frontend/repository/components/header_area/blob_controls_spec.js index 39a1c194f8129..422164f79353c 100644 --- a/spec/frontend/repository/components/header_area/blob_controls_spec.js +++ b/spec/frontend/repository/components/header_area/blob_controls_spec.js @@ -1,6 +1,5 @@ import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; - import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import BlobControls from '~/repository/components/header_area/blob_controls.vue'; @@ -13,6 +12,7 @@ import { resetShortcutsForTests } from '~/behaviors/shortcuts'; import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob'; import Shortcuts from '~/behaviors/shortcuts/shortcuts'; import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater'; +import OverflowMenu from '~/repository/components/header_area/blob_overflow_menu.vue'; import { blobControlsDataMock, refMock } from '../../mock_data'; jest.mock('~/repository/utils/dom'); @@ -23,24 +23,45 @@ let router; let wrapper; let mockResolver; -const createComponent = async () => { +const createComponent = async ( + props = {}, + blobInfoOverrides = {}, + glFeatures = { blobOverflowMenu: false }, +) => { Vue.use(VueApollo); - const project = { ...blobControlsDataMock }; const projectPath = 'some/project'; - router = createRouter(projectPath, refMock); router.replace({ name: 'blobPath', params: { path: '/some/file.js' } }); - mockResolver = jest.fn().mockResolvedValue({ data: { project } }); + mockResolver = jest.fn().mockResolvedValue({ + data: { + project: { + id: '1234', + repository: { + blobs: { + nodes: [{ ...blobControlsDataMock.repository.blobs.nodes[0], ...blobInfoOverrides }], + }, + }, + }, + }, + }); await resetShortcutsForTests(); wrapper = shallowMountExtended(BlobControls, { router, apolloProvider: createMockApollo([[blobControlsQuery, mockResolver]]), - propsData: { projectPath }, + provide: { + glFeatures, + }, + propsData: { + projectPath, + isBinary: false, + refType: 'heads', + ...props, + }, mixins: [{ data: () => ({ ref: refMock }) }], }); @@ -51,29 +72,56 @@ describe('Blob controls component', () => { const findFindButton = () => wrapper.findByTestId('find'); const findBlameButton = () => wrapper.findByTestId('blame'); const findPermalinkButton = () => wrapper.findByTestId('permalink'); + const findOverflowMenu = () => wrapper.findComponent(OverflowMenu); const { bindInternalEventDocument } = useMockInternalEventsTracking(); beforeEach(() => createComponent()); - it('triggers a `focusSearchFile` shortcut when the findFile button is clicked', () => { - const findFileButton = findFindButton(); - jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue(); - findFileButton.vm.$emit('click'); + describe('FindFile button', () => { + it('renders FindFile button', () => { + expect(findFindButton().exists()).toBe(true); + }); - expect(Shortcuts.focusSearchFile).toHaveBeenCalled(); - }); + it('triggers a `focusSearchFile` shortcut when the findFile button is clicked', () => { + const findFileButton = findFindButton(); + jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue(); + findFileButton.vm.$emit('click'); - it('emits a tracking event when the Find file button is clicked', () => { - const { trackEventSpy } = bindInternalEventDocument(wrapper.element); - jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue(); + expect(Shortcuts.focusSearchFile).toHaveBeenCalled(); + }); - findFindButton().vm.$emit('click'); + it('emits a tracking event when the Find file button is clicked', () => { + const { trackEventSpy } = bindInternalEventDocument(wrapper.element); + jest.spyOn(Shortcuts, 'focusSearchFile').mockResolvedValue(); - expect(trackEventSpy).toHaveBeenCalledWith('click_find_file_button_on_repository_pages'); + findFindButton().vm.$emit('click'); + + expect(trackEventSpy).toHaveBeenCalledWith('click_find_file_button_on_repository_pages'); + }); }); - it('renders a blame button with the correct href', () => { - expect(findBlameButton().attributes('href')).toBe('blame/file.js'); + describe('Blame button', () => { + it('renders a blame button with the correct href', () => { + expect(findBlameButton().attributes('href')).toBe('blame/file.js'); + }); + + it('does not render blame button when blobInfo.storedExternally is true', async () => { + await createComponent({}, { storedExternally: true }); + + expect(findBlameButton().exists()).toBe(false); + }); + + it('does not render blame button when blobInfo.externalStorage is "lfs"', async () => { + await createComponent({}, { externalStorage: 'lfs' }); + + expect(findBlameButton().exists()).toBe(false); + }); + + it('renders blame button when blobInfo.storedExternally is false and externalStorage is not "lfs"', async () => { + await createComponent({}, { storedExternally: false, externalStorage: null }); + + expect(findBlameButton().exists()).toBe(true); + }); }); it('renders a permalink button with the correct href', () => { @@ -105,4 +153,56 @@ describe('Blob controls component', () => { it('loads the BlobLinePermalinkUpdater', () => { expect(BlobLinePermalinkUpdater).toHaveBeenCalled(); }); + + describe('BlobOverflow dropdown', () => { + it('renders BlobOverflow component with correct props', async () => { + await createComponent({}, {}, { blobOverflowMenu: true }); + + expect(findOverflowMenu().exists()).toBe(true); + expect(findOverflowMenu().props()).toEqual({ + rawPath: 'https://testing.com/flightjs/flight/snippets/51/raw', + isBinary: true, + environmentName: '', + environmentPath: '', + isEmpty: false, + overrideCopy: true, + simpleViewer: { + renderError: null, + tooLarge: false, + type: 'simple', + fileType: 'rich', + }, + richViewer: { + renderError: 'too big file', + tooLarge: false, + type: 'rich', + fileType: 'rich', + }, + }); + }); + + it('passes the correct isBinary value to BlobOverflow when viewing a binary file', async () => { + await createComponent( + { isBinary: true }, + { + simpleViewer: { + ...blobControlsDataMock.repository.blobs.nodes[0].simpleViewer, + fileType: 'podfile', + }, + }, + { blobOverflowMenu: true }, + ); + + expect(findOverflowMenu().props('isBinary')).toBe(true); + }); + + it('copies to clipboard raw blob text, when receives copy event', async () => { + await createComponent({}, {}, { blobOverflowMenu: true }); + + jest.spyOn(navigator.clipboard, 'writeText'); + findOverflowMenu().vm.$emit('copy'); + + expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Example raw text content'); + }); + }); }); diff --git a/spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js b/spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js index 949aa6c6614e6..b9c6b9b3eac92 100644 --- a/spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js +++ b/spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js @@ -1,6 +1,8 @@ import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui'; -import BlobOverflowMenu from '~/repository/components/header_area/blob_overflow_menu.vue'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import BlobOverflowMenu from '~/repository/components/header_area/blob_overflow_menu.vue'; +import createRouter from '~/repository/router'; +import { refMock } from '../../mock_data'; const Blob = { binary: false, @@ -36,16 +38,25 @@ const mockEnvironmentPath = 'https://my.testing.environment'; describe('Blob Overflow Menu', () => { let wrapper; + const projectPath = '/some/project'; + const router = createRouter(projectPath, refMock); + + router.replace({ name: 'blobPath', params: { path: '/some/file.js' } }); + const blobHash = 'foo-bar'; function createComponent(propsData = {}, provided = {}) { wrapper = shallowMountExtended(BlobOverflowMenu, { + router, provide: { blobHash, ...provided, }, propsData: { rawPath: Blob.rawPath, + richViewer: Blob.richViewer, + simpleViewer: Blob.simpleViewer, + isBinary: false, ...propsData, }, stub: { @@ -83,9 +94,12 @@ describe('Blob Overflow Menu', () => { }); it('renders "Copy file contents" button as disabled if the viewer is Rich', () => { - createComponent({ - activeViewer: 'rich', - }); + // Create rich viewer element in DOM + const richViewer = document.createElement('div'); + richViewer.className = 'blob-viewer'; + richViewer.dataset.type = 'rich'; + document.body.appendChild(richViewer); + createComponent(); expect(findCopyFileContentItem().props('item')).toMatchObject({ extraAttrs: { disabled: true }, @@ -94,7 +108,10 @@ describe('Blob Overflow Menu', () => { it('does not render the copy button if a rendering error is set', () => { createComponent({ - hasRenderError: true, + richViewer: { + ...Blob.richViewer, + renderError: 'File too big', + }, }); expect(findDropdownItemWithText('Copy file contents')).toBeUndefined(); diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index 4368edb541f62..dc7d40ae78755 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -98,7 +98,27 @@ export const blobControlsDataMock = { blamePath: 'blame/file.js', permalinkPath: 'permalink/file.js', storedExternally: false, - externalStorage: '', + externalStorage: 'https://external-storage', + environmentFormattedExternalUrl: '', + environmentExternalUrlForRouteMap: '', + rawPath: 'https://testing.com/flightjs/flight/snippets/51/raw', + rawTextBlob: 'Example raw text content', + simpleViewer: { + collapsed: false, + loadingPartialName: 'loading', + renderError: null, + tooLarge: false, + type: 'simple', + fileType: 'rich', + }, + richViewer: { + collapsed: false, + loadingPartialName: 'loading', + renderError: 'too big file', + tooLarge: false, + type: 'rich', + fileType: 'rich', + }, }, ], }, @@ -240,4 +260,5 @@ export const headerAppInjected = { downloadArtifacts: [ 'https://gitlab.com/example-group/example-project/-/jobs/artifacts/main/download?job=build', ], + isBinary: false, }; diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 0ef54fa605978..eb4ae483919db 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -596,6 +596,7 @@ it 'returns data related to blob header' do expect(helper.vue_blob_header_app_data(project, blob, ref)).to include({ blob_path: blob.path, + is_binary: blob.binary?, breadcrumbs: breadcrumb_data, escaped_ref: ref, history_link: project_commits_path(project, ref), -- GitLab