Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
71752eae
Commit
71752eae
authored
Oct 07, 2021
by
Jacques Erasmus
Committed by
Peter Hegman
Oct 07, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Display fork suggestion when editing blob
Displays a fork suggestion when user should fork before editing
parent
e9735782
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
168 additions
and
11 deletions
+168
-11
app/assets/javascripts/repository/components/blob_content_viewer.vue
...javascripts/repository/components/blob_content_viewer.vue
+39
-1
app/assets/javascripts/repository/components/blob_edit.vue
app/assets/javascripts/repository/components/blob_edit.vue
+14
-2
app/assets/javascripts/repository/queries/blob_info.query.graphql
...ts/javascripts/repository/queries/blob_info.query.graphql
+5
-0
app/assets/javascripts/vue_shared/components/web_ide_link.vue
...assets/javascripts/vue_shared/components/web_ide_link.vue
+8
-2
spec/frontend/repository/components/blob_content_viewer_spec.js
...rontend/repository/components/blob_content_viewer_spec.js
+77
-3
spec/frontend/repository/components/blob_edit_spec.js
spec/frontend/repository/components/blob_edit_spec.js
+3
-3
spec/frontend/vue_shared/components/web_ide_link_spec.js
spec/frontend/vue_shared/components/web_ide_link_spec.js
+22
-0
No files found.
app/assets/javascripts/repository/components/blob_content_viewer.vue
View file @
71752eae
...
...
@@ -8,10 +8,12 @@ import createFlash from '~/flash';
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
isLoggedIn
}
from
'
~/lib/utils/common_utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
getRefMixin
from
'
../mixins/get_ref
'
;
import
blobInfoQuery
from
'
../queries/blob_info.query.graphql
'
;
import
BlobButtonGroup
from
'
./blob_button_group.vue
'
;
import
BlobEdit
from
'
./blob_edit.vue
'
;
import
ForkSuggestion
from
'
./fork_suggestion.vue
'
;
import
{
loadViewer
,
viewerProps
}
from
'
./blob_viewers
'
;
export
default
{
...
...
@@ -21,6 +23,7 @@ export default {
BlobButtonGroup
,
BlobContent
,
GlLoadingIcon
,
ForkSuggestion
,
},
mixins
:
[
getRefMixin
],
inject
:
{
...
...
@@ -65,6 +68,7 @@ export default {
},
data
()
{
return
{
forkTarget
:
null
,
legacyRichViewer
:
null
,
legacySimpleViewer
:
null
,
isBinary
:
false
,
...
...
@@ -74,6 +78,8 @@ export default {
userPermissions
:
{
pushCode
:
false
,
downloadCode
:
false
,
createMergeRequestIn
:
false
,
forkProject
:
false
,
},
pathLocks
:
{
nodes
:
[],
...
...
@@ -92,12 +98,14 @@ export default {
path
:
''
,
editBlobPath
:
''
,
ideEditPath
:
''
,
forkAndEditPath
:
''
,
ideForkAndEditPath
:
''
,
storedExternally
:
false
,
canModifyBlob
:
false
,
rawPath
:
''
,
externalStorageUrl
:
''
,
replacePath
:
''
,
deletePath
:
''
,
forkPath
:
''
,
simpleViewer
:
{},
richViewer
:
null
,
webPath
:
''
,
...
...
@@ -149,6 +157,17 @@ export default {
isLocked
()
{
return
this
.
project
.
pathLocks
.
nodes
.
some
((
node
)
=>
node
.
path
===
this
.
path
);
},
showForkSuggestion
()
{
const
{
createMergeRequestIn
,
forkProject
}
=
this
.
project
.
userPermissions
;
const
{
canModifyBlob
}
=
this
.
blobInfo
;
return
this
.
isLoggedIn
&&
!
canModifyBlob
&&
createMergeRequestIn
&&
forkProject
;
},
forkPath
()
{
return
this
.
forkTarget
===
'
ide
'
?
this
.
blobInfo
.
ideForkAndEditPath
:
this
.
blobInfo
.
forkAndEditPath
;
},
},
methods
:
{
loadLegacyViewer
(
type
)
{
...
...
@@ -187,6 +206,18 @@ export default {
this
.
loadLegacyViewer
(
this
.
activeViewerType
);
}
},
editBlob
(
target
)
{
if
(
this
.
showForkSuggestion
)
{
this
.
setForkTarget
(
target
);
return
;
}
const
{
ideEditPath
,
editBlobPath
}
=
this
.
blobInfo
;
redirectTo
(
target
===
'
ide
'
?
ideEditPath
:
editBlobPath
);
},
setForkTarget
(
target
)
{
this
.
forkTarget
=
target
;
},
},
};
</
script
>
...
...
@@ -208,6 +239,8 @@ export default {
:show-edit-button=
"!isBinaryFileType"
:edit-path=
"blobInfo.editBlobPath"
:web-ide-path=
"blobInfo.ideEditPath"
:needs-to-fork=
"showForkSuggestion"
@
edit=
"editBlob"
/>
<blob-button-group
v-if=
"isLoggedIn"
...
...
@@ -223,6 +256,11 @@ export default {
/>
</
template
>
</blob-header>
<fork-suggestion
v-if=
"forkTarget && showForkSuggestion"
:fork-path=
"forkPath"
@
cancel=
"setForkTarget(null)"
/>
<blob-content
v-if=
"!blobViewer"
:rich-viewer=
"legacyRichViewer"
...
...
app/assets/javascripts/repository/components/blob_edit.vue
View file @
71752eae
...
...
@@ -27,6 +27,16 @@ export default {
type
:
String
,
required
:
true
,
},
needsToFork
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
methods
:
{
onEdit
(
target
)
{
this
.
$emit
(
'
edit
'
,
target
);
},
},
};
</
script
>
...
...
@@ -38,7 +48,9 @@ export default {
class=
"gl-mr-3"
:edit-url=
"editPath"
:web-ide-url=
"webIdePath"
:needs-to-fork=
"needsToFork"
:is-blob=
"true"
@
edit=
"onEdit"
/>
<div
v-else
>
<gl-button
...
...
@@ -46,8 +58,8 @@ export default {
class=
"gl-mr-2"
category=
"primary"
variant=
"confirm"
:href=
"editPath"
data-testid=
"edit"
@
click=
"onEdit('simple')"
>
{{
$options
.
i18n
.
edit
}}
</gl-button>
...
...
@@ -56,8 +68,8 @@ export default {
class=
"gl-mr-3"
category=
"primary"
variant=
"confirm"
:href=
"webIdePath"
data-testid=
"web-ide"
@
click=
"onEdit('ide')"
>
{{
$options
.
i18n
.
webIde
}}
</gl-button>
...
...
app/assets/javascripts/repository/queries/blob_info.query.graphql
View file @
71752eae
...
...
@@ -4,6 +4,8 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
userPermissions
{
pushCode
downloadCode
createMergeRequestIn
forkProject
}
pathLocks
{
nodes
{
...
...
@@ -23,6 +25,9 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
path
editBlobPath
ideEditPath
forkAndEditPath
ideForkAndEditPath
canModifyBlob
storedExternally
rawPath
replacePath
...
...
app/assets/javascripts/vue_shared/components/web_ide_link.vue
View file @
71752eae
...
...
@@ -92,7 +92,10 @@ export default {
const
handleOptions
=
this
.
needsToFork
?
{
href
:
'
#modal-confirm-fork-edit
'
,
handle
:
()
=>
this
.
showModal
(
'
#modal-confirm-fork-edit
'
),
handle
:
()
=>
{
this
.
$emit
(
'
edit
'
,
'
simple
'
);
this
.
showModal
(
'
#modal-confirm-fork-edit
'
);
},
}
:
{
href
:
this
.
editUrl
};
...
...
@@ -128,7 +131,10 @@ export default {
const
handleOptions
=
this
.
needsToFork
?
{
href
:
'
#modal-confirm-fork-webide
'
,
handle
:
()
=>
this
.
showModal
(
'
#modal-confirm-fork-webide
'
),
handle
:
()
=>
{
this
.
$emit
(
'
edit
'
,
'
ide
'
);
this
.
showModal
(
'
#modal-confirm-fork-webide
'
);
},
}
:
{
href
:
this
.
webIdeUrl
};
...
...
spec/frontend/repository/components/blob_content_viewer_spec.js
View file @
71752eae
...
...
@@ -11,13 +11,18 @@ import BlobHeader from '~/blob/components/blob_header.vue';
import
BlobButtonGroup
from
'
~/repository/components/blob_button_group.vue
'
;
import
BlobContentViewer
from
'
~/repository/components/blob_content_viewer.vue
'
;
import
BlobEdit
from
'
~/repository/components/blob_edit.vue
'
;
import
ForkSuggestion
from
'
~/repository/components/fork_suggestion.vue
'
;
import
{
loadViewer
,
viewerProps
}
from
'
~/repository/components/blob_viewers
'
;
import
DownloadViewer
from
'
~/repository/components/blob_viewers/download_viewer.vue
'
;
import
EmptyViewer
from
'
~/repository/components/blob_viewers/empty_viewer.vue
'
;
import
TextViewer
from
'
~/repository/components/blob_viewers/text_viewer.vue
'
;
import
blobInfoQuery
from
'
~/repository/queries/blob_info.query.graphql
'
;
import
{
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
{
isLoggedIn
}
from
'
~/lib/utils/common_utils
'
;
jest
.
mock
(
'
~/repository/components/blob_viewers
'
);
jest
.
mock
(
'
~/lib/utils/url_utility
'
);
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
let
wrapper
;
let
mockResolver
;
...
...
@@ -34,12 +39,14 @@ const simpleMockData = {
webPath
:
'
some_file.js
'
,
editBlobPath
:
'
some_file.js/edit
'
,
ideEditPath
:
'
some_file.js/ide/edit
'
,
forkAndEditPath
:
'
some_file.js/fork/edit
'
,
ideForkAndEditPath
:
'
some_file.js/fork/ide
'
,
canModifyBlob
:
true
,
storedExternally
:
false
,
rawPath
:
'
some_file.js
'
,
externalStorageUrl
:
'
some_file.js
'
,
replacePath
:
'
some_file.js/replace
'
,
deletePath
:
'
some_file.js/delete
'
,
forkPath
:
'
some_file.js/fork
'
,
simpleViewer
:
{
fileType
:
'
text
'
,
tooLarge
:
false
,
...
...
@@ -62,6 +69,8 @@ const projectMockData = {
userPermissions
:
{
pushCode
:
true
,
downloadCode
:
true
,
createMergeRequestIn
:
true
,
forkProject
:
true
,
},
repository
:
{
empty
:
false
,
...
...
@@ -82,6 +91,8 @@ const createComponentWithApollo = (mockData = {}, inject = {}) => {
emptyRepo
=
defaultEmptyRepo
,
canPushCode
=
defaultPushCode
,
canDownloadCode
=
defaultDownloadCode
,
createMergeRequestIn
=
projectMockData
.
userPermissions
.
createMergeRequestIn
,
forkProject
=
projectMockData
.
userPermissions
.
forkProject
,
pathLocks
=
[],
}
=
mockData
;
...
...
@@ -89,7 +100,12 @@ const createComponentWithApollo = (mockData = {}, inject = {}) => {
data
:
{
project
:
{
id
:
'
1234
'
,
userPermissions
:
{
pushCode
:
canPushCode
,
downloadCode
:
canDownloadCode
},
userPermissions
:
{
pushCode
:
canPushCode
,
downloadCode
:
canDownloadCode
,
createMergeRequestIn
,
forkProject
,
},
pathLocks
:
{
nodes
:
pathLocks
,
},
...
...
@@ -158,9 +174,11 @@ describe('Blob content viewer component', () => {
const
findBlobEdit
=
()
=>
wrapper
.
findComponent
(
BlobEdit
);
const
findBlobContent
=
()
=>
wrapper
.
findComponent
(
BlobContent
);
const
findBlobButtonGroup
=
()
=>
wrapper
.
findComponent
(
BlobButtonGroup
);
const
findForkSuggestion
=
()
=>
wrapper
.
findComponent
(
ForkSuggestion
);
beforeEach
(()
=>
{
gon
.
features
=
{
refactorTextViewer
:
true
};
isLoggedIn
.
mockReturnValue
(
true
);
});
afterEach
(()
=>
{
...
...
@@ -469,7 +487,7 @@ describe('Blob content viewer component', () => {
});
it
(
'
does not render if not logged in
'
,
async
()
=>
{
window
.
gon
.
current_user_id
=
null
;
isLoggedIn
.
mockReturnValueOnce
(
false
)
;
fullFactory
({
mockData
:
{
blobInfo
:
simpleMockData
},
...
...
@@ -513,4 +531,60 @@ describe('Blob content viewer component', () => {
);
});
});
describe
(
'
edit blob
'
,
()
=>
{
beforeEach
(()
=>
{
fullFactory
({
mockData
:
{
blobInfo
:
simpleMockData
},
stubs
:
{
BlobContent
:
true
,
BlobReplace
:
true
,
},
});
});
it
(
'
simple edit redirects to the simple editor
'
,
()
=>
{
findBlobEdit
().
vm
.
$emit
(
'
edit
'
,
'
simple
'
);
expect
(
redirectTo
).
toHaveBeenCalledWith
(
simpleMockData
.
editBlobPath
);
});
it
(
'
IDE edit redirects to the IDE editor
'
,
()
=>
{
findBlobEdit
().
vm
.
$emit
(
'
edit
'
,
'
ide
'
);
expect
(
redirectTo
).
toHaveBeenCalledWith
(
simpleMockData
.
ideEditPath
);
});
it
.
each
`
loggedIn | canModifyBlob | createMergeRequestIn | forkProject | showForkSuggestion
${
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 according to a set of conditions
'
,
async
({
loggedIn
,
canModifyBlob
,
createMergeRequestIn
,
forkProject
,
showForkSuggestion
,
})
=>
{
isLoggedIn
.
mockReturnValueOnce
(
loggedIn
);
fullFactory
({
mockData
:
{
blobInfo
:
{
...
simpleMockData
,
canModifyBlob
},
project
:
{
userPermissions
:
{
createMergeRequestIn
,
forkProject
}
},
},
stubs
:
{
BlobContent
:
true
,
BlobButtonGroup
:
true
,
},
});
findBlobEdit
().
vm
.
$emit
(
'
edit
'
,
'
simple
'
);
await
nextTick
();
expect
(
findForkSuggestion
().
exists
()).
toBe
(
showForkSuggestion
);
},
);
});
});
spec/frontend/repository/components/blob_edit_spec.js
View file @
71752eae
...
...
@@ -7,6 +7,7 @@ const DEFAULT_PROPS = {
editPath
:
'
some_file.js/edit
'
,
webIdePath
:
'
some_file.js/ide/edit
'
,
showEditButton
:
true
,
needsToFork
:
false
,
};
describe
(
'
BlobEdit component
'
,
()
=>
{
...
...
@@ -56,7 +57,6 @@ describe('BlobEdit component', () => {
it
(
'
renders the Edit button
'
,
()
=>
{
createComponent
();
expect
(
findEditButton
().
attributes
(
'
href
'
)).
toBe
(
DEFAULT_PROPS
.
editPath
);
expect
(
findEditButton
().
text
()).
toBe
(
'
Edit
'
);
expect
(
findEditButton
()).
not
.
toBeDisabled
();
});
...
...
@@ -64,7 +64,6 @@ describe('BlobEdit component', () => {
it
(
'
renders the Web IDE button
'
,
()
=>
{
createComponent
();
expect
(
findWebIdeButton
().
attributes
(
'
href
'
)).
toBe
(
DEFAULT_PROPS
.
webIdePath
);
expect
(
findWebIdeButton
().
text
()).
toBe
(
'
Web IDE
'
);
expect
(
findWebIdeButton
()).
not
.
toBeDisabled
();
});
...
...
@@ -72,13 +71,14 @@ describe('BlobEdit component', () => {
it
(
'
renders WebIdeLink component
'
,
()
=>
{
createComponent
(
true
);
const
{
editPath
:
editUrl
,
webIdePath
:
webIdeUrl
}
=
DEFAULT_PROPS
;
const
{
editPath
:
editUrl
,
webIdePath
:
webIdeUrl
,
needsToFork
}
=
DEFAULT_PROPS
;
expect
(
findWebIdeLink
().
props
()).
toMatchObject
({
editUrl
,
webIdeUrl
,
isBlob
:
true
,
showEditButton
:
true
,
needsToFork
,
});
});
...
...
spec/frontend/vue_shared/components/web_ide_link_spec.js
View file @
71752eae
...
...
@@ -160,4 +160,26 @@ describe('Web IDE link component', () => {
expect
(
findLocalStorageSync
().
props
(
'
value
'
)).
toBe
(
ACTION_GITPOD
.
key
);
});
});
describe
(
'
edit actions
'
,
()
=>
{
it
.
each
([
{
props
:
{
showWebIdeButton
:
true
,
showEditButton
:
false
},
expectedEventPayload
:
'
ide
'
,
},
{
props
:
{
showWebIdeButton
:
false
,
showEditButton
:
true
},
expectedEventPayload
:
'
simple
'
,
},
])(
'
emits the correct event when an action handler is called
'
,
async
({
props
,
expectedEventPayload
})
=>
{
createComponent
({
...
props
,
needsToFork
:
true
});
findActionsButton
().
props
(
'
actions
'
)[
0
].
handle
();
expect
(
wrapper
.
emitted
(
'
edit
'
)).
toEqual
([[
expectedEventPayload
]]);
},
);
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment