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
4b95c248
Commit
4b95c248
authored
Dec 10, 2021
by
Jiaan Louw
Committed by
Phil Hughes
Dec 10, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add merge request violation component for compliance reports
parent
43659d07
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
248 additions
and
44 deletions
+248
-44
app/assets/javascripts/lib/utils/common_utils.js
app/assets/javascripts/lib/utils/common_utils.js
+11
-0
ee/app/assets/javascripts/compliance_dashboard/components/drawer.vue
...ts/javascripts/compliance_dashboard/components/drawer.vue
+12
-5
ee/app/assets/javascripts/compliance_dashboard/components/shared/drawer_avatars_list.vue
...iance_dashboard/components/shared/drawer_avatars_list.vue
+4
-17
ee/app/assets/javascripts/compliance_dashboard/components/shared/user_avatar.vue
...ts/compliance_dashboard/components/shared/user_avatar.vue
+31
-0
ee/app/assets/javascripts/compliance_dashboard/components/violations/reason.vue
...pts/compliance_dashboard/components/violations/reason.vue
+36
-0
ee/app/assets/javascripts/compliance_dashboard/constants.js
ee/app/assets/javascripts/compliance_dashboard/constants.js
+18
-0
ee/spec/frontend/compliance_dashboard/components/drawer_spec.js
...c/frontend/compliance_dashboard/components/drawer_spec.js
+6
-5
ee/spec/frontend/compliance_dashboard/components/shared/drawer_avatars_list_spec.js
...e_dashboard/components/shared/drawer_avatars_list_spec.js
+6
-17
ee/spec/frontend/compliance_dashboard/components/shared/user_avatar_spec.js
...ompliance_dashboard/components/shared/user_avatar_spec.js
+48
-0
ee/spec/frontend/compliance_dashboard/components/violations/reason_spec.js
...compliance_dashboard/components/violations/reason_spec.js
+56
-0
locale/gitlab.pot
locale/gitlab.pot
+9
-0
spec/frontend/lib/utils/common_utils_spec.js
spec/frontend/lib/utils/common_utils_spec.js
+11
-0
No files found.
app/assets/javascripts/lib/utils/common_utils.js
View file @
4b95c248
...
...
@@ -735,3 +735,14 @@ export const isFeatureFlagEnabled = (flag) => window.gon.features?.[flag];
export
const
convertArrayToCamelCase
=
(
array
)
=>
array
.
map
((
i
)
=>
convertToCamelCase
(
i
));
export
const
isLoggedIn
=
()
=>
Boolean
(
window
.
gon
?.
current_user_id
);
/**
* This method takes in array of objects with snake_case
* property names and returns a new array of objects with
* camelCase property names
*
* @param {Array[Object]} array - Array to be converted
* @returns {Array[Object]} Converted array
*/
export
const
convertArrayOfObjectsToCamelCase
=
(
array
)
=>
array
.
map
((
o
)
=>
convertObjectPropsToCamelCase
(
o
));
ee/app/assets/javascripts/compliance_dashboard/components/drawer.vue
View file @
4b95c248
<
script
>
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
DRAWER_Z_INDEX
}
from
'
~/lib/utils/constants
'
;
import
{
convertArrayOfObjectsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
{
COMPLIANCE_DRAWER_CONTAINER_CLASS
}
from
'
../constants
'
;
import
{
getContentWrapperHeight
}
from
'
../../threat_monitoring/utils
'
;
import
BranchPath
from
'
./drawer_sections/branch_path.vue
'
;
...
...
@@ -38,6 +39,15 @@ export default {
drawerHeaderHeight
()
{
return
getContentWrapperHeight
(
COMPLIANCE_DRAWER_CONTAINER_CLASS
);
},
committers
()
{
return
convertArrayOfObjectsToCamelCase
(
this
.
mergeRequest
.
committers
);
},
approvedByUsers
()
{
return
convertArrayOfObjectsToCamelCase
(
this
.
mergeRequest
.
approved_by_users
);
},
commenters
()
{
return
convertArrayOfObjectsToCamelCase
(
this
.
mergeRequest
.
participants
);
},
},
DRAWER_Z_INDEX
,
};
...
...
@@ -67,11 +77,8 @@ export default {
:target-branch=
"mergeRequest.target_branch"
:target-branch-uri=
"mergeRequest.target_branch_uri"
/>
<committers
:committers=
"mergeRequest.committers"
/>
<reviewers
:approvers=
"mergeRequest.approved_by_users"
:commenters=
"mergeRequest.participants"
/>
<committers
:committers=
"committers"
/>
<reviewers
:approvers=
"approvedByUsers"
:commenters=
"commenters"
/>
<merged-by
:merged-by=
"mergeRequest.merged_by"
/>
</
template
>
</gl-drawer>
...
...
ee/app/assets/javascripts/compliance_dashboard/components/shared/drawer_avatars_list.vue
View file @
4b95c248
<
script
>
import
{
GlAvatar
,
GlAvatarsInline
,
GlAvatarLink
}
from
'
@gitlab/ui
'
;
import
{
GlAvatar
sInline
}
from
'
@gitlab/ui
'
;
import
{
DRAWER_AVATAR_SIZE
,
DRAWER_MAXIMUM_AVATARS
}
from
'
../../constants
'
;
import
DrawerSectionSubHeader
from
'
./drawer_section_sub_header.vue
'
;
import
UserAvatar
from
'
./user_avatar.vue
'
;
export
default
{
components
:
{
DrawerSectionSubHeader
,
GlAvatar
,
GlAvatarsInline
,
GlAvatarLink
,
UserAvatar
,
},
props
:
{
avatars
:
{
...
...
@@ -57,20 +57,7 @@ export default {
badge-tooltip-prop=
"name"
>
<template
#avatar
="
{ avatar }">
<gl-avatar-link
target=
"blank"
:href=
"avatar.web_url"
:title=
"avatar.name"
class=
"js-user-link"
:data-user-id=
"avatar.id"
:data-name=
"avatar.name"
>
<gl-avatar
:src=
"avatar.avatar_url"
:entity-name=
"avatar.name"
:size=
"$options.DRAWER_AVATAR_SIZE"
/>
</gl-avatar-link>
<user-avatar
:user=
"avatar"
/>
</
template
>
</gl-avatars-inline>
</div>
...
...
ee/app/assets/javascripts/compliance_dashboard/components/shared/user_avatar.vue
0 → 100644
View file @
4b95c248
<
script
>
import
{
GlAvatar
,
GlAvatarLink
}
from
'
@gitlab/ui
'
;
import
{
DRAWER_AVATAR_SIZE
}
from
'
../../constants
'
;
export
default
{
components
:
{
GlAvatar
,
GlAvatarLink
,
},
props
:
{
user
:
{
type
:
Object
,
required
:
true
,
},
},
DRAWER_AVATAR_SIZE
,
};
</
script
>
<
template
>
<gl-avatar-link
target=
"blank"
:href=
"user.webUrl"
:title=
"user.name"
class=
"js-user-link"
:data-user-id=
"user.id"
:data-name=
"user.name"
>
<gl-avatar
:src=
"user.avatarUrl"
:entity-name=
"user.name"
:size=
"$options.DRAWER_AVATAR_SIZE"
/>
</gl-avatar-link>
</
template
>
ee/app/assets/javascripts/compliance_dashboard/components/violations/reason.vue
0 → 100644
View file @
4b95c248
<
script
>
import
{
MERGE_REQUEST_VIOLATION_REASONS
,
MERGE_REQUEST_VIOLATION_MESSAGES
}
from
'
../../constants
'
;
import
UserAvatar
from
'
../shared/user_avatar.vue
'
;
export
default
{
components
:
{
UserAvatar
,
},
props
:
{
reason
:
{
type
:
Number
,
required
:
true
,
},
user
:
{
type
:
Object
,
required
:
false
,
default
:
null
,
},
},
computed
:
{
violation
()
{
return
MERGE_REQUEST_VIOLATION_REASONS
[
this
.
reason
];
},
violationMessage
()
{
return
MERGE_REQUEST_VIOLATION_MESSAGES
[
this
.
violation
];
},
},
};
</
script
>
<
template
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<span
class=
"gl-mr-2"
>
{{
violationMessage
}}
</span>
<user-avatar
v-if=
"user"
:user=
"user"
/>
</div>
</
template
>
ee/app/assets/javascripts/compliance_dashboard/constants.js
View file @
4b95c248
import
{
s__
}
from
'
~/locale
'
;
export
const
PRESENTABLE_APPROVERS_LIMIT
=
2
;
export
const
COMPLIANCE_TAB_COOKIE_KEY
=
'
compliance_dashboard_tabs
'
;
...
...
@@ -11,3 +13,19 @@ export const DRAWER_AVATAR_SIZE = 24;
export
const
DRAWER_MAXIMUM_AVATARS
=
20
;
export
const
COMPLIANCE_DRAWER_CONTAINER_CLASS
=
'
.content-wrapper
'
;
const
VIOLATION_TYPE_APPROVED_BY_AUTHOR
=
'
approved_by_author
'
;
const
VIOLATION_TYPE_APPROVED_BY_COMMITTER
=
'
approved_by_committer
'
;
const
VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS
=
'
approved_by_insufficient_users
'
;
export
const
MERGE_REQUEST_VIOLATION_REASONS
=
{
0
:
VIOLATION_TYPE_APPROVED_BY_AUTHOR
,
1
:
VIOLATION_TYPE_APPROVED_BY_COMMITTER
,
2
:
VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS
,
};
export
const
MERGE_REQUEST_VIOLATION_MESSAGES
=
{
[
VIOLATION_TYPE_APPROVED_BY_AUTHOR
]:
s__
(
'
ComplianceReport|Approved by author
'
),
[
VIOLATION_TYPE_APPROVED_BY_COMMITTER
]:
s__
(
'
ComplianceReport|Approved by committer
'
),
[
VIOLATION_TYPE_APPROVED_BY_INSUFFICIENT_USERS
]:
s__
(
'
ComplianceReport|Less than 2 approvers
'
),
};
ee/spec/frontend/compliance_dashboard/components/drawer_spec.js
View file @
4b95c248
import
{
GlDrawer
}
from
'
@gitlab/ui
'
;
import
{
convertArrayOfObjectsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
MergeRequestDrawer
from
'
ee/compliance_dashboard/components/drawer.vue
'
;
import
BranchPath
from
'
ee/compliance_dashboard/components/drawer_sections/branch_path.vue
'
;
import
Committers
from
'
ee/compliance_dashboard/components/drawer_sections/committers.vue
'
;
...
...
@@ -112,16 +113,16 @@ describe('MergeRequestDrawer component', () => {
expect
(
findBranchPath
().
exists
()).
toBe
(
false
);
});
it
(
'
has the committers section
'
,
()
=>
{
it
(
'
has the committers section
with users array converted to camel case
'
,
()
=>
{
expect
(
findCommitters
().
props
()).
toStrictEqual
({
committers
:
mergeRequest
.
committers
,
committers
:
convertArrayOfObjectsToCamelCase
(
mergeRequest
.
committers
)
,
});
});
it
(
'
has the reviewers section
'
,
()
=>
{
it
(
'
has the reviewers section
with users array converted to camel case
'
,
()
=>
{
expect
(
findReviewers
().
props
()).
toStrictEqual
({
approvers
:
mergeRequest
.
approved_by_users
,
commenters
:
mergeRequest
.
participants
,
approvers
:
convertArrayOfObjectsToCamelCase
(
mergeRequest
.
approved_by_users
)
,
commenters
:
convertArrayOfObjectsToCamelCase
(
mergeRequest
.
participants
)
,
});
});
...
...
ee/spec/frontend/compliance_dashboard/components/shared/drawer_avatars_list_spec.js
View file @
4b95c248
import
{
GlAvatar
,
GlAvatarLink
,
GlAvatar
sInline
}
from
'
@gitlab/ui
'
;
import
{
GlAvatarsInline
}
from
'
@gitlab/ui
'
;
import
{
mount
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
DrawerAvatarsList
from
'
ee/compliance_dashboard/components/shared/drawer_avatars_list.vue
'
;
import
UserAvatar
from
'
ee/compliance_dashboard/components/shared//user_avatar.vue
'
;
import
DrawerSectionSubHeader
from
'
ee/compliance_dashboard/components/shared/drawer_section_sub_header.vue
'
;
import
{
createApprovers
}
from
'
../../mock_data
'
;
...
...
@@ -12,8 +13,7 @@ describe('DrawerAvatarsList component', () => {
const
findHeader
=
()
=>
wrapper
.
findComponent
(
DrawerSectionSubHeader
);
const
findInlineAvatars
=
()
=>
wrapper
.
findComponent
(
GlAvatarsInline
);
const
findAvatarLinks
=
()
=>
wrapper
.
findAllComponents
(
GlAvatarLink
);
const
findAvatars
=
()
=>
wrapper
.
findAllComponents
(
GlAvatar
);
const
findAvatars
=
()
=>
wrapper
.
findAllComponents
(
UserAvatar
);
const
createComponent
=
(
mountFn
=
shallowMount
,
propsData
=
{})
=>
{
return
mountFn
(
DrawerAvatarsList
,
{
...
...
@@ -57,27 +57,16 @@ describe('DrawerAvatarsList component', () => {
});
it
(
'
renders the avatars
'
,
()
=>
{
expect
(
findAvatar
Link
s
()).
toHaveLength
(
avatars
.
length
);
expect
(
findAvatars
()).
toHaveLength
(
avatars
.
length
);
expect
(
findInlineAvatars
().
props
()).
toMatchObject
({
avatars
,
badgeTooltipProp
:
'
name
'
,
});
});
it
(
'
sets the correct attributes to the avatar links
'
,
()
=>
{
expect
(
findAvatarLinks
().
at
(
0
).
classes
()).
toContain
(
'
js-user-link
'
);
expect
(
findAvatarLinks
().
at
(
0
).
attributes
()).
toMatchObject
({
title
:
avatars
[
0
].
name
,
href
:
avatars
[
0
].
web_url
,
'
data-name
'
:
avatars
[
0
].
name
,
'
data-user-id
'
:
`
${
avatars
[
0
].
id
}
`
,
});
});
it
(
'
sets the correct props to the avatars
'
,
()
=>
{
expect
(
findAvatars
().
at
(
0
).
props
()).
toMatchObject
({
entityName
:
avatars
[
0
].
name
,
src
:
avatars
[
0
].
avatar_url
,
avatars
.
forEach
((
avatar
,
idx
)
=>
{
expect
(
findAvatars
().
at
(
idx
).
props
(
'
user
'
)).
toBe
(
avatar
);
});
});
});
...
...
ee/spec/frontend/compliance_dashboard/components/shared/user_avatar_spec.js
0 → 100644
View file @
4b95c248
import
{
GlAvatar
,
GlAvatarLink
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
UserAvatar
from
'
ee/compliance_dashboard/components/shared/user_avatar.vue
'
;
import
{
DRAWER_AVATAR_SIZE
}
from
'
ee/compliance_dashboard/constants
'
;
import
{
createUser
}
from
'
../../mock_data
'
;
describe
(
'
UserAvatar component
'
,
()
=>
{
let
wrapper
;
const
user
=
convertObjectPropsToCamelCase
(
createUser
(
1
));
const
findAvatar
=
()
=>
wrapper
.
findComponent
(
GlAvatar
);
const
findAvatarLink
=
()
=>
wrapper
.
findComponent
(
GlAvatarLink
);
const
createComponent
=
(
props
=
{})
=>
{
wrapper
=
shallowMount
(
UserAvatar
,
{
propsData
:
{
user
,
...
props
,
},
});
};
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
sets the correct attributes to the avatar
'
,
()
=>
{
expect
(
findAvatar
().
props
()).
toMatchObject
({
src
:
user
.
avatarUrl
,
entityName
:
user
.
name
,
size
:
DRAWER_AVATAR_SIZE
,
});
});
it
(
'
sets the correct props to the avatar link
'
,
()
=>
{
expect
(
findAvatarLink
().
attributes
()).
toMatchObject
({
title
:
user
.
name
,
href
:
user
.
webUrl
,
'
data-name
'
:
user
.
name
,
'
data-user-id
'
:
`
${
user
.
id
}
`
,
});
});
});
ee/spec/frontend/compliance_dashboard/components/violations/reason_spec.js
0 → 100644
View file @
4b95c248
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
ViolationReason
from
'
ee/compliance_dashboard/components/violations/reason.vue
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
UserAvatar
from
'
ee/compliance_dashboard/components/shared/user_avatar.vue
'
;
import
{
MERGE_REQUEST_VIOLATION_MESSAGES
,
MERGE_REQUEST_VIOLATION_REASONS
,
}
from
'
ee/compliance_dashboard/constants
'
;
import
{
createUser
}
from
'
../../mock_data
'
;
describe
(
'
ViolationReason component
'
,
()
=>
{
let
wrapper
;
const
user
=
convertObjectPropsToCamelCase
(
createUser
(
1
));
const
getViolationMessage
=
(
reason
)
=>
MERGE_REQUEST_VIOLATION_MESSAGES
[
MERGE_REQUEST_VIOLATION_REASONS
[
reason
]];
const
findAvatar
=
()
=>
wrapper
.
findComponent
(
UserAvatar
);
const
createComponent
=
(
propsData
=
{})
=>
{
wrapper
=
shallowMount
(
ViolationReason
,
{
propsData
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
violation message
'
,
()
=>
{
it
.
each
`
reason | message
${
0
}
|
${
getViolationMessage
(
0
)}
${
1
}
|
${
getViolationMessage
(
1
)}
${
2
}
|
${
getViolationMessage
(
2
)}
`
(
'
renders the violation message "$message" for the reason code $reason
'
,
({
reason
,
message
})
=>
{
createComponent
({
reason
});
expect
(
wrapper
.
text
()).
toContain
(
message
);
},
);
});
describe
(
'
violation user
'
,
()
=>
{
it
(
'
does not render a user avatar by default
'
,
()
=>
{
createComponent
({
reason
:
0
});
expect
(
findAvatar
().
exists
()).
toBe
(
false
);
});
it
(
'
renders a user avatar when the user prop is set
'
,
()
=>
{
createComponent
({
reason
:
0
,
user
});
expect
(
findAvatar
().
props
(
'
user
'
)).
toBe
(
user
);
});
});
});
locale/gitlab.pot
View file @
4b95c248
...
...
@@ -8813,6 +8813,15 @@ msgstr ""
msgid "ComplianceFramework|New compliance framework"
msgstr ""
msgid "ComplianceReport|Approved by author"
msgstr ""
msgid "ComplianceReport|Approved by committer"
msgstr ""
msgid "ComplianceReport|Less than 2 approvers"
msgstr ""
msgid "Component"
msgstr ""
...
...
spec/frontend/lib/utils/common_utils_spec.js
View file @
4b95c248
...
...
@@ -1040,4 +1040,15 @@ describe('common_utils', () => {
expect
(
result
).
toEqual
([
'
hello
'
,
'
helloWorld
'
]);
});
});
describe
(
'
convertArrayOfObjectsToCamelCase
'
,
()
=>
{
it
(
'
returns a new array with snake_case object property names converted camelCase
'
,
()
=>
{
const
result
=
commonUtils
.
convertArrayOfObjectsToCamelCase
([
{
hello
:
''
},
{
hello_world
:
''
},
]);
expect
(
result
).
toEqual
([{
hello
:
''
},
{
helloWorld
:
''
}]);
});
});
});
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