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
21b64cbe
Commit
21b64cbe
authored
Jun 04, 2020
by
Kushal Pandya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add test report status badge support
Shows test report status badge on requirements list page
parent
8640a0d1
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
297 additions
and
22 deletions
+297
-22
ee/app/assets/javascripts/requirements/components/requirement_item.vue
.../javascripts/requirements/components/requirement_item.vue
+38
-13
ee/app/assets/javascripts/requirements/components/requirement_status_badge.vue
...ipts/requirements/components/requirement_status_badge.vue
+74
-0
ee/app/assets/javascripts/requirements/constants.js
ee/app/assets/javascripts/requirements/constants.js
+5
-0
ee/app/assets/javascripts/requirements/queries/projectRequirements.query.graphql
...ts/requirements/queries/projectRequirements.query.graphql
+7
-0
ee/app/assets/stylesheets/pages/requirements.scss
ee/app/assets/stylesheets/pages/requirements.scss
+6
-0
ee/changelogs/unreleased/215516-show-requirements-test-status-badges.yml
...nreleased/215516-show-requirements-test-status-badges.yml
+5
-0
ee/spec/frontend/requirements/components/requirement_item_spec.js
...frontend/requirements/components/requirement_item_spec.js
+32
-9
ee/spec/frontend/requirements/components/requirement_status_badge_spec.js
.../requirements/components/requirement_status_badge_spec.js
+88
-0
ee/spec/frontend/requirements/mock_data.js
ee/spec/frontend/requirements/mock_data.js
+33
-0
locale/gitlab.pot
locale/gitlab.pot
+9
-0
No files found.
ee/app/assets/javascripts/requirements/components/requirement_item.vue
View file @
21b64cbe
...
...
@@ -14,6 +14,7 @@ import { getTimeago } from '~/lib/utils/datetime_utility';
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
RequirementForm
from
'
./requirement_form.vue
'
;
import
RequirementStatusBadge
from
'
./requirement_status_badge.vue
'
;
import
{
FilterState
}
from
'
../constants
'
;
...
...
@@ -26,6 +27,7 @@ export default {
GlIcon
,
GlLoadingIcon
,
RequirementForm
,
RequirementStatusBadge
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
...
...
@@ -36,9 +38,16 @@ export default {
type
:
Object
,
required
:
true
,
validator
:
value
=>
[
'
iid
'
,
'
state
'
,
'
userPermissions
'
,
'
title
'
,
'
createdAt
'
,
'
updatedAt
'
,
'
author
'
].
every
(
prop
=>
value
[
prop
],
),
[
'
iid
'
,
'
state
'
,
'
userPermissions
'
,
'
title
'
,
'
createdAt
'
,
'
updatedAt
'
,
'
author
'
,
'
testReports
'
,
].
every
(
prop
=>
value
[
prop
]),
},
showUpdateForm
:
{
type
:
Boolean
,
...
...
@@ -82,6 +91,12 @@ export default {
author
()
{
return
this
.
requirement
.
author
;
},
testReport
()
{
return
this
.
requirement
.
testReports
.
nodes
[
0
];
},
showIssuableMetaActions
()
{
return
Boolean
(
this
.
canUpdate
||
this
.
canArchive
||
this
.
testReport
);
},
},
methods
:
{
/**
...
...
@@ -131,8 +146,8 @@ export default {
<div
class=
"issue-title title"
>
<span
class=
"issue-title-text"
>
{{
requirement
.
title
}}
</span>
</div>
<div
class=
"issuable-info"
>
<span
class=
"issuable-authored
d-none d-sm-inline-block
"
>
<div
class=
"issuable-info
d-none d-sm-inline-block
"
>
<span
class=
"issuable-authored"
>
<span
v-gl-tooltip:tooltipcontainer
.
bottom
:title=
"tooltipTitle(requirement.createdAt)"
...
...
@@ -143,10 +158,27 @@ export default {
<span
class=
"author"
>
{{
author
.
name
}}
</span>
</gl-link>
</span>
<span
v-gl-tooltip:tooltipcontainer
.
bottom
:title=
"tooltipTitle(requirement.updatedAt)"
class=
"issuable-updated-at"
>
·
{{
updatedAt
}}
</span
>
</div>
<requirement-status-badge
v-if=
"testReport"
:test-report=
"testReport"
class=
"d-block d-sm-none"
/>
</div>
<div
class=
"issuable-meta"
>
<ul
v-if=
"canUpdate || canArchive"
class=
"controls flex-column flex-sm-row"
>
<ul
v-if=
"showIssuableMetaActions"
class=
"controls flex-column flex-sm-row"
>
<requirement-status-badge
v-if=
"testReport"
:test-report=
"testReport"
element-type=
"li"
class=
"d-none d-sm-block"
/>
<li
v-if=
"canUpdate && !isArchived"
class=
"requirement-edit d-sm-block"
>
<gl-deprecated-button
v-gl-tooltip
...
...
@@ -180,13 +212,6 @@ export default {
>
</li>
</ul>
<div
class=
"float-right issuable-updated-at d-none d-sm-inline-block"
>
<span
v-gl-tooltip:tooltipcontainer
.
bottom
:title=
"tooltipTitle(requirement.updatedAt)"
>
{{
updatedAt
}}
</span
>
</div>
</div>
</div>
</div>
...
...
ee/app/assets/javascripts/requirements/components/requirement_status_badge.vue
0 → 100644
View file @
21b64cbe
<
script
>
import
{
GlBadge
,
GlIcon
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
__
}
from
'
~/locale
'
;
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
{
TestReportStatus
}
from
'
../constants
'
;
export
default
{
components
:
{
GlBadge
,
GlIcon
,
GlTooltip
,
},
mixins
:
[
timeagoMixin
],
props
:
{
testReport
:
{
type
:
Object
,
required
:
true
,
},
elementType
:
{
type
:
String
,
required
:
false
,
default
:
'
div
'
,
},
},
computed
:
{
testReportBadge
()
{
if
(
this
.
testReport
.
state
===
TestReportStatus
.
Passed
)
{
return
{
variant
:
'
success
'
,
icon
:
'
status_success
'
,
text
:
__
(
'
satisfied
'
),
tooltipTitle
:
__
(
'
Passed on
'
),
};
}
else
if
(
this
.
testReport
.
state
===
TestReportStatus
.
Failed
)
{
return
{
variant
:
'
danger
'
,
icon
:
'
status_failed
'
,
text
:
__
(
'
failed
'
),
tooltipTitle
:
__
(
'
Failed on
'
),
};
}
return
{
variant
:
'
warning
'
,
icon
:
'
status_warning
'
,
text
:
__
(
'
missing
'
),
tooltipTitle
:
''
,
};
},
},
methods
:
{
getTestReportBadgeTarget
()
{
return
this
.
$refs
.
testReportBadge
?.
$el
||
''
;
},
},
};
</
script
>
<
template
>
<component
:is=
"elementType"
class=
"requirement-status-badge"
>
<gl-badge
ref=
"testReportBadge"
:variant=
"testReportBadge.variant"
>
<gl-icon
:name=
"testReportBadge.icon"
class=
"mr-1"
/>
{{
testReportBadge
.
text
}}
</gl-badge>
<gl-tooltip
v-if=
"testReportBadge.tooltipTitle"
:target=
"getTestReportBadgeTarget"
custom-class=
"requirement-status-tooltip"
>
<b>
{{
testReportBadge
.
tooltipTitle
}}
</b>
<div
class=
"mt-1"
>
{{
tooltipTitle
(
testReport
.
createdAt
)
}}
</div>
</gl-tooltip>
</component>
</
template
>
ee/app/assets/javascripts/requirements/constants.js
View file @
21b64cbe
...
...
@@ -30,6 +30,11 @@ export const AvailableSortOptions = [
},
];
export
const
TestReportStatus
=
{
Passed
:
'
PASSED
'
,
Failed
:
'
FAILED
'
,
};
export
const
DEFAULT_PAGE_SIZE
=
20
;
export
const
MAX_TITLE_LENGTH
=
255
;
ee/app/assets/javascripts/requirements/queries/projectRequirements.query.graphql
View file @
21b64cbe
...
...
@@ -26,6 +26,13 @@ query projectRequirements(
createdAt
updatedAt
state
testReports
(
last
:
1
)
{
nodes
{
id
state
createdAt
}
}
userPermissions
{
updateRequirement
adminRequirement
...
...
ee/app/assets/stylesheets/pages/requirements.scss
View file @
21b64cbe
...
...
@@ -65,3 +65,9 @@
}
}
}
.requirement-status-tooltip
{
.tooltip-inner
{
max-width
:
100%
;
}
}
ee/changelogs/unreleased/215516-show-requirements-test-status-badges.yml
0 → 100644
View file @
21b64cbe
---
title
:
Show test report status badge on Requirements list
merge_request
:
33848
author
:
type
:
added
ee/spec/frontend/requirements/components/requirement_item_spec.js
View file @
21b64cbe
...
...
@@ -3,8 +3,14 @@ import { shallowMount } from '@vue/test-utils';
import
{
GlLink
,
GlDeprecatedButton
,
GlIcon
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
RequirementItem
from
'
ee/requirements/components/requirement_item.vue
'
;
import
RequirementForm
from
'
ee/requirements/components/requirement_form.vue
'
;
import
RequirementStatusBadge
from
'
ee/requirements/components/requirement_status_badge.vue
'
;
import
{
requirement1
,
requirementArchived
,
mockUserPermissions
}
from
'
../mock_data
'
;
import
{
requirement1
,
requirementArchived
,
mockUserPermissions
,
mockTestReport
,
}
from
'
../mock_data
'
;
const
createComponent
=
(
requirement
=
requirement1
)
=>
shallowMount
(
RequirementItem
,
{
...
...
@@ -77,6 +83,12 @@ describe('RequirementItem', () => {
expect
(
wrapper
.
vm
.
author
).
toBe
(
requirement1
.
author
);
});
});
describe
(
'
testReport
'
,
()
=>
{
it
(
'
returns testReport object from reports array within `requirement`
'
,
()
=>
{
expect
(
wrapper
.
vm
.
testReport
).
toBe
(
mockTestReport
);
});
});
});
describe
(
'
methods
'
,
()
=>
{
...
...
@@ -172,6 +184,25 @@ describe('RequirementItem', () => {
expect
(
authorEl
.
find
(
'
.author
'
).
text
()).
toBe
(
requirement1
.
author
.
name
);
});
it
(
'
renders element containing requirement updated at
'
,
()
=>
{
const
updatedAtEl
=
wrapper
.
find
(
'
.issuable-info .issuable-updated-at
'
);
expect
(
updatedAtEl
.
text
()).
toContain
(
'
updated
'
);
expect
(
updatedAtEl
.
text
()).
toContain
(
'
ago
'
);
expect
(
updatedAtEl
.
attributes
(
'
title
'
)).
toBe
(
'
Mar 20, 2020 8:09am GMT+0000
'
);
});
it
(
'
renders requirement-status-badge component
'
,
()
=>
{
const
statusBadgeElSm
=
wrapper
.
find
(
'
.issuable-main-info
'
).
find
(
RequirementStatusBadge
);
const
statusBadgeElMd
=
wrapper
.
find
(
'
.issuable-meta
'
).
find
(
RequirementStatusBadge
);
expect
(
statusBadgeElSm
.
exists
()).
toBe
(
true
);
expect
(
statusBadgeElMd
.
exists
()).
toBe
(
true
);
expect
(
statusBadgeElSm
.
props
(
'
testReport
'
)).
toBe
(
mockTestReport
);
expect
(
statusBadgeElMd
.
props
(
'
testReport
'
)).
toBe
(
mockTestReport
);
expect
(
statusBadgeElMd
.
props
(
'
elementType
'
)).
toBe
(
'
li
'
);
});
it
(
'
renders element containing requirement `Edit` button when `requirement.userPermissions.updateRequirement` is true
'
,
()
=>
{
const
editButtonEl
=
wrapper
.
find
(
'
.controls .requirement-edit
'
).
find
(
GlDeprecatedButton
);
...
...
@@ -258,13 +289,5 @@ describe('RequirementItem', () => {
expect
(
wrapperArchived
.
contains
(
'
.controls .requirement-reopen
'
)).
toBe
(
false
);
});
});
it
(
'
renders element containing requirement updated at
'
,
()
=>
{
const
updatedAtEl
=
wrapper
.
find
(
'
.issuable-meta .issuable-updated-at > span
'
);
expect
(
updatedAtEl
.
text
()).
toContain
(
'
updated
'
);
expect
(
updatedAtEl
.
text
()).
toContain
(
'
ago
'
);
expect
(
updatedAtEl
.
attributes
(
'
title
'
)).
toBe
(
'
Mar 20, 2020 8:09am GMT+0000
'
);
});
});
});
ee/spec/frontend/requirements/components/requirement_status_badge_spec.js
0 → 100644
View file @
21b64cbe
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlBadge
,
GlIcon
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
RequirementStatusBadge
from
'
ee/requirements/components/requirement_status_badge.vue
'
;
import
{
mockTestReport
,
mockTestReportFailed
,
mockTestReportMissing
}
from
'
../mock_data
'
;
const
createComponent
=
(
testReport
=
mockTestReport
)
=>
shallowMount
(
RequirementStatusBadge
,
{
propsData
:
{
testReport
,
},
});
describe
(
'
RequirementStatusBadge
'
,
()
=>
{
let
wrapper
;
beforeEach
(()
=>
{
wrapper
=
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
computed
'
,
()
=>
{
describe
(
'
testReportBadge
'
,
()
=>
{
it
(
'
returns object containing variant, icon, text and tooltipTitle when status is "PASSED"
'
,
()
=>
{
expect
(
wrapper
.
vm
.
testReportBadge
).
toEqual
({
variant
:
'
success
'
,
icon
:
'
status_success
'
,
text
:
'
satisfied
'
,
tooltipTitle
:
'
Passed on
'
,
});
});
it
(
'
returns object containing variant, icon, text and tooltipTitle when status is "FAILED"
'
,
()
=>
{
wrapper
.
setProps
({
testReport
:
mockTestReportFailed
,
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
testReportBadge
).
toEqual
({
variant
:
'
danger
'
,
icon
:
'
status_failed
'
,
text
:
'
failed
'
,
tooltipTitle
:
'
Failed on
'
,
});
});
});
it
(
'
returns object containing variant, icon, text and tooltipTitle when status missing
'
,
()
=>
{
wrapper
.
setProps
({
testReport
:
mockTestReportMissing
,
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
vm
.
testReportBadge
).
toEqual
({
variant
:
'
warning
'
,
icon
:
'
status_warning
'
,
text
:
'
missing
'
,
tooltipTitle
:
''
,
});
});
});
});
});
describe
(
'
template
'
,
()
=>
{
it
(
'
renders GlBadge component
'
,
()
=>
{
const
badgeEl
=
wrapper
.
find
(
GlBadge
);
expect
(
badgeEl
.
exists
()).
toBe
(
true
);
expect
(
badgeEl
.
props
(
'
variant
'
)).
toBe
(
'
success
'
);
expect
(
badgeEl
.
text
()).
toBe
(
'
satisfied
'
);
expect
(
badgeEl
.
contains
(
GlIcon
)).
toBe
(
true
);
expect
(
badgeEl
.
find
(
GlIcon
).
props
(
'
name
'
)).
toBe
(
'
status_success
'
);
});
it
(
'
renders GlTooltip component
'
,
()
=>
{
const
tooltipEl
=
wrapper
.
find
(
GlTooltip
);
expect
(
tooltipEl
.
exists
()).
toBe
(
true
);
expect
(
tooltipEl
.
find
(
'
b
'
).
text
()).
toBe
(
'
Passed on
'
);
expect
(
tooltipEl
.
find
(
'
div
'
).
text
()).
toBe
(
'
Jun 4, 2020 10:55am GMT+0000
'
);
});
});
});
ee/spec/frontend/requirements/mock_data.js
View file @
21b64cbe
...
...
@@ -10,6 +10,27 @@ export const mockAuthor = {
webUrl
:
'
http://0.0.0.0:3000/root
'
,
};
export
const
mockTestReport
=
{
id
:
'
gid://gitlab/RequirementsManagement::TestReport/1
'
,
state
:
'
PASSED
'
,
createdAt
:
'
2020-06-04T10:55:48Z
'
,
__typename
:
'
TestReport
'
,
};
export
const
mockTestReportFailed
=
{
id
:
'
gid://gitlab/RequirementsManagement::TestReport/1
'
,
state
:
'
FAILED
'
,
createdAt
:
'
2020-06-04T10:55:48Z
'
,
__typename
:
'
TestReport
'
,
};
export
const
mockTestReportMissing
=
{
id
:
'
gid://gitlab/RequirementsManagement::TestReport/1
'
,
state
:
''
,
createdAt
:
'
2020-06-04T10:55:48Z
'
,
__typename
:
'
TestReport
'
,
};
export
const
requirement1
=
{
iid
:
'
1
'
,
title
:
'
Virtutis, magnitudinis animi, patientiae, fortitudinis fomentis dolor mitigari solet.
'
,
...
...
@@ -18,6 +39,9 @@ export const requirement1 = {
state
:
'
OPENED
'
,
userPermissions
:
mockUserPermissions
,
author
:
mockAuthor
,
testReports
:
{
nodes
:
[
mockTestReport
],
},
};
export
const
requirement2
=
{
...
...
@@ -28,6 +52,9 @@ export const requirement2 = {
state
:
'
OPENED
'
,
userPermissions
:
mockUserPermissions
,
author
:
mockAuthor
,
testReports
:
{
nodes
:
[
mockTestReport
],
},
};
export
const
requirement3
=
{
...
...
@@ -38,6 +65,9 @@ export const requirement3 = {
state
:
'
OPENED
'
,
userPermissions
:
mockUserPermissions
,
author
:
mockAuthor
,
testReports
:
{
nodes
:
[
mockTestReport
],
},
};
export
const
requirementArchived
=
{
...
...
@@ -48,6 +78,9 @@ export const requirementArchived = {
state
:
'
ARCHIVED
'
,
userPermissions
:
mockUserPermissions
,
author
:
mockAuthor
,
testReports
:
{
nodes
:
[
mockTestReport
],
},
};
export
const
mockRequirementsOpen
=
[
requirement1
,
requirement2
,
requirement3
];
...
...
locale/gitlab.pot
View file @
21b64cbe
...
...
@@ -9281,6 +9281,9 @@ msgstr ""
msgid "Failed Jobs"
msgstr ""
msgid "Failed on"
msgstr ""
msgid "Failed to add a Zoom meeting"
msgstr ""
...
...
@@ -15757,6 +15760,9 @@ msgstr ""
msgid "Passed"
msgstr ""
msgid "Passed on"
msgstr ""
msgid "Password"
msgstr ""
...
...
@@ -27246,6 +27252,9 @@ msgstr ""
msgid "revised"
msgstr ""
msgid "satisfied"
msgstr ""
msgid "score"
msgstr ""
...
...
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