From c2e7ecfa8eb08bcb4ec938c33634853df18ee6e1 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Thu, 18 Jul 2024 16:02:07 +0200 Subject: [PATCH 1/7] Allow to edit files from a commit or tag with WebIDE Single file editor and other UI editing actions need to make sure the ref is on top of the branch. WebIDE doesn't have that limitation and allows editing in such situation, by applying a new commit onto a newly created branch. It was possible with a shortcut. This change makes it possible with the Edit button, while still showing a fork suggestion when relevant. Changelog: fixed --- .../blob/components/blob_header.vue | 6 ++++ .../components/blob_content_viewer.vue | 30 +++++++++++------ .../vue_shared/components/web_ide_link.vue | 7 +++- .../repository/blob_info.query.graphql | 1 + app/graphql/types/repository/blob_type.rb | 3 ++ app/helpers/blob_helper.rb | 7 ++++ app/presenters/blob_presenter.rb | 4 +++ doc/api/graphql/reference/index.md | 1 + .../blob/components/blob_header_spec.js | 4 ++- .../components/blob_content_viewer_spec.js | 32 +++++++++++++++++++ spec/frontend/repository/mock_data.js | 1 + .../components/web_ide_link_spec.js | 16 +++++++--- .../types/repository/blob_type_spec.rb | 1 + spec/presenters/blob_presenter_spec.rb | 26 +++++++++++++++ 14 files changed, 124 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/blob/components/blob_header.vue b/app/assets/javascripts/blob/components/blob_header.vue index 5411881a8d2d10..79ac27ccbe2664 100644 --- a/app/assets/javascripts/blob/components/blob_header.vue +++ b/app/assets/javascripts/blob/components/blob_header.vue @@ -75,6 +75,11 @@ export default { required: false, default: false, }, + showWebIdeForkSuggestion: { + type: Boolean, + required: false, + default: false, + }, projectPath: { type: String, required: false, @@ -156,6 +161,7 @@ export default { :edit-url="blob.editBlobPath" :web-ide-url="blob.ideEditPath" :needs-to-fork="showForkSuggestion" + :needs-to-fork-with-web-ide="showWebIdeForkSuggestion" :show-pipeline-editor-button="Boolean(blob.pipelineEditorPath)" :pipeline-editor-url="blob.pipelineEditorPath" :gitpod-url="blob.gitpodBlobUrl" diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 4102036d74152b..a0ffa08f320e1b 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -174,13 +174,20 @@ export default { return pathLock ? pathLock.user : null; }, - showForkSuggestion() { + canFork() { const { createMergeRequestIn, forkProject } = this.userPermissions; + + return this.isLoggedIn && !this.isUsingLfs && createMergeRequestIn && forkProject; + }, + showForkSuggestion() { const { canModifyBlob } = this.blobInfo; - return ( - this.isLoggedIn && !this.isUsingLfs && !canModifyBlob && createMergeRequestIn && forkProject - ); + return this.canFork && !canModifyBlob; + }, + showWebIdeForkSuggestion() { + const { canModifyBlobWithWebIde } = this.blobInfo; + + return this.canFork && !canModifyBlobWithWebIde; }, forkPath() { const forkPaths = { @@ -266,13 +273,17 @@ export default { this.$router.push({ path: this.$route.path, query: { ...this.$route.query, plain } }); }, editBlob(target) { - if (this.showForkSuggestion) { + const { ideEditPath, editBlobPath } = this.blobInfo; + const isIdeTarget = target === 'ide'; + const showForkSuggestion = isIdeTarget + ? this.showWebIdeForkSuggestion + : this.showForkSuggestion; + + if (showForkSuggestion) { this.setForkTarget(target); - return; + } else { + visitUrl(isIdeTarget ? ideEditPath : editBlobPath); } - - const { ideEditPath, editBlobPath } = this.blobInfo; - visitUrl(target === 'ide' ? ideEditPath : editBlobPath); }, setForkTarget(target) { this.forkTarget = target; @@ -312,6 +323,7 @@ export default { :show-path="false" :override-copy="true" :show-fork-suggestion="showForkSuggestion" + :show-web-ide-fork-suggestion="showWebIdeForkSuggestion" :show-blame-toggle="true" :project-path="projectPath" :project-id="projectId" diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue index 0f7307d0b51d91..096ba7c00e840a 100644 --- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -57,6 +57,11 @@ export default { required: false, default: false, }, + needsToForkWithWebIde: { + type: Boolean, + required: false, + default: false, + }, gitpodEnabled: { type: Boolean, required: false, @@ -221,7 +226,7 @@ export default { webIdeAction() { if (!this.showWebIdeButton) return null; - const handleOptions = this.needsToFork + const handleOptions = this.needsToForkWithWebIde ? { handle: () => { if (this.disableForkModal) { diff --git a/app/graphql/queries/repository/blob_info.query.graphql b/app/graphql/queries/repository/blob_info.query.graphql index 13966d022a9342..e320d8701903aa 100644 --- a/app/graphql/queries/repository/blob_info.query.graphql +++ b/app/graphql/queries/repository/blob_info.query.graphql @@ -36,6 +36,7 @@ query getBlobInfo( environmentFormattedExternalUrl environmentExternalUrlForRouteMap canModifyBlob + canModifyBlobWithWebIde canCurrentUserPushToBranch archived storedExternally diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index a5c9d6940ceb6a..6698ba226875a0 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -127,6 +127,9 @@ class BlobType < BaseObject calls_gitaly: true, description: 'Whether the current user can modify the blob.' + field :can_modify_blob_with_web_ide, GraphQL::Types::Boolean, null: true, method: :can_modify_blob_with_web_ide?, + description: 'Whether the current user can modify the blob with WebIDE.' + field :can_current_user_push_to_branch, GraphQL::Types::Boolean, null: true, method: :can_current_user_push_to_branch?, description: 'Whether the current user can push to the branch.' diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 117b2ea6f80d56..d4c3cb5b5de79a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -76,10 +76,17 @@ def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) ) end + # Used for single file Web Editor, Delete and Replace UI actions. + # can_edit_tree checks if ref is on top of the branch. def can_modify_blob?(blob, project = @project, ref = @ref) !blob.stored_externally? && can_edit_tree?(project, ref) end + # Used for WebIDE editor where editing is possible even if ref is not on top of the branch. + def can_modify_blob_with_web_ide?(blob, project = @project) + !blob.stored_externally? && can_collaborate_with_project?(project) + end + def leave_edit_message _("Leave edit mode? All unsaved changes will be lost.") end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index 778043955198f2..dcaf9de19978ee 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -140,6 +140,10 @@ def can_modify_blob? super(blob, project, commit_id) end + def can_modify_blob_with_web_ide? + super(blob, project) + end + def can_current_user_push_to_branch? return false unless current_user && project.repository.branch_exists?(commit_id) diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 1249fe42b7a857..af28f589b1e04c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -30753,6 +30753,7 @@ Returns [`RepositoryCodeownerValidation`](#repositorycodeownervalidation). | `blamePath` | [`String`](#string) | Web path to blob blame page. | | `canCurrentUserPushToBranch` | [`Boolean`](#boolean) | Whether the current user can push to the branch. | | `canModifyBlob` | [`Boolean`](#boolean) | Whether the current user can modify the blob. | +| `canModifyBlobWithWebIde` | [`Boolean`](#boolean) | Whether the current user can modify the blob with WebIDE. | | `codeNavigationPath` | [`String`](#string) | Web path for code navigation. | | `codeOwners` | [`[UserCore!]`](#usercore) | List of code owners for the blob. | | `editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. | diff --git a/spec/frontend/blob/components/blob_header_spec.js b/spec/frontend/blob/components/blob_header_spec.js index e7b2ee7494049a..3faa6e18d05ac4 100644 --- a/spec/frontend/blob/components/blob_header_spec.js +++ b/spec/frontend/blob/components/blob_header_spec.js @@ -75,13 +75,15 @@ describe('Blob Header Default Actions', () => { it('renders the WebIdeLink component with the correct props', async () => { const { ideEditPath, editBlobPath, gitpodBlobUrl, pipelineEditorPath } = Blob; const showForkSuggestion = false; - await createComponent({ propsData: { showForkSuggestion } }); + const showWebIdeForkSuggestion = false; + await createComponent({ propsData: { showForkSuggestion, showWebIdeForkSuggestion } }); expect(findWebIdeLink().props()).toMatchObject({ showEditButton: true, editUrl: editBlobPath, webIdeUrl: ideEditPath, needsToFork: showForkSuggestion, + needsToForkWithWebIde: showWebIdeForkSuggestion, showPipelineEditorButton: Boolean(pipelineEditorPath), pipelineEditorUrl: pipelineEditorPath, gitpodUrl: gitpodBlobUrl, diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 866655bb2f297c..10ad9d7647927e 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -574,6 +574,38 @@ describe('Blob content viewer component', () => { expect(findForkSuggestion().exists()).toBe(showForkSuggestion); }, ); + + it.each` + loggedIn | canModifyBlobWithWebIde | createMergeRequestIn | forkProject | showWebIdeForkSuggestion + ${true} | ${false} | ${true} | ${true} | ${true} + ${false} | ${false} | ${true} | ${true} | ${false} + ${true} | ${true} | ${false} | ${true} | ${false} + ${true} | ${true} | ${true} | ${false} | ${false} + `( + 'shows/hides a fork suggestion for WebIDE according to a set of conditions', + async ({ + loggedIn, + canModifyBlobWithWebIde, + createMergeRequestIn, + forkProject, + showWebIdeForkSuggestion, + }) => { + isLoggedIn.mockReturnValueOnce(loggedIn); + await createComponent( + { + blob: { ...simpleViewerMock, canModifyBlobWithWebIde }, + createMergeRequestIn, + forkProject, + }, + mount, + ); + + findBlobHeader().vm.$emit('edit', 'ide'); + await nextTick(); + + expect(findForkSuggestion().exists()).toBe(showWebIdeForkSuggestion); + }, + ); }); describe('active viewer based on plain attribute', () => { diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index a5d374c234efe1..aa31a129769514 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -21,6 +21,7 @@ export const simpleViewerMock = { environmentFormattedExternalUrl: '', environmentExternalUrlForRouteMap: '', canModifyBlob: true, + canModifyBlobWithWebIde: true, canCurrentUserPushToBranch: true, archived: false, storedExternally: false, diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js index 84ecbb431ac404..6bf4cd99aa4c27 100644 --- a/spec/frontend/vue_shared/components/web_ide_link_spec.js +++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js @@ -173,7 +173,7 @@ describe('vue_shared/components/web_ide_link', () => { expectedActions: [ACTION_WEB_IDE_EDIT_FORK, ACTION_EDIT], }, { - props: { needsToFork: true }, + props: { needsToFork: true, needsToForkWithWebIde: true }, expectedActions: [ACTION_WEB_IDE_CONFIRM_FORK, ACTION_EDIT_CONFIRM_FORK], }, { @@ -342,7 +342,12 @@ describe('vue_shared/components/web_ide_link', () => { it.each(testActions)( 'emits the correct event when an action handler is called', ({ props, expectedEventPayload }) => { - createComponent({ ...props, needsToFork: true, disableForkModal: true }); + createComponent({ + ...props, + needsToFork: true, + needsToForkWithWebIde: true, + disableForkModal: true, + }); findDisclosureDropdownItems().at(0).props().item.handle(); @@ -351,7 +356,7 @@ describe('vue_shared/components/web_ide_link', () => { ); it.each(testActions)('renders the fork confirmation modal', ({ props }) => { - createComponent({ ...props, needsToFork: true }); + createComponent({ ...props, needsToFork: true, needsToForkWithWebIde: true }); expect(findForkConfirmModal().exists()).toBe(true); expect(findForkConfirmModal().props()).toEqual({ @@ -362,7 +367,10 @@ describe('vue_shared/components/web_ide_link', () => { }); it.each(testActions)('opens the modal when the button is clicked', async ({ props }) => { - createComponent({ ...props, needsToFork: true }, { mountFn: mountExtended }); + createComponent( + { ...props, needsToFork: true, needsToForkWithWebIde: true }, + { mountFn: mountExtended }, + ); findDisclosureDropdownItems().at(0).props().item.handle(); diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb index 1c27b6fca503f0..a76c51a92dcfdc 100644 --- a/spec/graphql/types/repository/blob_type_spec.rb +++ b/spec/graphql/types/repository/blob_type_spec.rb @@ -42,6 +42,7 @@ :rich_viewer, :plain_data, :can_modify_blob, + :can_modify_blob_with_web_ide, :can_current_user_push_to_branch, :archived, :ide_edit_path, diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index e6c8b63f2346cb..f0d50f81fd1ac9 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -119,6 +119,32 @@ end end + describe '#can_modify_blob_with_web_ide?' do + context 'when blob is store externally' do + before do + allow(blob).to receive(:stored_externally?).and_return(true) + end + + it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey } + end + + context 'when user can collaborate with the project' do + before do + allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(true) + end + + it { expect(presenter.can_modify_blob?).to be_truthy } + end + + context 'when user cannot collaborate with the project' do + before do + allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(false) + end + + it { expect(presenter.can_modify_blob?).to be_falsey } + end + end + describe '#can_current_user_push_to_branch?' do context 'when ref is a branch' do let(:ref) { 'feature' } -- GitLab From f337888385da172c48884e2c78fda38ab1cdbb62 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Thu, 18 Jul 2024 17:17:06 +0200 Subject: [PATCH 2/7] Improve test cases setup --- spec/presenters/blob_presenter_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index f0d50f81fd1ac9..8bd815914330ca 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -130,18 +130,20 @@ context 'when user can collaborate with the project' do before do + allow(blob).to receive(:stored_externally?).and_return(false) allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(true) end - it { expect(presenter.can_modify_blob?).to be_truthy } + it { expect(presenter.can_modify_blob_with_web_ide?).to be_truthy } end context 'when user cannot collaborate with the project' do before do + allow(blob).to receive(:stored_externally?).and_return(false) allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(false) end - it { expect(presenter.can_modify_blob?).to be_falsey } + it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey } end end -- GitLab From 3449be9e7527aded980461f65f3d87ad44d3ed7f Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Wed, 24 Jul 2024 15:06:00 +0200 Subject: [PATCH 3/7] Add condition encapsulating both fork suggestions --- .../repository/components/blob_content_viewer.vue | 5 ++++- .../repository/components/blob_content_viewer_spec.js | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index a0ffa08f320e1b..4139af0a1f77f9 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -179,7 +179,7 @@ export default { return this.isLoggedIn && !this.isUsingLfs && createMergeRequestIn && forkProject; }, - showForkSuggestion() { + showSingleFileEditorForkSuggestion() { const { canModifyBlob } = this.blobInfo; return this.canFork && !canModifyBlob; @@ -189,6 +189,9 @@ export default { return this.canFork && !canModifyBlobWithWebIde; }, + showForkSuggestion() { + return this.showSingleFileEditorForkSuggestion || this.showWebIdeForkSuggestion; + }, forkPath() { const forkPaths = { ide: this.blobInfo.ideForkAndEditPath, diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 10ad9d7647927e..b667c28be7789d 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -544,7 +544,7 @@ describe('Blob content viewer component', () => { }); it.each` - loggedIn | canModifyBlob | createMergeRequestIn | forkProject | showForkSuggestion + loggedIn | canModifyBlob | createMergeRequestIn | forkProject | showSingleFileEditorForkSuggestion ${true} | ${false} | ${true} | ${true} | ${true} ${false} | ${false} | ${true} | ${true} | ${false} ${true} | ${true} | ${false} | ${true} | ${false} @@ -556,7 +556,7 @@ describe('Blob content viewer component', () => { canModifyBlob, createMergeRequestIn, forkProject, - showForkSuggestion, + showSingleFileEditorForkSuggestion, }) => { isLoggedIn.mockReturnValueOnce(loggedIn); await createComponent( @@ -571,7 +571,7 @@ describe('Blob content viewer component', () => { findBlobHeader().vm.$emit('edit', 'simple'); await nextTick(); - expect(findForkSuggestion().exists()).toBe(showForkSuggestion); + expect(findForkSuggestion().exists()).toBe(showSingleFileEditorForkSuggestion); }, ); -- GitLab From d3120b4c9264bee58a899a0b11680c8af255dbf4 Mon Sep 17 00:00:00 2001 From: Paulina Sedlak-Jakubowska Date: Thu, 25 Jul 2024 09:34:14 +0000 Subject: [PATCH 4/7] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Marcin Sedlak-Jakubowski --- app/graphql/types/repository/blob_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 6698ba226875a0..eefedcd27a3572 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -128,7 +128,7 @@ class BlobType < BaseObject description: 'Whether the current user can modify the blob.' field :can_modify_blob_with_web_ide, GraphQL::Types::Boolean, null: true, method: :can_modify_blob_with_web_ide?, - description: 'Whether the current user can modify the blob with WebIDE.' + description: 'Whether the current user can modify the blob with Web IDE.' field :can_current_user_push_to_branch, GraphQL::Types::Boolean, null: true, method: :can_current_user_push_to_branch?, description: 'Whether the current user can push to the branch.' -- GitLab From fe0f4f57ba93264b20579dbdcc23796ed2a59b66 Mon Sep 17 00:00:00 2001 From: Paulina Sedlak-Jakubowska Date: Thu, 25 Jul 2024 09:55:43 +0000 Subject: [PATCH 5/7] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Lukas Wanko --- spec/presenters/blob_presenter_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index 8bd815914330ca..60b3c3b840a80e 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -120,7 +120,7 @@ end describe '#can_modify_blob_with_web_ide?' do - context 'when blob is store externally' do + context 'when blob is stored externally' do before do allow(blob).to receive(:stored_externally?).and_return(true) end -- GitLab From c1ec5f5d0f6b49c88c2e7df443fbc8446a5a3612 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Thu, 25 Jul 2024 16:51:51 +0200 Subject: [PATCH 6/7] Apply reviewer suggestions --- .../components/blob_content_viewer.vue | 20 ++++++++----- app/graphql/types/repository/blob_type.rb | 2 +- doc/api/graphql/reference/index.md | 2 +- .../components/blob_content_viewer_spec.js | 30 +++++++++++-------- spec/presenters/blob_presenter_spec.rb | 17 +++++------ 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 4139af0a1f77f9..cf67fb44c1ffe7 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -275,14 +275,20 @@ export default { if (this.$route?.query?.plain === plain) return; this.$router.push({ path: this.$route.path, query: { ...this.$route.query, plain } }); }, + isIdeTarget(target) { + return target === 'ide'; + }, + forkSuggestionForSelectedEditor(target) { + return this.isIdeTarget(target) + ? this.showWebIdeForkSuggestion + : this.showSingleFileEditorForkSuggestion; + }, editBlob(target) { const { ideEditPath, editBlobPath } = this.blobInfo; - const isIdeTarget = target === 'ide'; - const showForkSuggestion = isIdeTarget - ? this.showWebIdeForkSuggestion - : this.showForkSuggestion; + const isIdeTarget = this.isIdeTarget(target); + const showForkSuggestionForSelectedEditor = this.forkSuggestionForSelectedEditor(target); - if (showForkSuggestion) { + if (showForkSuggestionForSelectedEditor) { this.setForkTarget(target); } else { visitUrl(isIdeTarget ? ideEditPath : editBlobPath); @@ -325,7 +331,7 @@ export default { :has-render-error="hasRenderError" :show-path="false" :override-copy="true" - :show-fork-suggestion="showForkSuggestion" + :show-fork-suggestion="showSingleFileEditorForkSuggestion" :show-web-ide-fork-suggestion="showWebIdeForkSuggestion" :show-blame-toggle="true" :project-path="projectPath" @@ -349,7 +355,7 @@ export default { :project-path="projectPath" :is-locked="Boolean(pathLockedByUser)" :can-lock="canLock" - :show-fork-suggestion="showForkSuggestion" + :show-fork-suggestion="showSingleFileEditorForkSuggestion" :is-using-lfs="isUsingLfs" @fork="setForkTarget('view')" /> diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index eefedcd27a3572..300dab726d4962 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -127,7 +127,7 @@ class BlobType < BaseObject calls_gitaly: true, description: 'Whether the current user can modify the blob.' - field :can_modify_blob_with_web_ide, GraphQL::Types::Boolean, null: true, method: :can_modify_blob_with_web_ide?, + field :can_modify_blob_with_web_ide, GraphQL::Types::Boolean, null: false, method: :can_modify_blob_with_web_ide?, description: 'Whether the current user can modify the blob with Web IDE.' field :can_current_user_push_to_branch, GraphQL::Types::Boolean, null: true, method: :can_current_user_push_to_branch?, diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index af28f589b1e04c..1dfcb694fc082b 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -30753,7 +30753,7 @@ Returns [`RepositoryCodeownerValidation`](#repositorycodeownervalidation). | `blamePath` | [`String`](#string) | Web path to blob blame page. | | `canCurrentUserPushToBranch` | [`Boolean`](#boolean) | Whether the current user can push to the branch. | | `canModifyBlob` | [`Boolean`](#boolean) | Whether the current user can modify the blob. | -| `canModifyBlobWithWebIde` | [`Boolean`](#boolean) | Whether the current user can modify the blob with WebIDE. | +| `canModifyBlobWithWebIde` | [`Boolean!`](#boolean) | Whether the current user can modify the blob with Web IDE. | | `codeNavigationPath` | [`String`](#string) | Web path for code navigation. | | `codeOwners` | [`[UserCore!]`](#usercore) | List of code owners for the blob. | | `editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. | diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index b667c28be7789d..078b11a91a9683 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -544,16 +544,19 @@ describe('Blob content viewer component', () => { }); it.each` - loggedIn | canModifyBlob | createMergeRequestIn | forkProject | showSingleFileEditorForkSuggestion - ${true} | ${false} | ${true} | ${true} | ${true} - ${false} | ${false} | ${true} | ${true} | ${false} - ${true} | ${true} | ${false} | ${true} | ${false} - ${true} | ${true} | ${true} | ${false} | ${false} + loggedIn | canModifyBlob | isUsingLfs | createMergeRequestIn | forkProject | showSingleFileEditorForkSuggestion + ${true} | ${true} | ${false} | ${true} | ${true} | ${false} + ${true} | ${false} | ${false} | ${true} | ${true} | ${true} + ${false} | ${false} | ${false} | ${true} | ${true} | ${false} + ${true} | ${false} | ${false} | ${false} | ${true} | ${false} + ${true} | ${false} | ${false} | ${true} | ${false} | ${false} + ${true} | ${false} | ${true} | ${true} | ${true} | ${false} `( 'shows/hides a fork suggestion according to a set of conditions', async ({ loggedIn, canModifyBlob, + isUsingLfs, createMergeRequestIn, forkProject, showSingleFileEditorForkSuggestion, @@ -561,7 +564,7 @@ describe('Blob content viewer component', () => { isLoggedIn.mockReturnValueOnce(loggedIn); await createComponent( { - blob: { ...simpleViewerMock, canModifyBlob }, + blob: { ...simpleViewerMock, canModifyBlob, storedExternally: isUsingLfs }, createMergeRequestIn, forkProject, }, @@ -576,16 +579,19 @@ describe('Blob content viewer component', () => { ); it.each` - loggedIn | canModifyBlobWithWebIde | createMergeRequestIn | forkProject | showWebIdeForkSuggestion - ${true} | ${false} | ${true} | ${true} | ${true} - ${false} | ${false} | ${true} | ${true} | ${false} - ${true} | ${true} | ${false} | ${true} | ${false} - ${true} | ${true} | ${true} | ${false} | ${false} + loggedIn | canModifyBlobWithWebIde | isUsingLfs | createMergeRequestIn | forkProject | showWebIdeForkSuggestion + ${true} | ${true} | ${false} | ${true} | ${true} | ${false} + ${true} | ${false} | ${false} | ${true} | ${true} | ${true} + ${false} | ${false} | ${false} | ${true} | ${true} | ${false} + ${true} | ${false} | ${false} | ${false} | ${true} | ${false} + ${true} | ${false} | ${false} | ${true} | ${false} | ${false} + ${true} | ${false} | ${true} | ${true} | ${true} | ${false} `( 'shows/hides a fork suggestion for WebIDE according to a set of conditions', async ({ loggedIn, canModifyBlobWithWebIde, + isUsingLfs, createMergeRequestIn, forkProject, showWebIdeForkSuggestion, @@ -593,7 +599,7 @@ describe('Blob content viewer component', () => { isLoggedIn.mockReturnValueOnce(loggedIn); await createComponent( { - blob: { ...simpleViewerMock, canModifyBlobWithWebIde }, + blob: { ...simpleViewerMock, canModifyBlobWithWebIde, storedExternally: isUsingLfs }, createMergeRequestIn, forkProject, }, diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index 60b3c3b840a80e..128f3facdac643 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -120,6 +120,13 @@ end describe '#can_modify_blob_with_web_ide?' do + before do + allow(blob).to receive(:stored_externally?).and_return(false) + allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(false) + end + + it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey } + context 'when blob is stored externally' do before do allow(blob).to receive(:stored_externally?).and_return(true) @@ -130,21 +137,11 @@ context 'when user can collaborate with the project' do before do - allow(blob).to receive(:stored_externally?).and_return(false) allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(true) end it { expect(presenter.can_modify_blob_with_web_ide?).to be_truthy } end - - context 'when user cannot collaborate with the project' do - before do - allow(blob).to receive(:stored_externally?).and_return(false) - allow(presenter).to receive(:can_collaborate_with_project?).with(project).and_return(false) - end - - it { expect(presenter.can_modify_blob_with_web_ide?).to be_falsey } - end end describe '#can_current_user_push_to_branch?' do -- GitLab From 13ed3f148d15eace2853c9ba326b032ffffd31a1 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Mon, 29 Jul 2024 09:55:01 +0200 Subject: [PATCH 7/7] Update mock for blobInfo --- ee/spec/frontend/vulnerabilities/mock_data.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/spec/frontend/vulnerabilities/mock_data.js b/ee/spec/frontend/vulnerabilities/mock_data.js index 22fbf1ed834c0a..fbf8abfbdcd269 100644 --- a/ee/spec/frontend/vulnerabilities/mock_data.js +++ b/ee/spec/frontend/vulnerabilities/mock_data.js @@ -187,6 +187,7 @@ export const TEST_ALL_BLOBS_INFO_GRAPHQL_SUCCESS_RESPONSE = { environmentFormattedExternalUrl: null, environmentExternalUrlForRouteMap: null, canModifyBlob: false, + canModifyBlobWithWebIde: false, canCurrentUserPushToBranch: false, archived: false, storedExternally: null, -- GitLab