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
0b11dd4f
Commit
0b11dd4f
authored
Jan 25, 2021
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature/file-review-code' into 'master'
Mark files as viewed See merge request gitlab-org/gitlab!51513
parents
069d0b03
dc2558cc
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
432 additions
and
134 deletions
+432
-134
app/assets/javascripts/diffs/components/app.vue
app/assets/javascripts/diffs/components/app.vue
+5
-6
app/assets/javascripts/diffs/components/diff_file.vue
app/assets/javascripts/diffs/components/diff_file.vue
+14
-0
app/assets/javascripts/diffs/components/diff_file_header.vue
app/assets/javascripts/diffs/components/diff_file_header.vue
+61
-3
app/assets/javascripts/diffs/i18n.js
app/assets/javascripts/diffs/i18n.js
+2
-0
app/assets/javascripts/diffs/store/actions.js
app/assets/javascripts/diffs/store/actions.js
+3
-5
app/assets/javascripts/diffs/store/getters.js
app/assets/javascripts/diffs/store/getters.js
+0
-5
app/assets/javascripts/diffs/store/modules/diff_state.js
app/assets/javascripts/diffs/store/modules/diff_state.js
+1
-0
app/assets/javascripts/diffs/utils/file_reviews.js
app/assets/javascripts/diffs/utils/file_reviews.js
+13
-12
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+1
-0
changelogs/unreleased/feature-file-review.yml
changelogs/unreleased/feature-file-review.yml
+5
-0
config/feature_flags/development/local_file_reviews.yml
config/feature_flags/development/local_file_reviews.yml
+8
-0
locale/gitlab.pot
locale/gitlab.pot
+6
-0
spec/frontend/diffs/components/diff_file_header_spec.js
spec/frontend/diffs/components/diff_file_header_spec.js
+229
-66
spec/frontend/diffs/components/diff_file_spec.js
spec/frontend/diffs/components/diff_file_spec.js
+50
-1
spec/frontend/diffs/store/getters_spec.js
spec/frontend/diffs/store/getters_spec.js
+0
-22
spec/frontend/diffs/utils/file_reviews_spec.js
spec/frontend/diffs/utils/file_reviews_spec.js
+34
-14
No files found.
app/assets/javascripts/diffs/components/app.vue
View file @
0b11dd4f
...
...
@@ -26,6 +26,7 @@ import CollapsedFilesWarning from './collapsed_files_warning.vue';
import
{
diffsApp
}
from
'
../utils/performance
'
;
import
{
fileByFile
}
from
'
../utils/preferences
'
;
import
{
reviewStatuses
}
from
'
../utils/file_reviews
'
;
import
{
TREE_LIST_WIDTH_STORAGE_KEY
,
...
...
@@ -169,12 +170,7 @@ export default {
'
hasConflicts
'
,
'
viewDiffsFileByFile
'
,
]),
...
mapGetters
(
'
diffs
'
,
[
'
whichCollapsedTypes
'
,
'
isParallelView
'
,
'
currentDiffIndex
'
,
'
fileReviews
'
,
]),
...
mapGetters
(
'
diffs
'
,
[
'
whichCollapsedTypes
'
,
'
isParallelView
'
,
'
currentDiffIndex
'
]),
...
mapGetters
([
'
isNotesFetched
'
,
'
getNoteableData
'
]),
diffs
()
{
if
(
!
this
.
viewDiffsFileByFile
)
{
...
...
@@ -232,6 +228,9 @@ export default {
return
visible
;
},
fileReviews
()
{
return
reviewStatuses
(
this
.
diffFiles
,
this
.
mrReviews
);
},
},
watch
:
{
commit
(
newCommit
,
oldCommit
)
{
...
...
app/assets/javascripts/diffs/components/diff_file.vue
View file @
0b11dd4f
...
...
@@ -10,7 +10,9 @@ import notesEventHub from '../../notes/event_hub';
import
DiffFileHeader
from
'
./diff_file_header.vue
'
;
import
DiffContent
from
'
./diff_content.vue
'
;
import
{
diffViewerErrors
}
from
'
~/ide/constants
'
;
import
{
collapsedType
,
isCollapsed
}
from
'
../utils/diff_file
'
;
import
{
DIFF_FILE_AUTOMATIC_COLLAPSE
,
DIFF_FILE_MANUAL_COLLAPSE
,
...
...
@@ -144,6 +146,12 @@ export default {
showContent
()
{
return
!
this
.
isCollapsed
&&
!
this
.
isFileTooLarge
;
},
showLocalFileReviews
()
{
const
loggedIn
=
Boolean
(
gon
.
current_user_id
);
const
featureOn
=
this
.
glFeatures
.
localFileReviews
;
return
loggedIn
&&
featureOn
;
},
},
watch
:
{
'
file.file_hash
'
:
{
...
...
@@ -181,6 +189,10 @@ export default {
if
(
this
.
hasDiff
)
{
this
.
postRender
();
}
if
(
this
.
reviewed
&&
!
this
.
isCollapsed
&&
this
.
showLocalFileReviews
)
{
this
.
handleToggle
();
}
},
beforeDestroy
()
{
eventHub
.
$off
(
EVT_EXPAND_ALL_FILES
,
this
.
expandAllListener
);
...
...
@@ -273,9 +285,11 @@ export default {
:can-current-user-fork=
"canCurrentUserFork"
:diff-file=
"file"
:collapsible=
"true"
:reviewed=
"reviewed"
:expanded=
"!isCollapsed"
:add-merge-request-buttons=
"true"
:view-diffs-file-by-file=
"viewDiffsFileByFile"
:show-local-file-reviews=
"showLocalFileReviews"
class=
"js-file-title file-title gl-border-1 gl-border-solid gl-border-gray-100"
:class=
"hasBodyClasses.header"
@
toggleFile=
"handleToggle"
...
...
app/assets/javascripts/diffs/components/diff_file_header.vue
View file @
0b11dd4f
<
script
>
import
{
escape
}
from
'
lodash
'
;
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
GlTooltipDirective
,
GlSafeHtmlDirective
,
...
...
@@ -10,16 +10,23 @@ import {
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
GlFormCheckbox
,
GlLoadingIcon
,
}
from
'
@gitlab/ui
'
;
import
glFeatureFlagsMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
FileIcon
from
'
~/vue_shared/components/file_icon.vue
'
;
import
{
truncateSha
}
from
'
~/lib/utils/text_utility
'
;
import
{
__
,
s__
,
sprintf
}
from
'
~/locale
'
;
import
{
diffViewerModes
}
from
'
~/ide/constants
'
;
import
DiffStats
from
'
./diff_stats.vue
'
;
import
{
scrollToElement
}
from
'
~/lib/utils/common_utils
'
;
import
{
isCollapsed
}
from
'
../utils/diff_file
'
;
import
{
collapsedType
,
isCollapsed
}
from
'
../utils/diff_file
'
;
import
{
reviewable
}
from
'
../utils/file_reviews
'
;
import
{
diffViewerModes
}
from
'
~/ide/constants
'
;
import
{
DIFF_FILE_AUTOMATIC_COLLAPSE
}
from
'
../constants
'
;
import
{
DIFF_FILE_HEADER
}
from
'
../i18n
'
;
export
default
{
...
...
@@ -33,12 +40,14 @@ export default {
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
GlFormCheckbox
,
GlLoadingIcon
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
SafeHtml
:
GlSafeHtmlDirective
,
},
mixins
:
[
glFeatureFlagsMixin
()],
i18n
:
{
...
DIFF_FILE_HEADER
,
},
...
...
@@ -76,6 +85,16 @@ export default {
required
:
false
,
default
:
false
,
},
showLocalFileReviews
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
reviewed
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
...
...
@@ -83,6 +102,7 @@ export default {
};
},
computed
:
{
...
mapState
(
'
diffs
'
,
[
'
latestDiff
'
]),
...
mapGetters
(
'
diffs
'
,
[
'
diffHasExpandedDiscussions
'
,
'
diffHasDiscussions
'
]),
diffContentIDSelector
()
{
return
`#diff-content-
${
this
.
diffFile
.
file_hash
}
`
;
...
...
@@ -170,6 +190,9 @@ export default {
(
this
.
diffFile
.
edit_path
||
this
.
diffFile
.
ide_edit_path
)
);
},
isReviewable
()
{
return
reviewable
(
this
.
diffFile
);
},
},
methods
:
{
...
mapActions
(
'
diffs
'
,
[
...
...
@@ -177,6 +200,8 @@ export default {
'
toggleFileDiscussionWrappers
'
,
'
toggleFullDiff
'
,
'
toggleActiveFileByHash
'
,
'
reviewFile
'
,
'
setFileCollapsedByUser
'
,
]),
handleToggleFile
()
{
this
.
$emit
(
'
toggleFile
'
);
...
...
@@ -204,6 +229,26 @@ export default {
setMoreActionsShown
(
val
)
{
this
.
moreActionsShown
=
val
;
},
toggleReview
(
newReviewedStatus
)
{
const
autoCollapsed
=
this
.
isCollapsed
&&
collapsedType
(
this
.
diffFile
)
===
DIFF_FILE_AUTOMATIC_COLLAPSE
;
const
open
=
this
.
expanded
;
const
closed
=
!
open
;
const
reviewed
=
newReviewedStatus
;
this
.
reviewFile
({
file
:
this
.
diffFile
,
reviewed
});
if
(
reviewed
&&
autoCollapsed
)
{
this
.
setFileCollapsedByUser
({
filePath
:
this
.
diffFile
.
file_path
,
collapsed
:
true
,
});
}
if
((
open
&&
reviewed
)
||
(
closed
&&
!
reviewed
))
{
this
.
$emit
(
'
toggleFile
'
);
}
},
},
};
</
script
>
...
...
@@ -291,6 +336,19 @@ export default {
class=
"file-actions d-flex align-items-center gl-ml-auto gl-align-self-start"
>
<diff-stats
:added-lines=
"diffFile.added_lines"
:removed-lines=
"diffFile.removed_lines"
/>
<gl-form-checkbox
v-if=
"isReviewable && showLocalFileReviews"
v-gl-tooltip
.
hover
data-testid=
"fileReviewCheckbox"
class=
"gl-mb-0"
:title=
"$options.i18n.fileReviewTooltip"
:checked=
"reviewed"
@
change=
"toggleReview"
>
<span
class=
"gl-line-height-20"
>
{{
$options
.
i18n
.
fileReviewLabel
}}
</span>
</gl-form-checkbox>
<gl-button-group
class=
"gl-pt-0!"
>
<gl-button
v-if=
"diffFile.external_url"
...
...
app/assets/javascripts/diffs/i18n.js
View file @
0b11dd4f
...
...
@@ -4,6 +4,8 @@ export const GENERIC_ERROR = __('Something went wrong on our end. Please try aga
export
const
DIFF_FILE_HEADER
=
{
optionsDropdownTitle
:
__
(
'
Options
'
),
fileReviewLabel
:
__
(
'
Viewed
'
),
fileReviewTooltip
:
__
(
'
Collapses this file (only for you) until it’s changed again.
'
),
};
export
const
DIFF_FILE
=
{
...
...
app/assets/javascripts/diffs/store/actions.js
View file @
0b11dd4f
...
...
@@ -749,12 +749,10 @@ export const setFileByFile = ({ commit }, { fileByFile }) => {
);
};
export
function
reviewFile
({
commit
,
state
,
getters
},
{
file
,
reviewed
=
true
})
{
export
function
reviewFile
({
commit
,
state
},
{
file
,
reviewed
=
true
})
{
const
{
mrPath
}
=
getDerivedMergeRequestInformation
({
endpoint
:
file
.
load_collapsed_diff_url
});
const
reviews
=
setReviewsForMergeRequest
(
mrPath
,
markFileReview
(
getters
.
fileReviews
(
state
),
file
,
reviewed
),
);
const
reviews
=
markFileReview
(
state
.
mrReviews
,
file
,
reviewed
);
setReviewsForMergeRequest
(
mrPath
,
reviews
);
commit
(
types
.
SET_MR_FILE_REVIEWS
,
reviews
);
}
app/assets/javascripts/diffs/store/getters.js
View file @
0b11dd4f
import
{
__
,
n__
}
from
'
~/locale
'
;
import
{
parallelizeDiffLines
}
from
'
./utils
'
;
import
{
isFileReviewed
}
from
'
../utils/file_reviews
'
;
import
{
PARALLEL_DIFF_VIEW_TYPE
,
INLINE_DIFF_VIEW_TYPE
,
...
...
@@ -155,7 +154,3 @@ export const diffLines = (state) => (file, unifiedDiffComponents) => {
state
.
diffViewType
===
INLINE_DIFF_VIEW_TYPE
,
);
};
export
function
fileReviews
(
state
)
{
return
state
.
diffFiles
.
map
((
file
)
=>
isFileReviewed
(
state
.
mrReviews
,
file
));
}
app/assets/javascripts/diffs/store/modules/diff_state.js
View file @
0b11dd4f
...
...
@@ -47,4 +47,5 @@ export default () => ({
showSuggestPopover
:
true
,
defaultSuggestionCommitMessage
:
''
,
mrReviews
:
{},
latestDiff
:
true
,
});
app/assets/javascripts/diffs/utils/file_reviews.js
View file @
0b11dd4f
...
...
@@ -2,6 +2,16 @@ function getFileReviewsKey(mrPath) {
return
`
${
mrPath
}
-file-reviews`
;
}
export
function
isFileReviewed
(
reviews
,
file
)
{
const
fileReviews
=
reviews
[
file
.
file_identifier_hash
];
return
file
?.
id
&&
fileReviews
?.
length
?
new
Set
(
fileReviews
).
has
(
file
.
id
)
:
false
;
}
export
function
reviewStatuses
(
files
,
reviews
)
{
return
files
.
map
((
file
)
=>
isFileReviewed
(
reviews
,
file
));
}
export
function
getReviewsForMergeRequest
(
mrPath
)
{
const
reviewsForMr
=
localStorage
.
getItem
(
getFileReviewsKey
(
mrPath
));
let
reviews
=
{};
...
...
@@ -23,23 +33,17 @@ export function setReviewsForMergeRequest(mrPath, reviews) {
return
reviews
;
}
export
function
isFileReviewed
(
reviews
,
file
)
{
const
fileReviews
=
reviews
[
file
.
file_identifier_hash
];
return
file
?.
id
&&
fileReviews
?.
length
?
new
Set
(
fileReviews
).
has
(
file
.
id
)
:
false
;
}
export
function
reviewable
(
file
)
{
return
Boolean
(
file
.
id
)
&&
Boolean
(
file
.
file_identifier_hash
);
}
export
function
markFileReview
(
reviews
,
file
,
reviewed
=
true
)
{
const
usableReviews
=
{
...(
reviews
||
{})
};
le
t
updatedReviews
=
usableReviews
;
cons
t
updatedReviews
=
usableReviews
;
let
fileReviews
;
if
(
reviewable
(
file
))
{
fileReviews
=
new
Set
(
[...(
usableReviews
[
file
.
file_identifier_hash
]
||
[])
]);
fileReviews
=
new
Set
(
usableReviews
[
file
.
file_identifier_hash
]
||
[
]);
if
(
reviewed
)
{
fileReviews
.
add
(
file
.
id
);
...
...
@@ -47,10 +51,7 @@ export function markFileReview(reviews, file, reviewed = true) {
fileReviews
.
delete
(
file
.
id
);
}
updatedReviews
=
{
...
usableReviews
,
[
file
.
file_identifier_hash
]:
Array
.
from
(
fileReviews
),
};
updatedReviews
[
file
.
file_identifier_hash
]
=
Array
.
from
(
fileReviews
);
if
(
updatedReviews
[
file
.
file_identifier_hash
].
length
===
0
)
{
delete
updatedReviews
[
file
.
file_identifier_hash
];
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
0b11dd4f
...
...
@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag
(
:diffs_gradual_load
,
@project
,
default_enabled:
true
)
push_frontend_feature_flag
(
:codequality_mr_diff
,
@project
)
push_frontend_feature_flag
(
:suggestions_custom_commit
,
@project
)
push_frontend_feature_flag
(
:local_file_reviews
,
default_enabled: :yaml
)
record_experiment_user
(
:invite_members_version_a
)
record_experiment_user
(
:invite_members_version_b
)
...
...
changelogs/unreleased/feature-file-review.yml
0 → 100644
View file @
0b11dd4f
---
title
:
Mark files as reviewed locally
merge_request
:
51513
author
:
type
:
added
config/feature_flags/development/local_file_reviews.yml
0 → 100644
View file @
0b11dd4f
---
name
:
local_file_reviews
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48976
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/296674
milestone
:
'
13.8'
type
:
development
group
:
group::code review
default_enabled
:
false
locale/gitlab.pot
View file @
0b11dd4f
...
...
@@ -7081,6 +7081,9 @@ msgstr ""
msgid "Collapse sidebar"
msgstr ""
msgid "Collapses this file (only for you) until it’s changed again."
msgstr ""
msgid "Collector hostname"
msgstr ""
...
...
@@ -31401,6 +31404,9 @@ msgstr ""
msgid "View users statistics"
msgstr ""
msgid "Viewed"
msgstr ""
msgid "Viewing commit"
msgstr ""
...
...
spec/frontend/diffs/components/diff_file_header_spec.js
View file @
0b11dd4f
...
...
@@ -13,10 +13,18 @@ import { diffViewerModes } from '~/ide/constants';
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
{
scrollToElement
}
from
'
~/lib/utils/common_utils
'
;
import
testAction
from
'
../../__helpers__/vuex_action_helper
'
;
import
{
SET_MR_FILE_REVIEWS
}
from
'
~/diffs/store/mutation_types
'
;
import
{
reviewFile
}
from
'
~/diffs/store/actions
'
;
import
{
DIFF_FILE_AUTOMATIC_COLLAPSE
,
DIFF_FILE_MANUAL_COLLAPSE
}
from
'
~/diffs/constants
'
;
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
const
diffFile
=
Object
.
freeze
(
Object
.
assign
(
diffDiscussionsMockData
.
diff_file
,
{
id
:
'
123
'
,
file_identifier_hash
:
'
abc
'
,
edit_path
:
'
link:/to/edit/path
'
,
blob
:
{
id
:
'
848ed9407c6730ff16edb3dd24485a0eea24292a
'
,
...
...
@@ -52,6 +60,8 @@ describe('DiffFileHeader component', () => {
toggleFileDiscussionWrappers
:
jest
.
fn
(),
toggleFullDiff
:
jest
.
fn
(),
toggleActiveFileByHash
:
jest
.
fn
(),
setFileCollapsedByUser
:
jest
.
fn
(),
reviewFile
:
jest
.
fn
(),
},
},
},
...
...
@@ -79,10 +89,11 @@ describe('DiffFileHeader component', () => {
const
findViewFileButton
=
()
=>
wrapper
.
find
({
ref
:
'
viewButton
'
});
const
findCollapseIcon
=
()
=>
wrapper
.
find
({
ref
:
'
collapseIcon
'
});
const
findEditButton
=
()
=>
wrapper
.
find
({
ref
:
'
editButton
'
});
const
findReviewFileCheckbox
=
()
=>
wrapper
.
find
(
"
[data-testid='fileReviewCheckbox']
"
);
const
createComponent
=
(
props
)
=>
{
const
createComponent
=
(
{
props
,
options
=
{}
}
=
{}
)
=>
{
mockStoreConfig
=
cloneDeep
(
defaultMockStoreConfig
);
const
store
=
new
Vuex
.
Store
(
mockStoreConfig
);
const
store
=
new
Vuex
.
Store
(
{
...
mockStoreConfig
,
...(
options
.
store
||
{})
}
);
wrapper
=
shallowMount
(
DiffFileHeader
,
{
propsData
:
{
...
...
@@ -91,6 +102,7 @@ describe('DiffFileHeader component', () => {
viewDiffsFileByFile
:
false
,
...
props
,
},
...
options
,
localVue
,
store
,
});
...
...
@@ -101,7 +113,7 @@ describe('DiffFileHeader component', () => {
${
'
visible
'
}
|
${
true
}
${
'
hidden
'
}
|
${
false
}
`
(
'
collapse toggle is $visibility if collapsible is $collapsible
'
,
({
collapsible
})
=>
{
createComponent
({
collapsible
});
createComponent
({
props
:
{
collapsible
}
});
expect
(
findCollapseIcon
().
exists
()).
toBe
(
collapsible
);
});
...
...
@@ -110,7 +122,7 @@ describe('DiffFileHeader component', () => {
${
true
}
|
${
'
chevron-down
'
}
${
false
}
|
${
'
chevron-right
'
}
`
(
'
collapse icon is $icon if expanded is $expanded
'
,
({
icon
,
expanded
})
=>
{
createComponent
({
expanded
,
collapsible
:
true
});
createComponent
({
props
:
{
expanded
,
collapsible
:
true
}
});
expect
(
findCollapseIcon
().
props
(
'
name
'
)).
toBe
(
icon
);
});
...
...
@@ -124,7 +136,7 @@ describe('DiffFileHeader component', () => {
});
it
(
'
when collapseIcon is clicked emits toggleFile
'
,
()
=>
{
createComponent
({
collapsible
:
true
});
createComponent
({
props
:
{
collapsible
:
true
}
});
findCollapseIcon
().
vm
.
$emit
(
'
click
'
,
new
Event
(
'
click
'
));
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
emitted
().
toggleFile
).
toBeDefined
();
...
...
@@ -132,7 +144,7 @@ describe('DiffFileHeader component', () => {
});
it
(
'
when other element in header is clicked does not emits toggleFile
'
,
()
=>
{
createComponent
({
collapsible
:
true
});
createComponent
({
props
:
{
collapsible
:
true
}
});
findTitleLink
().
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
...
...
@@ -171,11 +183,13 @@ describe('DiffFileHeader component', () => {
it
(
'
prefers submodule_tree_url over submodule_link for href
'
,
()
=>
{
const
submoduleTreeUrl
=
'
some://tree/url
'
;
createComponent
({
props
:
{
discussionLink
:
'
discussionLink
'
,
diffFile
:
{
...
submoduleDiffFile
,
submodule_tree_url
:
'
some://tree/url
'
,
},
},
});
expect
(
findTitleLink
().
attributes
(
'
href
'
)).
toBe
(
submoduleTreeUrl
);
...
...
@@ -184,8 +198,10 @@ describe('DiffFileHeader component', () => {
it
(
'
uses submodule_link for href if submodule_tree_url does not exists
'
,
()
=>
{
const
submoduleLink
=
'
link://to/submodule
'
;
createComponent
({
props
:
{
discussionLink
:
'
discussionLink
'
,
diffFile
:
submoduleDiffFile
,
},
});
expect
(
findTitleLink
().
attributes
(
'
href
'
)).
toBe
(
submoduleLink
);
...
...
@@ -193,7 +209,9 @@ describe('DiffFileHeader component', () => {
it
(
'
uses file_path + SHA as link text
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
submoduleDiffFile
,
},
});
expect
(
findTitleLink
().
text
()).
toContain
(
...
...
@@ -203,15 +221,19 @@ describe('DiffFileHeader component', () => {
it
(
'
does not render file actions
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
submoduleDiffFile
,
addMergeRequestButtons
:
true
,
},
});
expect
(
findFileActions
().
exists
()).
toBe
(
false
);
});
it
(
'
renders submodule icon
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
submoduleDiffFile
,
},
});
expect
(
wrapper
.
find
(
FileIcon
).
props
(
'
submodule
'
)).
toBe
(
true
);
...
...
@@ -223,6 +245,7 @@ describe('DiffFileHeader component', () => {
it
(
'
for mode_changed file mode displays mode changes
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
a_mode
:
'
old-mode
'
,
...
...
@@ -232,6 +255,7 @@ describe('DiffFileHeader component', () => {
name
:
diffViewerModes
.
mode_changed
,
},
},
},
});
expect
(
findModeChangedLine
().
text
()).
toMatch
(
/old-mode.+new-mode/
);
});
...
...
@@ -240,6 +264,7 @@ describe('DiffFileHeader component', () => {
'
for %s file mode does not display mode changes
'
,
(
mode
)
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
a_mode
:
'
old-mode
'
,
...
...
@@ -249,6 +274,7 @@ describe('DiffFileHeader component', () => {
name
:
diffViewerModes
[
mode
],
},
},
},
});
expect
(
findModeChangedLine
().
exists
()).
toBeFalsy
();
},
...
...
@@ -256,32 +282,38 @@ describe('DiffFileHeader component', () => {
it
(
'
displays the LFS label for files stored in LFS
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
stored_externally
:
true
,
external_storage
:
'
lfs
'
},
},
});
expect
(
findLfsLabel
().
exists
()).
toBe
(
true
);
});
it
(
'
does not display the LFS label for files stored in repository
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
stored_externally
:
false
},
},
});
expect
(
findLfsLabel
().
exists
()).
toBe
(
false
);
});
it
(
'
does not render view replaced file button if no replaced view path is present
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
replaced_view_path
:
null
},
},
});
expect
(
findReplacedFileButton
().
exists
()).
toBe
(
false
);
});
describe
(
'
when addMergeRequestButtons is false
'
,
()
=>
{
it
(
'
does not render file actions
'
,
()
=>
{
createComponent
({
addMergeRequestButtons
:
false
});
createComponent
({
props
:
{
addMergeRequestButtons
:
false
}
});
expect
(
findFileActions
().
exists
()).
toBe
(
false
);
});
it
(
'
should not render edit button
'
,
()
=>
{
createComponent
({
addMergeRequestButtons
:
false
});
createComponent
({
props
:
{
addMergeRequestButtons
:
false
}
});
expect
(
findEditButton
().
exists
()).
toBe
(
false
);
});
});
...
...
@@ -290,7 +322,7 @@ describe('DiffFileHeader component', () => {
describe
(
'
without discussions
'
,
()
=>
{
it
(
'
does not render a toggle discussions button
'
,
()
=>
{
diffHasDiscussionsResultMock
.
mockReturnValue
(
false
);
createComponent
({
addMergeRequestButtons
:
true
});
createComponent
({
props
:
{
addMergeRequestButtons
:
true
}
});
expect
(
findToggleDiscussionsButton
().
exists
()).
toBe
(
false
);
});
});
...
...
@@ -298,7 +330,7 @@ describe('DiffFileHeader component', () => {
describe
(
'
with discussions
'
,
()
=>
{
it
(
'
dispatches toggleFileDiscussionWrappers when user clicks on toggle discussions button
'
,
()
=>
{
diffHasDiscussionsResultMock
.
mockReturnValue
(
true
);
createComponent
({
addMergeRequestButtons
:
true
});
createComponent
({
props
:
{
addMergeRequestButtons
:
true
}
});
expect
(
findToggleDiscussionsButton
().
exists
()).
toBe
(
true
);
findToggleDiscussionsButton
().
vm
.
$emit
(
'
click
'
);
expect
(
...
...
@@ -309,7 +341,9 @@ describe('DiffFileHeader component', () => {
it
(
'
should show edit button
'
,
()
=>
{
createComponent
({
props
:
{
addMergeRequestButtons
:
true
,
},
});
expect
(
findEditButton
().
exists
()).
toBe
(
true
);
});
...
...
@@ -319,25 +353,27 @@ describe('DiffFileHeader component', () => {
const
externalUrl
=
'
link://to/external
'
;
const
formattedExternalUrl
=
'
link://formatted
'
;
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
external_url
:
externalUrl
,
formatted_external_url
:
formattedExternalUrl
,
},
addMergeRequestButtons
:
true
,
},
});
expect
(
findExternalLink
().
exists
()).
toBe
(
true
);
});
it
(
'
is hidden by default
'
,
()
=>
{
createComponent
({
addMergeRequestButtons
:
true
});
createComponent
({
props
:
{
addMergeRequestButtons
:
true
}
});
expect
(
findExternalLink
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
without file blob
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
diffFile
:
{
...
diffFile
,
blob
:
false
}
});
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
blob
:
false
}
}
});
});
it
(
'
should not render toggle discussions button
'
,
()
=>
{
...
...
@@ -352,8 +388,10 @@ describe('DiffFileHeader component', () => {
it
(
'
should render correct file view button
'
,
()
=>
{
const
viewPath
=
'
link://view-path
'
;
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
view_path
:
viewPath
},
addMergeRequestButtons
:
true
,
},
});
expect
(
findViewFileButton
().
attributes
(
'
href
'
)).
toBe
(
viewPath
);
expect
(
findViewFileButton
().
text
()).
toEqual
(
...
...
@@ -367,10 +405,12 @@ describe('DiffFileHeader component', () => {
describe
(
'
when diff is fully expanded
'
,
()
=>
{
it
(
'
is not rendered
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
is_fully_expanded
:
true
,
},
},
});
expect
(
findExpandButton
().
exists
()).
toBe
(
false
);
});
...
...
@@ -387,17 +427,17 @@ describe('DiffFileHeader component', () => {
};
it
(
'
renders expand to full file button if not showing full file already
'
,
()
=>
{
createComponent
(
fullyNotExpandedFileProps
);
createComponent
(
{
props
:
fullyNotExpandedFileProps
}
);
expect
(
findExpandButton
().
exists
()).
toBe
(
true
);
});
it
(
'
renders loading icon when loading full file
'
,
()
=>
{
createComponent
(
fullyNotExpandedFileProps
);
createComponent
(
{
props
:
fullyNotExpandedFileProps
}
);
expect
(
findExpandButton
().
exists
()).
toBe
(
true
);
});
it
(
'
toggles full diff on click
'
,
()
=>
{
createComponent
(
fullyNotExpandedFileProps
);
createComponent
(
{
props
:
fullyNotExpandedFileProps
}
);
findExpandButton
().
vm
.
$emit
(
'
click
'
);
expect
(
mockStoreConfig
.
modules
.
diffs
.
actions
.
toggleFullDiff
).
toHaveBeenCalled
();
});
...
...
@@ -407,7 +447,9 @@ describe('DiffFileHeader component', () => {
it
(
'
uses discussionPath for link if it is defined
'
,
()
=>
{
const
discussionPath
=
'
link://to/discussion
'
;
createComponent
({
props
:
{
discussionPath
,
},
});
expect
(
findTitleLink
().
attributes
(
'
href
'
)).
toBe
(
discussionPath
);
});
...
...
@@ -436,21 +478,21 @@ describe('DiffFileHeader component', () => {
describe
(
'
for new file
'
,
()
=>
{
it
(
'
displays the path
'
,
()
=>
{
createComponent
({
diffFile
:
{
...
diffFile
,
new_file
:
true
}
});
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
new_file
:
true
}
}
});
expect
(
findTitleLink
().
text
()).
toBe
(
diffFile
.
file_path
);
});
});
describe
(
'
for deleted file
'
,
()
=>
{
it
(
'
displays the path
'
,
()
=>
{
createComponent
({
diffFile
:
{
...
diffFile
,
deleted_file
:
true
}
});
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
deleted_file
:
true
}
}
});
expect
(
findTitleLink
().
text
()).
toBe
(
sprintf
(
__
(
'
%{filePath} deleted
'
),
{
filePath
:
diffFile
.
file_path
},
false
),
);
});
it
(
'
does not show edit button
'
,
()
=>
{
createComponent
({
diffFile
:
{
...
diffFile
,
deleted_file
:
true
}
});
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
deleted_file
:
true
}
}
});
expect
(
findEditButton
().
exists
()).
toBe
(
false
);
});
});
...
...
@@ -458,12 +500,14 @@ describe('DiffFileHeader component', () => {
describe
(
'
for renamed file
'
,
()
=>
{
it
(
'
displays old and new path if the file was renamed
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
renamed_file
:
true
,
old_path_html
:
'
old
'
,
new_path_html
:
'
new
'
,
},
},
});
expect
(
findTitleLink
().
text
()).
toMatch
(
/^old.+new/
s
);
});
...
...
@@ -473,13 +517,132 @@ describe('DiffFileHeader component', () => {
it
(
'
renders view replaced file button
'
,
()
=>
{
const
replacedViewPath
=
'
some/path
'
;
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
replaced_view_path
:
replacedViewPath
,
},
addMergeRequestButtons
:
true
,
},
});
expect
(
findReplacedFileButton
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
file reviews
'
,
()
=>
{
it
(
'
calls the action to set the new review
'
,
()
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
viewer
:
{
...
diffFile
.
viewer
,
automaticallyCollapsed
:
false
,
manuallyCollapsed
:
null
,
},
},
showLocalFileReviews
:
true
,
addMergeRequestButtons
:
true
,
},
});
const
file
=
wrapper
.
vm
.
diffFile
;
findReviewFileCheckbox
().
vm
.
$emit
(
'
change
'
,
true
);
return
testAction
(
reviewFile
,
{
file
,
reviewed
:
true
},
{},
[{
type
:
SET_MR_FILE_REVIEWS
,
payload
:
{
[
file
.
file_identifier_hash
]:
[
file
.
id
]
}
}],
[],
);
});
it
.
each
`
description | newReviewedStatus | collapseType | aCollapse | mCollapse | callAction
${
'
does nothing
'
}
|
${
true
}
|
${
DIFF_FILE_MANUAL_COLLAPSE
}
|
${
false
}
|
${
true
}
|
${
false
}
${
'
does nothing
'
}
|
${
false
}
|
${
DIFF_FILE_AUTOMATIC_COLLAPSE
}
|
${
true
}
|
${
null
}
|
${
false
}
${
'
does nothing
'
}
|
${
true
}
|
${
'
not collapsed
'
}
|
${
false
}
|
${
null
}
|
${
false
}
${
'
does nothing
'
}
|
${
false
}
|
${
'
not collapsed
'
}
|
${
false
}
|
${
null
}
|
${
false
}
${
'
collapses the file
'
}
|
${
true
}
|
${
DIFF_FILE_AUTOMATIC_COLLAPSE
}
|
${
true
}
|
${
null
}
|
${
true
}
`
(
"
$description if the new review status is reviewed = $newReviewedStatus and the file's collapse type is collapse = $collapseType
"
,
({
newReviewedStatus
,
aCollapse
,
mCollapse
,
callAction
})
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
viewer
:
{
...
diffFile
.
viewer
,
automaticallyCollapsed
:
aCollapse
,
manuallyCollapsed
:
mCollapse
,
},
},
showLocalFileReviews
:
true
,
addMergeRequestButtons
:
true
,
},
});
findReviewFileCheckbox
().
vm
.
$emit
(
'
change
'
,
newReviewedStatus
);
if
(
callAction
)
{
expect
(
mockStoreConfig
.
modules
.
diffs
.
actions
.
setFileCollapsedByUser
).
toHaveBeenCalled
();
}
else
{
expect
(
mockStoreConfig
.
modules
.
diffs
.
actions
.
setFileCollapsedByUser
,
).
not
.
toHaveBeenCalled
();
}
},
);
it
.
each
`
description | show | visible
${
'
shows
'
}
|
${
true
}
|
${
true
}
${
'
hides
'
}
|
${
false
}
|
${
false
}
`
(
'
$description the file review feature given { showLocalFileReviewsProp: $show }
'
,
({
show
,
visible
})
=>
{
createComponent
({
props
:
{
showLocalFileReviews
:
show
,
addMergeRequestButtons
:
true
,
},
});
expect
(
findReviewFileCheckbox
().
exists
()).
toEqual
(
visible
);
},
);
it
.
each
`
open | status | fires
${
true
}
|
${
true
}
|
${
true
}
${
false
}
|
${
false
}
|
${
true
}
${
true
}
|
${
false
}
|
${
false
}
${
false
}
|
${
true
}
|
${
false
}
`
(
'
toggles appropriately when { fileExpanded: $open, newReviewStatus: $status }
'
,
({
open
,
status
,
fires
})
=>
{
createComponent
({
props
:
{
diffFile
:
{
...
diffFile
,
viewer
:
{
...
diffFile
.
viewer
,
automaticallyCollapsed
:
false
,
manuallyCollapsed
:
null
,
},
},
showLocalFileReviews
:
true
,
addMergeRequestButtons
:
true
,
expanded
:
open
,
},
});
findReviewFileCheckbox
().
vm
.
$emit
(
'
change
'
,
status
);
expect
(
Boolean
(
wrapper
.
emitted
().
toggleFile
)).
toBe
(
fires
);
},
);
});
});
spec/frontend/diffs/components/diff_file_spec.js
View file @
0b11dd4f
...
...
@@ -66,7 +66,7 @@ function markFileToBeRendered(store, index = 0) {
});
}
function
createComponent
({
file
,
first
=
false
,
last
=
false
})
{
function
createComponent
({
file
,
first
=
false
,
last
=
false
,
options
=
{},
props
=
{}
})
{
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
...
...
@@ -89,7 +89,9 @@ function createComponent({ file, first = false, last = false }) {
viewDiffsFileByFile
:
false
,
isFirstFile
:
first
,
isLastFile
:
last
,
...
props
,
},
...
options
,
});
return
{
...
...
@@ -220,6 +222,53 @@ describe('DiffFile', () => {
});
});
describe
(
'
computed
'
,
()
=>
{
describe
(
'
showLocalFileReviews
'
,
()
=>
{
let
gon
;
function
setLoggedIn
(
bool
)
{
window
.
gon
.
current_user_id
=
bool
;
}
beforeAll
(()
=>
{
gon
=
window
.
gon
;
window
.
gon
=
{};
});
afterEach
(()
=>
{
window
.
gon
=
gon
;
});
it
.
each
`
loggedIn | featureOn | bool
${
true
}
|
${
true
}
|
${
true
}
${
false
}
|
${
true
}
|
${
false
}
${
true
}
|
${
false
}
|
${
false
}
${
false
}
|
${
false
}
|
${
false
}
`
(
'
should be $bool when { userIsLoggedIn: $loggedIn, featureEnabled: $featureOn }
'
,
({
loggedIn
,
featureOn
,
bool
})
=>
{
setLoggedIn
(
loggedIn
);
({
wrapper
}
=
createComponent
({
options
:
{
provide
:
{
glFeatures
:
{
localFileReviews
:
featureOn
,
},
},
},
props
:
{
file
:
store
.
state
.
diffs
.
diffFiles
[
0
],
},
}));
expect
(
wrapper
.
vm
.
showLocalFileReviews
).
toBe
(
bool
);
},
);
});
});
describe
(
'
collapsing
'
,
()
=>
{
describe
(
`\`
${
EVT_EXPAND_ALL_FILES
}
\` event`
,
()
=>
{
beforeEach
(()
=>
{
...
...
spec/frontend/diffs/store/getters_spec.js
View file @
0b11dd4f
...
...
@@ -375,26 +375,4 @@ describe('Diffs Module Getters', () => {
});
});
});
describe
(
'
fileReviews
'
,
()
=>
{
const
file1
=
{
id
:
'
123
'
,
file_identifier_hash
:
'
abc
'
};
const
file2
=
{
id
:
'
098
'
,
file_identifier_hash
:
'
abc
'
};
it
.
each
`
reviews | files | fileReviews
${{}}
|
$
{[
file1
,
file2
]}
|
${[
false
,
false
]}
${{
abc
:
[
'
123
'
]
}
} |
${[
file1
,
file2
]}
|
${[
true
,
false
]}
${{
abc
:
[
'
098
'
]
}
} |
${[
file1
,
file2
]}
|
${[
false
,
true
]}
${{
def
:
[
'
123
'
]
}
} |
${[
file1
,
file2
]}
|
${[
false
,
false
]}
${{
abc
:
[
'
123
'
],
def
:
[
'
098
'
]
}
} |
${[]}
|
${[]}
`
(
'
returns $fileReviews based on the diff files in state and the existing reviews $reviews
'
,
({
reviews
,
files
,
fileReviews
})
=>
{
localState
.
diffFiles
=
files
;
localState
.
mrReviews
=
reviews
;
expect
(
getters
.
fileReviews
(
localState
)).
toStrictEqual
(
fileReviews
);
},
);
});
});
spec/frontend/diffs/utils/file_reviews_spec.js
View file @
0b11dd4f
...
...
@@ -5,6 +5,7 @@ import {
setReviewsForMergeRequest
,
isFileReviewed
,
markFileReview
,
reviewStatuses
,
reviewable
,
}
from
'
~/diffs/utils/file_reviews
'
;
...
...
@@ -28,6 +29,39 @@ describe('File Review(s) utilities', () => {
localStorage
.
clear
();
});
describe
(
'
isFileReviewed
'
,
()
=>
{
it
.
each
`
description | diffFile | fileReviews
${
'
the file does not have an `id`
'
}
|
${{
...
file
,
id
:
undefined
}
} |
${
getDefaultReviews
()}
${
'
there are no reviews for the file
'
}
|
${
file
}
|
${{
...
getDefaultReviews
(),
abc
:
undefined
}
}
`
(
'
returns `false` if $description
'
,
({
diffFile
,
fileReviews
})
=>
{
expect
(
isFileReviewed
(
fileReviews
,
diffFile
)).
toBe
(
false
);
});
it
(
"
returns `true` for a file if it's available in the provided reviews
"
,
()
=>
{
expect
(
isFileReviewed
(
reviews
,
file
)).
toBe
(
true
);
});
});
describe
(
'
reviewStatuses
'
,
()
=>
{
const
file1
=
{
id
:
'
123
'
,
file_identifier_hash
:
'
abc
'
};
const
file2
=
{
id
:
'
098
'
,
file_identifier_hash
:
'
abc
'
};
it
.
each
`
mrReviews | files | fileReviews
${{}}
|
$
{[
file1
,
file2
]}
|
${[
false
,
false
]}
${{
abc
:
[
'
123
'
]
}
} |
${[
file1
,
file2
]}
|
${[
true
,
false
]}
${{
abc
:
[
'
098
'
]
}
} |
${[
file1
,
file2
]}
|
${[
false
,
true
]}
${{
def
:
[
'
123
'
]
}
} |
${[
file1
,
file2
]}
|
${[
false
,
false
]}
${{
abc
:
[
'
123
'
],
def
:
[
'
098
'
]
}
} |
${[]}
|
${[]}
`
(
'
returns $fileReviews based on the diff files in state and the existing reviews $reviews
'
,
({
mrReviews
,
files
,
fileReviews
})
=>
{
expect
(
reviewStatuses
(
files
,
mrReviews
)).
toStrictEqual
(
fileReviews
);
},
);
});
describe
(
'
getReviewsForMergeRequest
'
,
()
=>
{
it
(
'
fetches the appropriate stored reviews from localStorage
'
,
()
=>
{
getReviewsForMergeRequest
(
mrPath
);
...
...
@@ -73,20 +107,6 @@ describe('File Review(s) utilities', () => {
});
});
describe
(
'
isFileReviewed
'
,
()
=>
{
it
.
each
`
description | diffFile | fileReviews
${
'
the file does not have an `id`
'
}
|
${{
...
file
,
id
:
undefined
}
} |
${
getDefaultReviews
()}
${
'
there are no reviews for the file
'
}
|
${
file
}
|
${{
...
getDefaultReviews
(),
abc
:
undefined
}
}
`
(
'
returns `false` if $description
'
,
({
diffFile
,
fileReviews
})
=>
{
expect
(
isFileReviewed
(
fileReviews
,
diffFile
)).
toBe
(
false
);
});
it
(
"
returns `true` for a file if it's available in the provided reviews
"
,
()
=>
{
expect
(
isFileReviewed
(
reviews
,
file
)).
toBe
(
true
);
});
});
describe
(
'
reviewable
'
,
()
=>
{
it
.
each
`
response | diffFile | description
...
...
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