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
6f46175d
Commit
6f46175d
authored
Sep 01, 2021
by
Florie Guibert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Delete legacy board code
- Remove deprecated components
parent
fe5831c6
Changes
22
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
0 additions
and
3818 deletions
+0
-3818
app/assets/javascripts/boards/components/board_card_deprecated.vue
...s/javascripts/boards/components/board_card_deprecated.vue
+0
-61
app/assets/javascripts/boards/components/board_card_layout_deprecated.vue
...cripts/boards/components/board_card_layout_deprecated.vue
+0
-91
app/assets/javascripts/boards/components/board_column_deprecated.vue
...javascripts/boards/components/board_column_deprecated.vue
+0
-112
app/assets/javascripts/boards/components/board_list_deprecated.vue
...s/javascripts/boards/components/board_list_deprecated.vue
+0
-459
app/assets/javascripts/boards/components/board_list_header_deprecated.vue
...cripts/boards/components/board_list_header_deprecated.vue
+0
-361
app/assets/javascripts/boards/components/board_new_issue_deprecated.vue
...ascripts/boards/components/board_new_issue_deprecated.vue
+0
-138
app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue
...scripts/boards/components/issue_card_inner_deprecated.vue
+0
-247
app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue
...ipts/boards/components/issue_time_estimate_deprecated.vue
+0
-48
app/assets/javascripts/boards/components/project_select_deprecated.vue
...vascripts/boards/components/project_select_deprecated.vue
+0
-146
ee/app/assets/javascripts/boards/components/board_list_header_deprecated.vue
...cripts/boards/components/board_list_header_deprecated.vue
+0
-41
ee/spec/frontend/boards/components/board_list_header_deprecated_spec.js
...nd/boards/components/board_list_header_deprecated_spec.js
+0
-129
locale/gitlab.pot
locale/gitlab.pot
+0
-12
spec/frontend/boards/board_list_deprecated_spec.js
spec/frontend/boards/board_list_deprecated_spec.js
+0
-274
spec/frontend/boards/board_new_issue_deprecated_spec.js
spec/frontend/boards/board_new_issue_deprecated_spec.js
+0
-211
spec/frontend/boards/components/board_card_deprecated_spec.js
.../frontend/boards/components/board_card_deprecated_spec.js
+0
-181
spec/frontend/boards/components/board_card_layout_deprecated_spec.js
...nd/boards/components/board_card_layout_deprecated_spec.js
+0
-154
spec/frontend/boards/components/board_column_deprecated_spec.js
...rontend/boards/components/board_column_deprecated_spec.js
+0
-106
spec/frontend/boards/components/board_list_header_deprecated_spec.js
...nd/boards/components/board_list_header_deprecated_spec.js
+0
-174
spec/frontend/boards/components/boards_selector_deprecated_spec.js
...tend/boards/components/boards_selector_deprecated_spec.js
+0
-214
spec/frontend/boards/components/issue_time_estimate_deprecated_spec.js
.../boards/components/issue_time_estimate_deprecated_spec.js
+0
-64
spec/frontend/boards/issue_card_deprecated_spec.js
spec/frontend/boards/issue_card_deprecated_spec.js
+0
-332
spec/frontend/boards/project_select_deprecated_spec.js
spec/frontend/boards/project_select_deprecated_spec.js
+0
-263
No files found.
app/assets/javascripts/boards/components/board_card_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
// This component is being replaced in favor of './board_card.vue' for GraphQL boards
import
sidebarEventHub
from
'
~/sidebar/event_hub
'
;
import
eventHub
from
'
../eventhub
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
import
BoardCardLayoutDeprecated
from
'
./board_card_layout_deprecated.vue
'
;
export
default
{
components
:
{
BoardCardLayout
:
BoardCardLayoutDeprecated
,
},
props
:
{
list
:
{
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
issue
:
{
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
},
methods
:
{
// These are methods instead of computed's, because boardsStore is not reactive.
isActive
()
{
return
this
.
getActiveId
()
===
this
.
issue
.
id
;
},
getActiveId
()
{
return
boardsStore
.
detail
?.
issue
?.
id
;
},
showIssue
({
isMultiSelect
})
{
// If no issues are opened, close all sidebars first
if
(
!
this
.
getActiveId
())
{
sidebarEventHub
.
$emit
(
'
sidebar.closeAll
'
);
}
if
(
this
.
isActive
())
{
eventHub
.
$emit
(
'
clearDetailIssue
'
,
isMultiSelect
);
if
(
isMultiSelect
)
{
eventHub
.
$emit
(
'
newDetailIssue
'
,
this
.
issue
,
isMultiSelect
);
}
}
else
{
eventHub
.
$emit
(
'
newDetailIssue
'
,
this
.
issue
,
isMultiSelect
);
boardsStore
.
setListDetail
(
this
.
list
);
}
},
},
};
</
script
>
<
template
>
<board-card-layout
data-qa-selector=
"board_card"
:issue=
"issue"
:list=
"list"
:is-active=
"isActive()"
v-bind=
"$attrs"
@
show=
"showIssue"
/>
</
template
>
app/assets/javascripts/boards/components/board_card_layout_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
import
IssueCardInnerDeprecated
from
'
./issue_card_inner_deprecated.vue
'
;
export
default
{
name
:
'
BoardCardLayout
'
,
components
:
{
IssueCardInner
:
IssueCardInnerDeprecated
,
},
mixins
:
[
glFeatureFlagMixin
()],
props
:
{
list
:
{
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
issue
:
{
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
disabled
:
{
type
:
Boolean
,
default
:
false
,
required
:
false
,
},
index
:
{
type
:
Number
,
default
:
0
,
required
:
false
,
},
isActive
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
showDetail
:
false
,
multiSelect
:
boardsStore
.
multiSelect
,
};
},
computed
:
{
...
mapGetters
([
'
isSwimlanesOn
'
]),
multiSelectVisible
()
{
return
this
.
multiSelect
.
list
.
findIndex
((
issue
)
=>
issue
.
id
===
this
.
issue
.
id
)
>
-
1
;
},
},
methods
:
{
...
mapActions
([
'
setActiveId
'
]),
mouseDown
()
{
this
.
showDetail
=
true
;
},
mouseMove
()
{
this
.
showDetail
=
false
;
},
showIssue
(
e
)
{
// Don't do anything if this happened on a no trigger element
if
(
e
.
target
.
classList
.
contains
(
'
js-no-trigger
'
))
return
;
this
.
setActiveId
({
id
:
this
.
issue
.
id
,
sidebarType
:
ISSUABLE
});
},
},
};
</
script
>
<
template
>
<li
:class=
"
{
'multi-select': multiSelectVisible,
'user-can-drag': !disabled
&&
issue.id,
'is-disabled': disabled || !issue.id,
'is-active': isActive,
}"
:index="index"
:data-issue-id="issue.id"
:data-issue-iid="issue.iid"
:data-issue-path="issue.referencePath"
data-testid="board_card"
class="board-card gl-p-5 gl-rounded-base"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)"
>
<issue-card-inner
:list=
"list"
:issue=
"issue"
:update-filters=
"true"
/>
</li>
</
template
>
app/assets/javascripts/boards/components/board_column_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
// This component is being replaced in favor of './board_column.vue' for GraphQL boards
import
Sortable
from
'
sortablejs
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header_deprecated.vue
'
;
import
{
getBoardSortableDefaultOptions
,
sortableEnd
}
from
'
../mixins/sortable_default_options
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
import
BoardList
from
'
./board_list_deprecated.vue
'
;
export
default
{
components
:
{
BoardListHeader
,
BoardList
,
},
inject
:
{
boardId
:
{
default
:
''
,
},
},
props
:
{
list
:
{
type
:
Object
,
default
:
()
=>
({}),
required
:
false
,
},
disabled
:
{
type
:
Boolean
,
required
:
true
,
},
},
data
()
{
return
{
detailIssue
:
boardsStore
.
detail
,
filter
:
boardsStore
.
filter
,
};
},
computed
:
{
listIssues
()
{
return
this
.
list
.
issues
;
},
},
watch
:
{
filter
:
{
handler
()
{
// eslint-disable-next-line vue/no-mutating-props
this
.
list
.
page
=
1
;
this
.
list
.
getIssues
(
true
).
catch
(()
=>
{
// TODO: handle request error
});
},
deep
:
true
,
},
'
list.highlighted
'
:
{
handler
(
highlighted
)
{
if
(
highlighted
)
{
this
.
$nextTick
(()
=>
{
this
.
$el
.
scrollIntoView
({
behavior
:
'
smooth
'
,
block
:
'
start
'
});
});
}
},
immediate
:
true
,
},
},
mounted
()
{
const
instance
=
this
;
const
sortableOptions
=
getBoardSortableDefaultOptions
({
disabled
:
this
.
disabled
,
group
:
'
boards
'
,
draggable
:
'
.is-draggable
'
,
handle
:
'
.js-board-handle
'
,
onEnd
(
e
)
{
sortableEnd
();
const
sortable
=
this
;
if
(
e
.
newIndex
!==
undefined
&&
e
.
oldIndex
!==
e
.
newIndex
)
{
const
order
=
sortable
.
toArray
();
const
list
=
boardsStore
.
findList
(
'
id
'
,
parseInt
(
e
.
item
.
dataset
.
id
,
10
));
instance
.
$nextTick
(()
=>
{
boardsStore
.
moveList
(
list
,
order
);
});
}
},
});
Sortable
.
create
(
this
.
$el
.
parentNode
,
sortableOptions
);
},
};
</
script
>
<
template
>
<div
:class=
"
{
'is-draggable': !list.preset,
'is-expandable': list.isExpandable,
'is-collapsed': !list.isExpanded,
'board-type-assignee': list.type === 'assignee',
}"
:data-id="list.id"
class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal"
data-qa-selector="board_list"
>
<div
class=
"board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
:class=
"
{ 'board-column-highlighted': list.highlighted }"
>
<board-list-header
:list=
"list"
:disabled=
"disabled"
/>
<board-list
ref=
"board-list"
:disabled=
"disabled"
:issues=
"listIssues"
:list=
"list"
/>
</div>
</div>
</
template
>
app/assets/javascripts/boards/components/board_list_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
This diff is collapsed.
Click to expand it.
app/assets/javascripts/boards/components/board_list_header_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
This diff is collapsed.
Click to expand it.
app/assets/javascripts/boards/components/board_new_issue_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
mapGetters
}
from
'
vuex
'
;
import
{
getMilestone
}
from
'
ee_else_ce/boards/boards_util
'
;
import
ListIssue
from
'
ee_else_ce/boards/models/issue
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
import
eventHub
from
'
../eventhub
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
import
ProjectSelect
from
'
./project_select_deprecated.vue
'
;
// This component is being replaced in favor of './board_new_issue.vue' for GraphQL boards
export
default
{
name
:
'
BoardNewIssueDeprecated
'
,
components
:
{
ProjectSelect
,
GlButton
,
},
mixins
:
[
glFeatureFlagMixin
()],
inject
:
[
'
groupId
'
],
props
:
{
list
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
title
:
''
,
error
:
false
,
selectedProject
:
{},
};
},
computed
:
{
...
mapGetters
([
'
isGroupBoard
'
]),
disabled
()
{
if
(
this
.
isGroupBoard
)
{
return
this
.
title
===
''
||
!
this
.
selectedProject
.
name
;
}
return
this
.
title
===
''
;
},
},
mounted
()
{
this
.
$refs
.
input
.
focus
();
eventHub
.
$on
(
'
setSelectedProject
'
,
this
.
setSelectedProject
);
},
methods
:
{
submit
(
e
)
{
e
.
preventDefault
();
if
(
this
.
title
.
trim
()
===
''
)
return
Promise
.
resolve
();
this
.
error
=
false
;
const
labels
=
this
.
list
.
label
?
[
this
.
list
.
label
]
:
[];
const
assignees
=
this
.
list
.
assignee
?
[
this
.
list
.
assignee
]
:
[];
const
milestone
=
getMilestone
(
this
.
list
);
const
{
weightFeatureAvailable
}
=
boardsStore
;
const
{
weight
}
=
weightFeatureAvailable
?
boardsStore
.
state
.
currentBoard
:
{};
const
issue
=
new
ListIssue
({
title
:
this
.
title
,
labels
,
subscribed
:
true
,
assignees
,
milestone
,
project_id
:
this
.
selectedProject
.
id
,
weight
,
});
eventHub
.
$emit
(
`scroll-board-list-
${
this
.
list
.
id
}
`
);
this
.
cancel
();
return
this
.
list
.
newIssue
(
issue
)
.
then
(()
=>
{
boardsStore
.
setIssueDetail
(
issue
);
boardsStore
.
setListDetail
(
this
.
list
);
})
.
catch
(()
=>
{
this
.
list
.
removeIssue
(
issue
);
// Show error message
this
.
error
=
true
;
});
},
cancel
()
{
this
.
title
=
''
;
eventHub
.
$emit
(
`toggle-issue-form-
${
this
.
list
.
id
}
`
);
},
setSelectedProject
(
selectedProject
)
{
this
.
selectedProject
=
selectedProject
;
},
},
};
</
script
>
<
template
>
<div
class=
"board-new-issue-form"
>
<div
class=
"board-card position-relative p-3 rounded"
>
<form
@
submit=
"submit($event)"
>
<div
v-if=
"error"
class=
"flash-container"
>
<div
class=
"flash-alert"
>
{{
__
(
'
An error occurred. Please try again.
'
)
}}
</div>
</div>
<label
:for=
"list.id + '-title'"
class=
"label-bold"
>
{{
__
(
'
Title
'
)
}}
</label>
<input
:id=
"list.id + '-title'"
ref=
"input"
v-model=
"title"
class=
"form-control"
type=
"text"
name=
"issue_title"
autocomplete=
"off"
/>
<project-select
v-if=
"isGroupBoard"
:group-id=
"groupId"
:list=
"list"
/>
<div
class=
"clearfix gl-mt-3"
>
<gl-button
ref=
"submitButton"
:disabled=
"disabled"
class=
"float-left js-no-auto-disable"
variant=
"success"
category=
"primary"
type=
"submit"
>
{{
__
(
'
Create issue
'
)
}}
</gl-button
>
<gl-button
ref=
"cancelButton"
class=
"float-right"
type=
"button"
variant=
"default"
@
click=
"cancel"
>
{{
__
(
'
Cancel
'
)
}}
</gl-button
>
</div>
</form>
</div>
</div>
</
template
>
app/assets/javascripts/boards/components/issue_card_inner_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
import
{
GlLabel
,
GlTooltipDirective
,
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
sortBy
}
from
'
lodash
'
;
import
{
mapState
}
from
'
vuex
'
;
import
boardCardInner
from
'
ee_else_ce/boards/mixins/board_card_inner
'
;
import
{
isScopedLabel
}
from
'
~/lib/utils/common_utils
'
;
import
{
sprintf
,
__
,
n__
}
from
'
~/locale
'
;
import
TooltipOnTruncate
from
'
~/vue_shared/components/tooltip_on_truncate.vue
'
;
import
UserAvatarLink
from
'
../../vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
import
IssueDueDate
from
'
./issue_due_date.vue
'
;
import
IssueTimeEstimate
from
'
./issue_time_estimate_deprecated.vue
'
;
export
default
{
components
:
{
GlLabel
,
GlIcon
,
UserAvatarLink
,
TooltipOnTruncate
,
IssueDueDate
,
IssueTimeEstimate
,
IssueCardWeight
:
()
=>
import
(
'
ee_component/boards/components/issue_card_weight.vue
'
),
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
boardCardInner
],
inject
:
[
'
groupId
'
,
'
rootPath
'
],
props
:
{
issue
:
{
type
:
Object
,
required
:
true
,
},
list
:
{
type
:
Object
,
required
:
false
,
default
:
()
=>
({}),
},
updateFilters
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
limitBeforeCounter
:
2
,
maxRender
:
3
,
maxCounter
:
99
,
};
},
computed
:
{
...
mapState
([
'
isShowingLabels
'
]),
numberOverLimit
()
{
return
this
.
issue
.
assignees
.
length
-
this
.
limitBeforeCounter
;
},
assigneeCounterTooltip
()
{
const
{
numberOverLimit
,
maxCounter
}
=
this
;
const
count
=
numberOverLimit
>
maxCounter
?
maxCounter
:
numberOverLimit
;
return
sprintf
(
__
(
'
%{count} more assignees
'
),
{
count
});
},
assigneeCounterLabel
()
{
if
(
this
.
numberOverLimit
>
this
.
maxCounter
)
{
return
`
${
this
.
maxCounter
}
+`
;
}
return
`+
${
this
.
numberOverLimit
}
`
;
},
shouldRenderCounter
()
{
if
(
this
.
issue
.
assignees
.
length
<=
this
.
maxRender
)
{
return
false
;
}
return
this
.
issue
.
assignees
.
length
>
this
.
numberOverLimit
;
},
issueId
()
{
if
(
this
.
issue
.
iid
)
{
return
`#
${
this
.
issue
.
iid
}
`
;
}
return
false
;
},
showLabelFooter
()
{
return
this
.
isShowingLabels
&&
this
.
issue
.
labels
.
find
(
this
.
showLabel
);
},
issueReferencePath
()
{
const
{
referencePath
,
groupId
}
=
this
.
issue
;
return
!
groupId
?
referencePath
.
split
(
'
#
'
)[
0
]
:
null
;
},
orderedLabels
()
{
return
sortBy
(
this
.
issue
.
labels
.
filter
(
this
.
isNonListLabel
),
'
title
'
);
},
blockedLabel
()
{
if
(
this
.
issue
.
blockedByCount
)
{
return
n__
(
`Blocked by %d issue`
,
`Blocked by %d issues`
,
this
.
issue
.
blockedByCount
);
}
return
__
(
'
Blocked issue
'
);
},
assignees
()
{
return
this
.
issue
.
assignees
.
filter
((
_
,
index
)
=>
this
.
shouldRenderAssignee
(
index
));
},
},
methods
:
{
isIndexLessThanlimit
(
index
)
{
return
index
<
this
.
limitBeforeCounter
;
},
shouldRenderAssignee
(
index
)
{
// Eg. maxRender is 4,
// Render up to all 4 assignees if there are only 4 assigness
// Otherwise render up to the limitBeforeCounter
if
(
this
.
issue
.
assignees
.
length
<=
this
.
maxRender
)
{
return
index
<
this
.
maxRender
;
}
return
index
<
this
.
limitBeforeCounter
;
},
assigneeUrl
(
assignee
)
{
if
(
!
assignee
)
return
''
;
return
`
${
this
.
rootPath
}${
assignee
.
username
}
`
;
},
avatarUrlTitle
(
assignee
)
{
return
sprintf
(
__
(
`Avatar for %{assigneeName}`
),
{
assigneeName
:
assignee
.
name
});
},
showLabel
(
label
)
{
if
(
!
label
.
id
)
return
false
;
return
true
;
},
isNonListLabel
(
label
)
{
return
label
.
id
&&
!
(
this
.
list
.
type
===
'
label
'
&&
this
.
list
.
title
===
label
.
title
);
},
filterByLabel
(
label
)
{
if
(
!
this
.
updateFilters
)
return
;
const
labelTitle
=
encodeURIComponent
(
label
.
title
);
const
filter
=
`label_name[]=
${
labelTitle
}
`
;
boardsStore
.
toggleFilter
(
filter
);
},
showScopedLabel
(
label
)
{
return
boardsStore
.
scopedLabels
.
enabled
&&
isScopedLabel
(
label
);
},
},
};
</
script
>
<
template
>
<div>
<div
class=
"gl-display-flex"
dir=
"auto"
>
<h4
class=
"board-card-title gl-mb-0 gl-mt-0"
>
<gl-icon
v-if=
"issue.blocked"
v-gl-tooltip
name=
"issue-block"
:title=
"blockedLabel"
class=
"issue-blocked-icon gl-mr-2"
:aria-label=
"blockedLabel"
data-testid=
"issue-blocked-icon"
/>
<gl-icon
v-if=
"issue.confidential"
v-gl-tooltip
name=
"eye-slash"
:title=
"__('Confidential')"
class=
"confidential-icon gl-mr-2"
:aria-label=
"__('Confidential')"
/>
<a
:href=
"issue.path || issue.webUrl || ''"
:title=
"issue.title"
class=
"js-no-trigger"
@
mousemove
.
stop
>
{{
issue
.
title
}}
</a
>
</h4>
</div>
<div
v-if=
"showLabelFooter"
class=
"board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap"
>
<template
v-for=
"label in orderedLabels"
>
<gl-label
:key=
"label.id"
:background-color=
"label.color"
:title=
"label.title"
:description=
"label.description"
size=
"sm"
:scoped=
"showScopedLabel(label)"
@
click=
"filterByLabel(label)"
/>
</
template
>
</div>
<div
class=
"board-card-footer gl-display-flex gl-justify-content-space-between gl-align-items-flex-end"
>
<div
class=
"gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden js-board-card-number-container"
>
<span
v-if=
"issue.referencePath"
class=
"board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3"
>
<tooltip-on-truncate
v-if=
"issueReferencePath"
:title=
"issueReferencePath"
placement=
"bottom"
class=
"board-issue-path gl-text-truncate gl-font-weight-bold"
>
{{ issueReferencePath }}
</tooltip-on-truncate
>
#{{ issue.iid }}
</span>
<span
class=
"board-info-items gl-mt-3 gl-display-inline-block"
>
<issue-due-date
v-if=
"issue.dueDate"
:date=
"issue.dueDate"
:closed=
"issue.closed || Boolean(issue.closedAt)"
/>
<issue-time-estimate
v-if=
"issue.timeEstimate"
:estimate=
"issue.timeEstimate"
/>
<issue-card-weight
v-if=
"validIssueWeight(issue)"
:weight=
"issue.weight"
@
click=
"filterByWeight(issue.weight)"
/>
</span>
</div>
<div
class=
"board-card-assignee gl-display-flex"
>
<user-avatar-link
v-for=
"assignee in assignees"
:key=
"assignee.id"
:link-href=
"assigneeUrl(assignee)"
:img-alt=
"avatarUrlTitle(assignee)"
:img-src=
"assignee.avatarUrl || assignee.avatar || assignee.avatar_url"
:img-size=
"24"
class=
"js-no-trigger"
tooltip-placement=
"bottom"
>
<span
class=
"js-assignee-tooltip"
>
<span
class=
"gl-font-weight-bold gl-display-block"
>
{{ __('Assignee') }}
</span>
{{ assignee.name }}
<span
class=
"text-white-50"
>
@{{ assignee.username }}
</span>
</span>
</user-avatar-link>
<span
v-if=
"shouldRenderCounter"
v-gl-tooltip
:title=
"assigneeCounterTooltip"
class=
"avatar-counter"
data-placement=
"bottom"
>
{{ assigneeCounterLabel }}
</span
>
</div>
</div>
</div>
</template>
app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
import
{
GlTooltip
,
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
parseSeconds
,
stringifyTime
}
from
'
~/lib/utils/datetime_utility
'
;
import
boardsStore
from
'
../stores/boards_store
'
;
export
default
{
components
:
{
GlIcon
,
GlTooltip
,
},
props
:
{
estimate
:
{
type
:
[
Number
,
String
],
required
:
true
,
},
},
data
()
{
return
{
limitToHours
:
boardsStore
.
timeTracking
.
limitToHours
,
};
},
computed
:
{
title
()
{
return
stringifyTime
(
parseSeconds
(
this
.
estimate
,
{
limitToHours
:
this
.
limitToHours
}),
true
);
},
timeEstimate
()
{
return
stringifyTime
(
parseSeconds
(
this
.
estimate
,
{
limitToHours
:
this
.
limitToHours
}));
},
},
};
</
script
>
<
template
>
<span>
<span
ref=
"issueTimeEstimate"
class=
"board-card-info card-number"
>
<gl-icon
name=
"hourglass"
class=
"board-card-info-icon"
/><time
class=
"board-card-info-text"
>
{{
timeEstimate
}}
</time>
</span>
<gl-tooltip
:target=
"() => $refs.issueTimeEstimate"
placement=
"bottom"
class=
"js-issue-time-estimate"
>
<span
class=
"bold d-block"
>
{{
__
(
'
Time estimate
'
)
}}
</span>
{{
title
}}
</gl-tooltip>
</span>
</
template
>
app/assets/javascripts/boards/components/project_select_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
import
{
GlDropdown
,
GlDropdownItem
,
GlDropdownText
,
GlSearchBoxByType
,
GlLoadingIcon
,
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
featureAccessLevel
}
from
'
~/pages/projects/shared/permissions/constants
'
;
import
Api
from
'
../../api
'
;
import
{
ListType
}
from
'
../constants
'
;
import
eventHub
from
'
../eventhub
'
;
export
default
{
name
:
'
ProjectSelect
'
,
i18n
:
{
headerTitle
:
s__
(
`BoardNewIssue|Projects`
),
dropdownText
:
s__
(
`BoardNewIssue|Select a project`
),
searchPlaceholder
:
s__
(
`BoardNewIssue|Search projects`
),
emptySearchResult
:
s__
(
`BoardNewIssue|No matching results`
),
},
defaultFetchOptions
:
{
with_issues_enabled
:
true
,
with_shared
:
false
,
include_subgroups
:
true
,
order_by
:
'
similarity
'
,
archived
:
false
,
},
components
:
{
GlLoadingIcon
,
GlDropdown
,
GlDropdownItem
,
GlDropdownText
,
GlSearchBoxByType
,
},
inject
:
[
'
groupId
'
],
props
:
{
list
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
initialLoading
:
true
,
isFetching
:
false
,
projects
:
[],
selectedProject
:
{},
searchTerm
:
''
,
};
},
computed
:
{
selectedProjectName
()
{
return
this
.
selectedProject
.
name
||
this
.
$options
.
i18n
.
dropdownText
;
},
fetchOptions
()
{
const
additionalAttrs
=
{};
if
(
this
.
list
.
type
&&
this
.
list
.
type
!==
ListType
.
backlog
)
{
additionalAttrs
.
min_access_level
=
featureAccessLevel
.
EVERYONE
;
}
return
{
...
this
.
$options
.
defaultFetchOptions
,
...
additionalAttrs
,
};
},
isFetchResultEmpty
()
{
return
this
.
projects
.
length
===
0
;
},
},
watch
:
{
searchTerm
()
{
this
.
fetchProjects
();
},
},
async
mounted
()
{
await
this
.
fetchProjects
();
this
.
initialLoading
=
false
;
},
methods
:
{
async
fetchProjects
()
{
this
.
isFetching
=
true
;
try
{
const
projects
=
await
Api
.
groupProjects
(
this
.
groupId
,
this
.
searchTerm
,
this
.
fetchOptions
);
this
.
projects
=
projects
.
map
((
project
)
=>
{
return
{
id
:
project
.
id
,
name
:
project
.
name
,
namespacedName
:
project
.
name_with_namespace
,
path
:
project
.
path_with_namespace
,
};
});
}
catch
(
err
)
{
/* Handled in Api.groupProjects */
}
finally
{
this
.
isFetching
=
false
;
}
},
selectProject
(
projectId
)
{
this
.
selectedProject
=
this
.
projects
.
find
((
project
)
=>
project
.
id
===
projectId
);
eventHub
.
$emit
(
'
setSelectedProject
'
,
this
.
selectedProject
);
},
},
};
</
script
>
<
template
>
<div>
<label
class=
"gl-font-weight-bold gl-mt-3"
data-testid=
"header-label"
>
{{
$options
.
i18n
.
headerTitle
}}
</label>
<gl-dropdown
data-testid=
"project-select-dropdown"
:text=
"selectedProjectName"
:header-text=
"$options.i18n.headerTitle"
block
menu-class=
"gl-w-full!"
:loading=
"initialLoading"
>
<gl-search-box-by-type
v-model.trim=
"searchTerm"
debounce=
"250"
:placeholder=
"$options.i18n.searchPlaceholder"
/>
<gl-dropdown-item
v-for=
"project in projects"
v-show=
"!isFetching"
:key=
"project.id"
:name=
"project.name"
@
click=
"selectProject(project.id)"
>
{{
project
.
namespacedName
}}
</gl-dropdown-item>
<gl-dropdown-text
v-show=
"isFetching"
data-testid=
"dropdown-text-loading-icon"
>
<gl-loading-icon
class=
"gl-mx-auto"
size=
"sm"
/>
</gl-dropdown-text>
<gl-dropdown-text
v-if=
"isFetchResultEmpty && !isFetching"
data-testid=
"empty-result-message"
>
<span
class=
"gl-text-gray-500"
>
{{
$options
.
i18n
.
emptySearchResult
}}
</span>
</gl-dropdown-text>
</gl-dropdown>
</div>
</
template
>
ee/app/assets/javascripts/boards/components/board_list_header_deprecated.vue
deleted
100644 → 0
View file @
fe5831c6
<
script
>
// This is a false violation of @gitlab/no-runtime-template-compiler, since it
// extends a valid Vue single file component.
/* eslint-disable @gitlab/no-runtime-template-compiler */
import
BoardListHeaderFoss
from
'
~/boards/components/board_list_header_deprecated.vue
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
{
__
,
sprintf
,
s__
}
from
'
~/locale
'
;
export
default
{
extends
:
BoardListHeaderFoss
,
data
()
{
return
{
weightFeatureAvailable
:
boardsStore
.
weightFeatureAvailable
,
};
},
computed
:
{
issuesTooltip
()
{
const
{
maxIssueCount
}
=
this
.
list
;
if
(
maxIssueCount
>
0
)
{
return
sprintf
(
__
(
'
%{issuesCount} issues with a limit of %{maxIssueCount}
'
),
{
issuesCount
:
this
.
issuesCount
,
maxIssueCount
,
});
}
// TODO: Remove this pattern.
return
BoardListHeaderFoss
.
computed
.
issuesTooltip
.
call
(
this
);
},
weightCountToolTip
()
{
const
{
totalWeight
}
=
this
.
list
;
if
(
this
.
weightFeatureAvailable
)
{
return
sprintf
(
s__
(
'
%{totalWeight} total weight
'
),
{
totalWeight
});
}
return
null
;
},
},
};
</
script
>
ee/spec/frontend/boards/components/board_list_header_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
AxiosMockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
BoardListHeader
from
'
ee/boards/components/board_list_header_deprecated.vue
'
;
import
getters
from
'
ee/boards/stores/getters
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
listObj
}
from
'
jest/boards/mock_data
'
;
import
{
ListType
,
inactiveId
}
from
'
~/boards/constants
'
;
import
List
from
'
~/boards/models/list
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
sidebarEventHub
from
'
~/sidebar/event_hub
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
Board List Header Component
'
,
()
=>
{
let
store
;
let
wrapper
;
let
axiosMock
;
beforeEach
(()
=>
{
window
.
gon
=
{};
axiosMock
=
new
AxiosMockAdapter
(
axios
);
axiosMock
.
onGet
(
`
${
TEST_HOST
}
/lists/1/issues`
).
reply
(
200
,
{
issues
:
[]
});
store
=
new
Vuex
.
Store
({
state
:
{
activeId
:
inactiveId
},
getters
});
jest
.
spyOn
(
store
,
'
dispatch
'
).
mockImplementation
();
});
afterEach
(()
=>
{
axiosMock
.
restore
();
wrapper
.
destroy
();
localStorage
.
clear
();
});
const
createComponent
=
({
listType
=
ListType
.
backlog
,
collapsed
=
false
,
withLocalStorage
=
true
,
currentUserId
=
1
,
}
=
{})
=>
{
const
boardId
=
'
1
'
;
const
listMock
=
{
...
listObj
,
list_type
:
listType
,
collapsed
,
};
if
(
listType
===
ListType
.
assignee
)
{
delete
listMock
.
label
;
listMock
.
user
=
{};
}
// Making List reactive
const
list
=
Vue
.
observable
(
new
List
(
listMock
));
if
(
withLocalStorage
)
{
localStorage
.
setItem
(
`boards.
${
boardId
}
.
${
list
.
type
}
.
${
list
.
id
}
.expanded`
,
(
!
collapsed
).
toString
(),
);
}
wrapper
=
shallowMount
(
BoardListHeader
,
{
store
,
localVue
,
propsData
:
{
disabled
:
false
,
list
,
},
provide
:
{
currentUserId
,
boardId
,
},
});
};
const
findSettingsButton
=
()
=>
wrapper
.
find
({
ref
:
'
settingsBtn
'
});
describe
(
'
Settings Button
'
,
()
=>
{
const
hasSettings
=
[
ListType
.
assignee
,
ListType
.
milestone
,
ListType
.
iteration
,
ListType
.
label
];
const
hasNoSettings
=
[
ListType
.
backlog
,
ListType
.
closed
];
it
.
each
(
hasSettings
)(
'
does render for List Type `%s`
'
,
(
listType
)
=>
{
createComponent
({
listType
});
expect
(
findSettingsButton
().
exists
()).
toBe
(
true
);
});
it
.
each
(
hasNoSettings
)(
'
does not render for List Type `%s`
'
,
(
listType
)
=>
{
createComponent
({
listType
});
expect
(
findSettingsButton
().
exists
()).
toBe
(
false
);
});
it
(
'
has a test for each list type
'
,
()
=>
{
Object
.
values
(
ListType
).
forEach
((
value
)
=>
{
expect
([...
hasSettings
,
...
hasNoSettings
]).
toContain
(
value
);
});
});
describe
(
'
emits sidebar.closeAll event on openSidebarSettings
'
,
()
=>
{
beforeEach
(()
=>
{
jest
.
spyOn
(
sidebarEventHub
,
'
$emit
'
);
});
it
(
'
emits event if no active List
'
,
()
=>
{
// Shares the same behavior for any settings-enabled List type
createComponent
({
listType
:
hasSettings
[
0
]
});
wrapper
.
vm
.
openSidebarSettings
();
expect
(
sidebarEventHub
.
$emit
).
toHaveBeenCalledWith
(
'
sidebar.closeAll
'
);
});
it
(
'
does not emit event when there is an active List
'
,
()
=>
{
store
.
state
.
activeId
=
listObj
.
id
;
createComponent
({
listType
:
hasSettings
[
0
]
});
wrapper
.
vm
.
openSidebarSettings
();
expect
(
sidebarEventHub
.
$emit
).
not
.
toHaveBeenCalled
();
});
});
});
});
locale/gitlab.pot
View file @
6f46175d
...
...
@@ -621,9 +621,6 @@ msgstr ""
msgid "%{issueType} actions"
msgstr ""
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{issuesSize} with a limit of %{maxIssueCount}"
msgstr ""
...
...
@@ -20214,9 +20211,6 @@ msgstr ""
msgid "Loading functions timed out. Please reload the page to try again."
msgstr ""
msgid "Loading issues"
msgstr ""
msgid "Loading more"
msgstr ""
...
...
@@ -30883,9 +30877,6 @@ msgstr ""
msgid "Showing %{pageSize} of %{total} %{issuableType}"
msgstr ""
msgid "Showing %{pageSize} of %{total} issues"
msgstr ""
msgid "Showing all epics"
msgstr ""
...
...
@@ -31306,9 +31297,6 @@ msgstr ""
msgid "Something went wrong while obtaining the Let's Encrypt certificate."
msgstr ""
msgid "Something went wrong while performing the action."
msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
...
...
spec/frontend/boards/board_list_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
/* global List */
/* global ListIssue */
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
from
'
vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
BoardList
from
'
~/boards/components/board_list_deprecated.vue
'
;
import
eventHub
from
'
~/boards/eventhub
'
;
import
store
from
'
~/boards/stores
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
'
~/boards/models/issue
'
;
import
'
~/boards/models/list
'
;
import
{
listObj
,
boardsMockInterceptor
}
from
'
./mock_data
'
;
const
createComponent
=
({
done
,
listIssueProps
=
{},
componentProps
=
{},
listProps
=
{}
})
=>
{
const
el
=
document
.
createElement
(
'
div
'
);
document
.
body
.
appendChild
(
el
);
const
mock
=
new
MockAdapter
(
axios
);
mock
.
onAny
().
reply
(
boardsMockInterceptor
);
boardsStore
.
create
();
const
BoardListComp
=
Vue
.
extend
(
BoardList
);
const
list
=
new
List
({
...
listObj
,
...
listProps
});
const
issue
=
new
ListIssue
({
title
:
'
Testing
'
,
id
:
1
,
iid
:
1
,
confidential
:
false
,
labels
:
[],
assignees
:
[],
...
listIssueProps
,
});
if
(
!
Object
.
prototype
.
hasOwnProperty
.
call
(
listProps
,
'
issuesSize
'
))
{
list
.
issuesSize
=
1
;
}
list
.
issues
.
push
(
issue
);
const
component
=
new
BoardListComp
({
el
,
store
,
propsData
:
{
disabled
:
false
,
list
,
issues
:
list
.
issues
,
...
componentProps
,
},
provide
:
{
groupId
:
null
,
rootPath
:
'
/
'
,
},
}).
$mount
();
Vue
.
nextTick
(()
=>
{
done
();
});
return
{
component
,
mock
};
};
describe
(
'
Board list component
'
,
()
=>
{
let
mock
;
let
component
;
let
getIssues
;
function
generateIssues
(
compWrapper
)
{
for
(
let
i
=
1
;
i
<
20
;
i
+=
1
)
{
const
issue
=
{
...
compWrapper
.
list
.
issues
[
0
]
};
issue
.
id
+=
i
;
compWrapper
.
list
.
issues
.
push
(
issue
);
}
}
describe
(
'
When Expanded
'
,
()
=>
{
beforeEach
((
done
)
=>
{
getIssues
=
jest
.
spyOn
(
List
.
prototype
,
'
getIssues
'
).
mockReturnValue
(
new
Promise
(()
=>
{}));
({
mock
,
component
}
=
createComponent
({
done
}));
});
afterEach
(()
=>
{
mock
.
restore
();
component
.
$destroy
();
});
it
(
'
loads first page of issues
'
,
()
=>
{
return
waitForPromises
().
then
(()
=>
{
expect
(
getIssues
).
toHaveBeenCalled
();
});
});
it
(
'
renders component
'
,
()
=>
{
expect
(
component
.
$el
.
classList
.
contains
(
'
board-list-component
'
)).
toBe
(
true
);
});
it
(
'
renders loading icon
'
,
()
=>
{
component
.
list
.
loading
=
true
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-loading
'
)).
not
.
toBeNull
();
});
});
it
(
'
renders issues
'
,
()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
.board-card
'
).
length
).
toBe
(
1
);
});
it
(
'
sets data attribute with issue id
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-card
'
).
getAttribute
(
'
data-issue-id
'
)).
toBe
(
'
1
'
);
});
it
(
'
shows new issue form
'
,
()
=>
{
component
.
toggleForm
();
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
)).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.is-smaller
'
)).
not
.
toBeNull
();
});
});
it
(
'
shows new issue form after eventhub event
'
,
()
=>
{
eventHub
.
$emit
(
`toggle-issue-form-
${
component
.
list
.
id
}
`
);
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
)).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.is-smaller
'
)).
not
.
toBeNull
();
});
});
it
(
'
does not show new issue form for closed list
'
,
()
=>
{
component
.
list
.
type
=
'
closed
'
;
component
.
toggleForm
();
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
)).
toBeNull
();
});
});
it
(
'
shows count list item
'
,
()
=>
{
component
.
showCount
=
true
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
)).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
).
textContent
.
trim
()).
toBe
(
'
Showing all issues
'
,
);
});
});
it
(
'
sets data attribute with invalid id
'
,
()
=>
{
component
.
showCount
=
true
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
).
getAttribute
(
'
data-issue-id
'
)).
toBe
(
'
-1
'
,
);
});
});
it
(
'
shows how many more issues to load
'
,
()
=>
{
component
.
showCount
=
true
;
component
.
list
.
issuesSize
=
20
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
).
textContent
.
trim
()).
toBe
(
'
Showing 1 of 20 issues
'
,
);
});
});
it
(
'
loads more issues after scrolling
'
,
()
=>
{
jest
.
spyOn
(
component
.
list
,
'
nextPage
'
).
mockImplementation
(()
=>
{});
generateIssues
(
component
);
component
.
$refs
.
list
.
dispatchEvent
(
new
Event
(
'
scroll
'
));
return
waitForPromises
().
then
(()
=>
{
expect
(
component
.
list
.
nextPage
).
toHaveBeenCalled
();
});
});
it
(
'
does not load issues if already loading
'
,
()
=>
{
component
.
list
.
nextPage
=
jest
.
spyOn
(
component
.
list
,
'
nextPage
'
)
.
mockReturnValue
(
new
Promise
(()
=>
{}));
component
.
onScroll
();
component
.
onScroll
();
return
waitForPromises
().
then
(()
=>
{
expect
(
component
.
list
.
nextPage
).
toHaveBeenCalledTimes
(
1
);
});
});
it
(
'
shows loading more spinner
'
,
()
=>
{
component
.
showCount
=
true
;
component
.
list
.
loadingMore
=
true
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count .gl-spinner
'
)).
not
.
toBeNull
();
});
});
});
describe
(
'
When Collapsed
'
,
()
=>
{
beforeEach
((
done
)
=>
{
getIssues
=
jest
.
spyOn
(
List
.
prototype
,
'
getIssues
'
).
mockReturnValue
(
new
Promise
(()
=>
{}));
({
mock
,
component
}
=
createComponent
({
done
,
listProps
:
{
type
:
'
closed
'
,
collapsed
:
true
,
issuesSize
:
50
},
}));
generateIssues
(
component
);
component
.
scrollHeight
=
jest
.
spyOn
(
component
,
'
scrollHeight
'
).
mockReturnValue
(
0
);
});
afterEach
(()
=>
{
mock
.
restore
();
component
.
$destroy
();
});
it
(
'
does not load all issues
'
,
()
=>
{
return
waitForPromises
().
then
(()
=>
{
// Initial getIssues from list constructor
expect
(
getIssues
).
toHaveBeenCalledTimes
(
1
);
});
});
});
describe
(
'
max issue count warning
'
,
()
=>
{
beforeEach
((
done
)
=>
{
({
mock
,
component
}
=
createComponent
({
done
,
listProps
:
{
type
:
'
closed
'
,
collapsed
:
true
,
issuesSize
:
50
},
}));
});
afterEach
(()
=>
{
mock
.
restore
();
component
.
$destroy
();
});
describe
(
'
when issue count exceeds max issue count
'
,
()
=>
{
it
(
'
sets background to bg-danger-100
'
,
()
=>
{
component
.
list
.
issuesSize
=
4
;
component
.
list
.
maxIssueCount
=
3
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.bg-danger-100
'
)).
not
.
toBeNull
();
});
});
});
describe
(
'
when list issue count does NOT exceed list max issue count
'
,
()
=>
{
it
(
'
does not sets background to bg-danger-100
'
,
()
=>
{
component
.
list
.
issuesSize
=
2
;
component
.
list
.
maxIssueCount
=
3
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.bg-danger-100
'
)).
toBeNull
();
});
});
});
describe
(
'
when list max issue count is 0
'
,
()
=>
{
it
(
'
does not sets background to bg-danger-100
'
,
()
=>
{
component
.
list
.
maxIssueCount
=
0
;
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.bg-danger-100
'
)).
toBeNull
();
});
});
});
});
});
spec/frontend/boards/board_new_issue_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
/* global List */
import
{
mount
}
from
'
@vue/test-utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
boardNewIssue
from
'
~/boards/components/board_new_issue_deprecated.vue
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
'
~/boards/models/list
'
;
import
{
listObj
,
boardsMockInterceptor
}
from
'
./mock_data
'
;
Vue
.
use
(
Vuex
);
describe
(
'
Issue boards new issue form
'
,
()
=>
{
let
wrapper
;
let
vm
;
let
list
;
let
mock
;
let
newIssueMock
;
const
promiseReturn
=
{
data
:
{
iid
:
100
,
},
};
const
submitIssue
=
()
=>
{
const
dummySubmitEvent
=
{
preventDefault
()
{},
};
wrapper
.
vm
.
$refs
.
submitButton
=
wrapper
.
find
({
ref
:
'
submitButton
'
});
return
wrapper
.
vm
.
submit
(
dummySubmitEvent
);
};
beforeEach
(()
=>
{
const
BoardNewIssueComp
=
Vue
.
extend
(
boardNewIssue
);
mock
=
new
MockAdapter
(
axios
);
mock
.
onAny
().
reply
(
boardsMockInterceptor
);
boardsStore
.
create
();
list
=
new
List
(
listObj
);
newIssueMock
=
Promise
.
resolve
(
promiseReturn
);
jest
.
spyOn
(
list
,
'
newIssue
'
).
mockImplementation
(()
=>
newIssueMock
);
const
store
=
new
Vuex
.
Store
({
getters
:
{
isGroupBoard
:
()
=>
false
},
});
wrapper
=
mount
(
BoardNewIssueComp
,
{
propsData
:
{
disabled
:
false
,
list
,
},
store
,
provide
:
{
groupId
:
null
,
},
});
vm
=
wrapper
.
vm
;
return
Vue
.
nextTick
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
mock
.
restore
();
});
it
(
'
calls submit if submit button is clicked
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
,
'
submit
'
).
mockImplementation
();
vm
.
title
=
'
Testing Title
'
;
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
wrapper
.
vm
.
submit
).
toHaveBeenCalled
();
});
});
it
(
'
disables submit button if title is empty
'
,
()
=>
{
expect
(
wrapper
.
find
({
ref
:
'
submitButton
'
}).
props
().
disabled
).
toBe
(
true
);
});
it
(
'
enables submit button if title is not empty
'
,
()
=>
{
wrapper
.
setData
({
title
:
'
Testing Title
'
});
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
wrapper
.
find
({
ref
:
'
input
'
}).
element
.
value
).
toBe
(
'
Testing Title
'
);
expect
(
wrapper
.
find
({
ref
:
'
submitButton
'
}).
props
().
disabled
).
toBe
(
false
);
});
});
it
(
'
clears title after clicking cancel
'
,
()
=>
{
wrapper
.
find
({
ref
:
'
cancelButton
'
}).
trigger
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
expect
(
vm
.
title
).
toBe
(
''
);
});
});
it
(
'
does not create new issue if title is empty
'
,
()
=>
{
return
submitIssue
().
then
(()
=>
{
expect
(
list
.
newIssue
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'
submit success
'
,
()
=>
{
it
(
'
creates new issue
'
,
()
=>
{
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
list
.
newIssue
).
toHaveBeenCalled
();
});
});
it
(
'
enables button after submit
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
,
'
submit
'
).
mockImplementation
();
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
wrapper
.
vm
.
$refs
.
submitButton
.
props
().
disabled
).
toBe
(
false
);
});
});
it
(
'
clears title after submit
'
,
()
=>
{
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
vm
.
title
).
toBe
(
''
);
});
});
it
(
'
sets detail issue after submit
'
,
()
=>
{
expect
(
boardsStore
.
detail
.
issue
.
title
).
toBe
(
undefined
);
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
boardsStore
.
detail
.
issue
.
title
).
toBe
(
'
create issue
'
);
});
});
it
(
'
sets detail list after submit
'
,
()
=>
{
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
boardsStore
.
detail
.
list
.
id
).
toBe
(
list
.
id
);
});
});
it
(
'
sets detail weight after submit
'
,
()
=>
{
boardsStore
.
weightFeatureAvailable
=
true
;
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
boardsStore
.
detail
.
list
.
weight
).
toBe
(
list
.
weight
);
});
});
it
(
'
does not set detail weight after submit
'
,
()
=>
{
boardsStore
.
weightFeatureAvailable
=
false
;
wrapper
.
setData
({
title
:
'
create issue
'
});
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
boardsStore
.
detail
.
list
.
weight
).
toBe
(
list
.
weight
);
});
});
});
describe
(
'
submit error
'
,
()
=>
{
beforeEach
(()
=>
{
newIssueMock
=
Promise
.
reject
(
new
Error
(
'
My hovercraft is full of eels!
'
));
vm
.
title
=
'
error
'
;
});
it
(
'
removes issue
'
,
()
=>
{
const
lengthBefore
=
list
.
issues
.
length
;
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
list
.
issues
.
length
).
toBe
(
lengthBefore
);
});
});
it
(
'
shows error
'
,
()
=>
{
return
Vue
.
nextTick
()
.
then
(
submitIssue
)
.
then
(()
=>
{
expect
(
vm
.
error
).
toBe
(
true
);
});
});
});
});
spec/frontend/boards/components/board_card_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
/* global List */
/* global ListAssignee */
/* global ListLabel */
import
{
mount
}
from
'
@vue/test-utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
BoardCardDeprecated
from
'
~/boards/components/board_card_deprecated.vue
'
;
import
issueCardInner
from
'
~/boards/components/issue_card_inner_deprecated.vue
'
;
import
store
from
'
~/boards/stores
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
sidebarEventHub
from
'
~/sidebar/event_hub
'
;
import
'
~/boards/models/label
'
;
import
'
~/boards/models/assignee
'
;
import
'
~/boards/models/list
'
;
import
userAvatarLink
from
'
~/vue_shared/components/user_avatar/user_avatar_link.vue
'
;
import
{
listObj
,
boardsMockInterceptor
,
setMockEndpoints
}
from
'
../mock_data
'
;
describe
(
'
BoardCard
'
,
()
=>
{
let
wrapper
;
let
mock
;
let
list
;
const
findIssueCardInner
=
()
=>
wrapper
.
find
(
issueCardInner
);
const
findUserAvatarLink
=
()
=>
wrapper
.
find
(
userAvatarLink
);
// this particular mount component needs to be used after the root beforeEach because it depends on list being initialized
const
mountComponent
=
(
propsData
)
=>
{
wrapper
=
mount
(
BoardCardDeprecated
,
{
stubs
:
{
issueCardInner
,
},
store
,
propsData
:
{
list
,
issue
:
list
.
issues
[
0
],
disabled
:
false
,
index
:
0
,
...
propsData
,
},
provide
:
{
groupId
:
null
,
rootPath
:
'
/
'
,
scopedLabelsAvailable
:
false
,
},
});
};
const
setupData
=
async
()
=>
{
list
=
new
List
(
listObj
);
boardsStore
.
create
();
boardsStore
.
detail
.
issue
=
{};
const
label1
=
new
ListLabel
({
id
:
3
,
title
:
'
testing 123
'
,
color
:
'
#000cff
'
,
text_color
:
'
white
'
,
description
:
'
test
'
,
});
await
waitForPromises
();
list
.
issues
[
0
].
labels
.
push
(
label1
);
};
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
mock
.
onAny
().
reply
(
boardsMockInterceptor
);
setMockEndpoints
();
return
setupData
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
list
=
null
;
mock
.
restore
();
});
it
(
'
when details issue is empty does not show the element
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
find
(
'
[data-testid="board_card"
'
).
classes
()).
not
.
toContain
(
'
is-active
'
);
});
it
(
'
when detailIssue is equal to card issue shows the element
'
,
()
=>
{
[
boardsStore
.
detail
.
issue
]
=
list
.
issues
;
mountComponent
();
expect
(
wrapper
.
classes
()).
toContain
(
'
is-active
'
);
});
it
(
'
when multiSelect does not contain issue removes multi select class
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
classes
()).
not
.
toContain
(
'
multi-select
'
);
});
it
(
'
when multiSelect contain issue add multi select class
'
,
()
=>
{
boardsStore
.
multiSelect
.
list
=
[
list
.
issues
[
0
]];
mountComponent
();
expect
(
wrapper
.
classes
()).
toContain
(
'
multi-select
'
);
});
it
(
'
adds user-can-drag class if not disabled
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
classes
()).
toContain
(
'
user-can-drag
'
);
});
it
(
'
does not add user-can-drag class disabled
'
,
()
=>
{
mountComponent
({
disabled
:
true
});
expect
(
wrapper
.
classes
()).
not
.
toContain
(
'
user-can-drag
'
);
});
it
(
'
does not add disabled class
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
classes
()).
not
.
toContain
(
'
is-disabled
'
);
});
it
(
'
adds disabled class is disabled is true
'
,
()
=>
{
mountComponent
({
disabled
:
true
});
expect
(
wrapper
.
classes
()).
toContain
(
'
is-disabled
'
);
});
describe
(
'
mouse events
'
,
()
=>
{
it
(
'
does not set detail issue if showDetail is false
'
,
()
=>
{
mountComponent
();
expect
(
boardsStore
.
detail
.
issue
).
toEqual
({});
});
it
(
'
does not set detail issue if link is clicked
'
,
()
=>
{
mountComponent
();
findIssueCardInner
().
find
(
'
a
'
).
trigger
(
'
mouseup
'
);
expect
(
boardsStore
.
detail
.
issue
).
toEqual
({});
});
it
(
'
does not set detail issue if img is clicked
'
,
()
=>
{
mountComponent
({
issue
:
{
...
list
.
issues
[
0
],
assignees
:
[
new
ListAssignee
({
id
:
1
,
name
:
'
testing 123
'
,
username
:
'
test
'
,
avatar
:
'
test_image
'
,
}),
],
},
});
findUserAvatarLink
().
trigger
(
'
mouseup
'
);
expect
(
boardsStore
.
detail
.
issue
).
toEqual
({});
});
it
(
'
does not set detail issue if showDetail is false after mouseup
'
,
()
=>
{
mountComponent
();
wrapper
.
trigger
(
'
mouseup
'
);
expect
(
boardsStore
.
detail
.
issue
).
toEqual
({});
});
});
describe
(
'
sidebarHub events
'
,
()
=>
{
it
(
'
it does not closes all sidebars before showing an issue if an issue is opened
'
,
()
=>
{
jest
.
spyOn
(
sidebarEventHub
,
'
$emit
'
).
mockImplementation
(()
=>
{});
const
[
issue
]
=
list
.
issues
;
boardsStore
.
detail
.
issue
=
issue
;
mountComponent
();
wrapper
.
trigger
(
'
mousedown
'
);
expect
(
sidebarEventHub
.
$emit
).
not
.
toHaveBeenCalledWith
(
'
sidebar.closeAll
'
);
});
});
});
spec/frontend/boards/components/board_card_layout_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
/* global List */
/* global ListLabel */
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
Vuex
from
'
vuex
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
'
~/boards/models/label
'
;
import
'
~/boards/models/assignee
'
;
import
'
~/boards/models/list
'
;
import
BoardCardLayout
from
'
~/boards/components/board_card_layout_deprecated.vue
'
;
import
issueCardInner
from
'
~/boards/components/issue_card_inner_deprecated.vue
'
;
import
{
ISSUABLE
}
from
'
~/boards/constants
'
;
import
boardsVuexStore
from
'
~/boards/stores
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
listObj
,
boardsMockInterceptor
,
setMockEndpoints
}
from
'
../mock_data
'
;
describe
(
'
Board card layout
'
,
()
=>
{
let
wrapper
;
let
mock
;
let
list
;
let
store
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
const
createStore
=
({
getters
=
{},
actions
=
{}
}
=
{})
=>
{
store
=
new
Vuex
.
Store
({
...
boardsVuexStore
,
actions
,
getters
,
});
};
// this particular mount component needs to be used after the root beforeEach because it depends on list being initialized
const
mountComponent
=
({
propsData
=
{},
provide
=
{}
}
=
{})
=>
{
wrapper
=
shallowMount
(
BoardCardLayout
,
{
localVue
,
stubs
:
{
issueCardInner
,
},
store
,
propsData
:
{
list
,
issue
:
list
.
issues
[
0
],
disabled
:
false
,
index
:
0
,
...
propsData
,
},
provide
:
{
groupId
:
null
,
rootPath
:
'
/
'
,
scopedLabelsAvailable
:
false
,
...
provide
,
},
});
};
const
setupData
=
()
=>
{
list
=
new
List
(
listObj
);
boardsStore
.
create
();
boardsStore
.
detail
.
issue
=
{};
const
label1
=
new
ListLabel
({
id
:
3
,
title
:
'
testing 123
'
,
color
:
'
#000cff
'
,
text_color
:
'
white
'
,
description
:
'
test
'
,
});
return
waitForPromises
().
then
(()
=>
{
list
.
issues
[
0
].
labels
.
push
(
label1
);
});
};
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
mock
.
onAny
().
reply
(
boardsMockInterceptor
);
setMockEndpoints
();
return
setupData
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
list
=
null
;
mock
.
restore
();
});
describe
(
'
mouse events
'
,
()
=>
{
it
(
'
sets showDetail to true on mousedown
'
,
async
()
=>
{
createStore
();
mountComponent
();
wrapper
.
trigger
(
'
mousedown
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
showDetail
).
toBe
(
true
);
});
it
(
'
sets showDetail to false on mousemove
'
,
async
()
=>
{
createStore
();
mountComponent
();
wrapper
.
trigger
(
'
mousedown
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
showDetail
).
toBe
(
true
);
wrapper
.
trigger
(
'
mousemove
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
showDetail
).
toBe
(
false
);
});
it
(
"
calls 'setActiveId'
"
,
async
()
=>
{
const
setActiveId
=
jest
.
fn
();
createStore
({
actions
:
{
setActiveId
,
},
});
mountComponent
();
wrapper
.
trigger
(
'
mouseup
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
setActiveId
).
toHaveBeenCalledTimes
(
1
);
expect
(
setActiveId
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
{
id
:
list
.
issues
[
0
].
id
,
sidebarType
:
ISSUABLE
,
});
});
it
(
"
calls 'setActiveId' when epic swimlanes is active
"
,
async
()
=>
{
const
setActiveId
=
jest
.
fn
();
const
isSwimlanesOn
=
()
=>
true
;
createStore
({
getters
:
{
isSwimlanesOn
},
actions
:
{
setActiveId
,
},
});
mountComponent
();
wrapper
.
trigger
(
'
mouseup
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
setActiveId
).
toHaveBeenCalledTimes
(
1
);
expect
(
setActiveId
).
toHaveBeenCalledWith
(
expect
.
any
(
Object
),
{
id
:
list
.
issues
[
0
].
id
,
sidebarType
:
ISSUABLE
,
});
});
});
});
spec/frontend/boards/components/board_column_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
AxiosMockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
listObj
}
from
'
jest/boards/mock_data
'
;
import
Board
from
'
~/boards/components/board_column_deprecated.vue
'
;
import
{
ListType
}
from
'
~/boards/constants
'
;
import
List
from
'
~/boards/models/list
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
describe
(
'
Board Column Component
'
,
()
=>
{
let
wrapper
;
let
axiosMock
;
beforeEach
(()
=>
{
window
.
gon
=
{};
axiosMock
=
new
AxiosMockAdapter
(
axios
);
axiosMock
.
onGet
(
`
${
TEST_HOST
}
/lists/1/issues`
).
reply
(
200
,
{
issues
:
[]
});
});
afterEach
(()
=>
{
axiosMock
.
restore
();
wrapper
.
destroy
();
localStorage
.
clear
();
});
const
createComponent
=
({
listType
=
ListType
.
backlog
,
collapsed
=
false
,
highlighted
=
false
,
withLocalStorage
=
true
,
}
=
{})
=>
{
const
boardId
=
'
1
'
;
const
listMock
=
{
...
listObj
,
list_type
:
listType
,
highlighted
,
collapsed
,
};
if
(
listType
===
ListType
.
assignee
)
{
delete
listMock
.
label
;
listMock
.
user
=
{};
}
// Making List reactive
const
list
=
Vue
.
observable
(
new
List
(
listMock
));
if
(
withLocalStorage
)
{
localStorage
.
setItem
(
`boards.
${
boardId
}
.
${
list
.
type
}
.
${
list
.
id
}
.expanded`
,
(
!
collapsed
).
toString
(),
);
}
wrapper
=
shallowMount
(
Board
,
{
propsData
:
{
boardId
,
disabled
:
false
,
list
,
},
provide
:
{
boardId
,
},
});
};
const
isExpandable
=
()
=>
wrapper
.
classes
(
'
is-expandable
'
);
const
isCollapsed
=
()
=>
wrapper
.
classes
(
'
is-collapsed
'
);
describe
(
'
Given different list types
'
,
()
=>
{
it
(
'
is expandable when List Type is `backlog`
'
,
()
=>
{
createComponent
({
listType
:
ListType
.
backlog
});
expect
(
isExpandable
()).
toBe
(
true
);
});
});
describe
(
'
expanded / collapsed column
'
,
()
=>
{
it
(
'
has class is-collapsed when list is collapsed
'
,
()
=>
{
createComponent
({
collapsed
:
false
});
expect
(
wrapper
.
vm
.
list
.
isExpanded
).
toBe
(
true
);
});
it
(
'
does not have class is-collapsed when list is expanded
'
,
()
=>
{
createComponent
({
collapsed
:
true
});
expect
(
isCollapsed
()).
toBe
(
true
);
});
});
describe
(
'
highlighting
'
,
()
=>
{
it
(
'
scrolls to column when highlighted
'
,
async
()
=>
{
createComponent
({
highlighted
:
true
});
await
nextTick
();
expect
(
wrapper
.
element
.
scrollIntoView
).
toHaveBeenCalled
();
});
});
});
spec/frontend/boards/components/board_list_header_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
AxiosMockAdapter
from
'
axios-mock-adapter
'
;
import
Vue
from
'
vue
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
listObj
}
from
'
jest/boards/mock_data
'
;
import
BoardListHeader
from
'
~/boards/components/board_list_header_deprecated.vue
'
;
import
{
ListType
}
from
'
~/boards/constants
'
;
import
List
from
'
~/boards/models/list
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
describe
(
'
Board List Header Component
'
,
()
=>
{
let
wrapper
;
let
axiosMock
;
beforeEach
(()
=>
{
window
.
gon
=
{};
axiosMock
=
new
AxiosMockAdapter
(
axios
);
axiosMock
.
onGet
(
`
${
TEST_HOST
}
/lists/1/issues`
).
reply
(
200
,
{
issues
:
[]
});
});
afterEach
(()
=>
{
axiosMock
.
restore
();
wrapper
.
destroy
();
localStorage
.
clear
();
});
const
createComponent
=
({
listType
=
ListType
.
backlog
,
collapsed
=
false
,
withLocalStorage
=
true
,
currentUserId
=
1
,
}
=
{})
=>
{
const
boardId
=
'
1
'
;
const
listMock
=
{
...
listObj
,
list_type
:
listType
,
collapsed
,
};
if
(
listType
===
ListType
.
assignee
)
{
delete
listMock
.
label
;
listMock
.
user
=
{};
}
// Making List reactive
const
list
=
Vue
.
observable
(
new
List
(
listMock
));
if
(
withLocalStorage
)
{
localStorage
.
setItem
(
`boards.
${
boardId
}
.
${
list
.
type
}
.
${
list
.
id
}
.expanded`
,
(
!
collapsed
).
toString
(),
);
}
wrapper
=
shallowMount
(
BoardListHeader
,
{
propsData
:
{
disabled
:
false
,
list
,
},
provide
:
{
boardId
,
currentUserId
,
},
});
};
const
isCollapsed
=
()
=>
!
wrapper
.
props
().
list
.
isExpanded
;
const
isExpanded
=
()
=>
wrapper
.
vm
.
list
.
isExpanded
;
const
findAddIssueButton
=
()
=>
wrapper
.
find
({
ref
:
'
newIssueBtn
'
});
const
findCaret
=
()
=>
wrapper
.
find
(
'
.board-title-caret
'
);
describe
(
'
Add issue button
'
,
()
=>
{
const
hasNoAddButton
=
[
ListType
.
closed
];
const
hasAddButton
=
[
ListType
.
backlog
,
ListType
.
label
,
ListType
.
milestone
,
ListType
.
iteration
,
ListType
.
assignee
,
];
it
.
each
(
hasNoAddButton
)(
'
does not render when List Type is `%s`
'
,
(
listType
)
=>
{
createComponent
({
listType
});
expect
(
findAddIssueButton
().
exists
()).
toBe
(
false
);
});
it
.
each
(
hasAddButton
)(
'
does render when List Type is `%s`
'
,
(
listType
)
=>
{
createComponent
({
listType
});
expect
(
findAddIssueButton
().
exists
()).
toBe
(
true
);
});
it
(
'
has a test for each list type
'
,
()
=>
{
Object
.
values
(
ListType
).
forEach
((
value
)
=>
{
expect
([...
hasAddButton
,
...
hasNoAddButton
]).
toContain
(
value
);
});
});
it
(
'
does not render when logged out
'
,
()
=>
{
createComponent
({
currentUserId
:
null
,
});
expect
(
findAddIssueButton
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
expanding / collapsing the column
'
,
()
=>
{
it
(
'
does not collapse when clicking the header
'
,
()
=>
{
createComponent
();
expect
(
isCollapsed
()).
toBe
(
false
);
wrapper
.
find
(
'
[data-testid="board-list-header"]
'
).
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
isCollapsed
()).
toBe
(
false
);
});
});
it
(
'
collapses expanded Column when clicking the collapse icon
'
,
()
=>
{
createComponent
();
expect
(
isExpanded
()).
toBe
(
true
);
findCaret
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
isCollapsed
()).
toBe
(
true
);
});
});
it
(
'
expands collapsed Column when clicking the expand icon
'
,
()
=>
{
createComponent
({
collapsed
:
true
});
expect
(
isCollapsed
()).
toBe
(
true
);
findCaret
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
isCollapsed
()).
toBe
(
false
);
});
});
it
(
"
when logged in it calls list update and doesn't set localStorage
"
,
()
=>
{
jest
.
spyOn
(
List
.
prototype
,
'
update
'
);
createComponent
({
withLocalStorage
:
false
});
findCaret
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
list
.
update
).
toHaveBeenCalledTimes
(
1
);
expect
(
localStorage
.
getItem
(
`
${
wrapper
.
vm
.
uniqueKey
}
.expanded`
)).
toBe
(
null
);
});
});
it
(
"
when logged out it doesn't call list update and sets localStorage
"
,
()
=>
{
jest
.
spyOn
(
List
.
prototype
,
'
update
'
);
createComponent
({
currentUserId
:
null
});
findCaret
().
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
list
.
update
).
not
.
toHaveBeenCalled
();
expect
(
localStorage
.
getItem
(
`
${
wrapper
.
vm
.
uniqueKey
}
.expanded`
)).
toBe
(
String
(
isExpanded
()));
});
});
});
});
spec/frontend/boards/components/boards_selector_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
GlDropdown
,
GlLoadingIcon
,
GlDropdownSectionHeader
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
{
TEST_HOST
}
from
'
spec/test_constants
'
;
import
BoardsSelector
from
'
~/boards/components/boards_selector_deprecated.vue
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
const
throttleDuration
=
1
;
function
boardGenerator
(
n
)
{
return
new
Array
(
n
).
fill
().
map
((
board
,
index
)
=>
{
const
id
=
`
${
index
}
`
;
const
name
=
`board
${
id
}
`
;
return
{
id
,
name
,
};
});
}
describe
(
'
BoardsSelector
'
,
()
=>
{
let
wrapper
;
let
allBoardsResponse
;
let
recentBoardsResponse
;
const
boards
=
boardGenerator
(
20
);
const
recentBoards
=
boardGenerator
(
5
);
const
fillSearchBox
=
(
filterTerm
)
=>
{
const
searchBox
=
wrapper
.
find
({
ref
:
'
searchBox
'
});
const
searchBoxInput
=
searchBox
.
find
(
'
input
'
);
searchBoxInput
.
setValue
(
filterTerm
);
searchBoxInput
.
trigger
(
'
input
'
);
};
const
getDropdownItems
=
()
=>
wrapper
.
findAll
(
'
.js-dropdown-item
'
);
const
getDropdownHeaders
=
()
=>
wrapper
.
findAll
(
GlDropdownSectionHeader
);
const
getLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
findDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
beforeEach
(()
=>
{
const
$apollo
=
{
queries
:
{
boards
:
{
loading
:
false
,
},
},
};
boardsStore
.
setEndpoints
({
boardsEndpoint
:
''
,
recentBoardsEndpoint
:
''
,
listsEndpoint
:
''
,
bulkUpdatePath
:
''
,
boardId
:
''
,
});
allBoardsResponse
=
Promise
.
resolve
({
data
:
{
group
:
{
boards
:
{
edges
:
boards
.
map
((
board
)
=>
({
node
:
board
})),
},
},
},
});
recentBoardsResponse
=
Promise
.
resolve
({
data
:
recentBoards
,
});
boardsStore
.
allBoards
=
jest
.
fn
(()
=>
allBoardsResponse
);
boardsStore
.
recentBoards
=
jest
.
fn
(()
=>
recentBoardsResponse
);
wrapper
=
mount
(
BoardsSelector
,
{
propsData
:
{
throttleDuration
,
currentBoard
:
{
id
:
1
,
name
:
'
Development
'
,
milestone_id
:
null
,
weight
:
null
,
assignee_id
:
null
,
labels
:
[],
},
boardBaseUrl
:
`
${
TEST_HOST
}
/board/base/url`
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
multipleIssueBoardsAvailable
:
true
,
labelsPath
:
`
${
TEST_HOST
}
/labels/path`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/labels`
,
projectId
:
42
,
groupId
:
19
,
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
},
mocks
:
{
$apollo
},
attachTo
:
document
.
body
,
});
wrapper
.
vm
.
$apollo
.
addSmartQuery
=
jest
.
fn
((
_
,
options
)
=>
{
wrapper
.
setData
({
[
options
.
loadingKey
]:
true
,
});
});
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
findDropdown
().
vm
.
$emit
(
'
show
'
);
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
loading
'
,
()
=>
{
// we are testing loading state, so don't resolve responses until after the tests
afterEach
(()
=>
{
return
Promise
.
all
([
allBoardsResponse
,
recentBoardsResponse
]).
then
(()
=>
nextTick
());
});
it
(
'
shows loading spinner
'
,
()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
expect
(
getDropdownItems
()).
toHaveLength
(
0
);
expect
(
getLoadingIcon
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
loaded
'
,
()
=>
{
beforeEach
(
async
()
=>
{
await
wrapper
.
setData
({
loadingBoards
:
false
,
});
return
Promise
.
all
([
allBoardsResponse
,
recentBoardsResponse
]).
then
(()
=>
nextTick
());
});
it
(
'
hides loading spinner
'
,
()
=>
{
expect
(
getLoadingIcon
().
exists
()).
toBe
(
false
);
});
describe
(
'
filtering
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
.
setData
({
boards
,
});
return
nextTick
();
});
it
(
'
shows all boards without filtering
'
,
()
=>
{
expect
(
getDropdownItems
()).
toHaveLength
(
boards
.
length
+
recentBoards
.
length
);
});
it
(
'
shows only matching boards when filtering
'
,
()
=>
{
const
filterTerm
=
'
board1
'
;
const
expectedCount
=
boards
.
filter
((
board
)
=>
board
.
name
.
includes
(
filterTerm
)).
length
;
fillSearchBox
(
filterTerm
);
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownItems
()).
toHaveLength
(
expectedCount
);
});
});
it
(
'
shows message if there are no matching boards
'
,
()
=>
{
fillSearchBox
(
'
does not exist
'
);
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownItems
()).
toHaveLength
(
0
);
expect
(
wrapper
.
text
().
includes
(
'
No matching boards found
'
)).
toBe
(
true
);
});
});
});
describe
(
'
recent boards section
'
,
()
=>
{
it
(
'
shows only when boards are greater than 10
'
,
()
=>
{
wrapper
.
setData
({
boards
,
});
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
2
);
});
});
it
(
'
does not show when boards are less than 10
'
,
()
=>
{
wrapper
.
setData
({
boards
:
boards
.
slice
(
0
,
5
),
});
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
});
});
it
(
'
does not show when recentBoards api returns empty array
'
,
()
=>
{
wrapper
.
setData
({
recentBoards
:
[],
});
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
});
});
it
(
'
does not show when search is active
'
,
()
=>
{
fillSearchBox
(
'
Random string
'
);
return
nextTick
().
then
(()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
});
});
});
});
});
spec/frontend/boards/components/issue_time_estimate_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
IssueTimeEstimate
from
'
~/boards/components/issue_time_estimate_deprecated.vue
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
describe
(
'
Issue Time Estimate component
'
,
()
=>
{
let
wrapper
;
beforeEach
(()
=>
{
boardsStore
.
create
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
when limitToHours is false
'
,
()
=>
{
beforeEach
(()
=>
{
boardsStore
.
timeTracking
.
limitToHours
=
false
;
wrapper
=
shallowMount
(
IssueTimeEstimate
,
{
propsData
:
{
estimate
:
374460
,
},
});
});
it
(
'
renders the correct time estimate
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
time
'
).
text
().
trim
()).
toEqual
(
'
2w 3d 1m
'
);
});
it
(
'
renders expanded time estimate in tooltip
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-issue-time-estimate
'
).
text
()).
toContain
(
'
2 weeks 3 days 1 minute
'
);
});
it
(
'
prevents tooltip xss
'
,
(
done
)
=>
{
const
alertSpy
=
jest
.
spyOn
(
window
,
'
alert
'
);
wrapper
.
setProps
({
estimate
:
'
Foo <script>alert("XSS")</script>
'
});
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
alertSpy
).
not
.
toHaveBeenCalled
();
expect
(
wrapper
.
find
(
'
time
'
).
text
().
trim
()).
toEqual
(
'
0m
'
);
expect
(
wrapper
.
find
(
'
.js-issue-time-estimate
'
).
text
()).
toContain
(
'
0m
'
);
done
();
});
});
});
describe
(
'
when limitToHours is true
'
,
()
=>
{
beforeEach
(()
=>
{
boardsStore
.
timeTracking
.
limitToHours
=
true
;
wrapper
=
shallowMount
(
IssueTimeEstimate
,
{
propsData
:
{
estimate
:
374460
,
},
});
});
it
(
'
renders the correct time estimate
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
time
'
).
text
().
trim
()).
toEqual
(
'
104h 1m
'
);
});
it
(
'
renders expanded time estimate in tooltip
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-issue-time-estimate
'
).
text
()).
toContain
(
'
104 hours 1 minute
'
);
});
});
});
spec/frontend/boards/issue_card_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
/* global ListAssignee, ListLabel, ListIssue */
import
{
GlLabel
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
range
}
from
'
lodash
'
;
import
'
~/boards/models/label
'
;
import
'
~/boards/models/assignee
'
;
import
'
~/boards/models/issue
'
;
import
'
~/boards/models/list
'
;
import
IssueCardInner
from
'
~/boards/components/issue_card_inner_deprecated.vue
'
;
import
store
from
'
~/boards/stores
'
;
import
{
listObj
}
from
'
./mock_data
'
;
describe
(
'
Issue card component
'
,
()
=>
{
const
user
=
new
ListAssignee
({
id
:
1
,
name
:
'
testing 123
'
,
username
:
'
test
'
,
avatar
:
'
test_image
'
,
});
const
label1
=
new
ListLabel
({
id
:
3
,
title
:
'
testing 123
'
,
color
:
'
#000CFF
'
,
text_color
:
'
white
'
,
description
:
'
test
'
,
});
let
wrapper
;
let
issue
;
let
list
;
beforeEach
(()
=>
{
list
=
{
...
listObj
,
type
:
'
label
'
};
issue
=
new
ListIssue
({
title
:
'
Testing
'
,
id
:
1
,
iid
:
1
,
confidential
:
false
,
labels
:
[
list
.
label
],
assignees
:
[],
reference_path
:
'
#1
'
,
real_path
:
'
/test/1
'
,
weight
:
1
,
});
wrapper
=
mount
(
IssueCardInner
,
{
propsData
:
{
list
,
issue
,
},
store
,
stubs
:
{
GlLabel
:
true
,
},
provide
:
{
groupId
:
null
,
rootPath
:
'
/
'
,
},
});
});
it
(
'
renders issue title
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-title
'
).
text
()).
toContain
(
issue
.
title
);
});
it
(
'
includes issue base in link
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-title a
'
).
attributes
(
'
href
'
)).
toContain
(
'
/test
'
);
});
it
(
'
includes issue title on link
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-title a
'
).
attributes
(
'
title
'
)).
toBe
(
issue
.
title
);
});
it
(
'
does not render confidential icon
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.confidential-icon
'
).
exists
()).
toBe
(
false
);
});
it
(
'
does not render blocked icon
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.issue-blocked-icon
'
).
exists
()).
toBe
(
false
);
});
it
(
'
renders confidential icon
'
,
(
done
)
=>
{
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
confidential
:
true
,
},
});
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
'
.confidential-icon
'
).
exists
()).
toBe
(
true
);
done
();
});
});
it
(
'
renders issue ID with #
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-number
'
).
text
()).
toContain
(
`#
${
issue
.
id
}
`
);
});
describe
(
'
assignee
'
,
()
=>
{
it
(
'
does not render assignee
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee .avatar
'
).
exists
()).
toBe
(
false
);
});
describe
(
'
exists
'
,
()
=>
{
beforeEach
((
done
)
=>
{
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
assignees
:
[
user
],
updateData
(
newData
)
{
Object
.
assign
(
this
,
newData
);
},
},
});
wrapper
.
vm
.
$nextTick
(
done
);
});
it
(
'
renders assignee
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee .avatar
'
).
exists
()).
toBe
(
true
);
});
it
(
'
sets title
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.js-assignee-tooltip
'
).
text
()).
toContain
(
`
${
user
.
name
}
`
);
});
it
(
'
sets users path
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee a
'
).
attributes
(
'
href
'
)).
toBe
(
'
/test
'
);
});
it
(
'
renders avatar
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee img
'
).
exists
()).
toBe
(
true
);
});
it
(
'
renders the avatar using avatar_url property
'
,
(
done
)
=>
{
wrapper
.
props
(
'
issue
'
).
updateData
({
...
wrapper
.
props
(
'
issue
'
),
assignees
:
[
{
id
:
'
1
'
,
name
:
'
test
'
,
state
:
'
active
'
,
username
:
'
test_name
'
,
avatar_url
:
'
test_image_from_avatar_url
'
,
},
],
});
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee img
'
).
attributes
(
'
src
'
)).
toBe
(
'
test_image_from_avatar_url?width=24
'
,
);
done
();
});
});
});
describe
(
'
assignee default avatar
'
,
()
=>
{
beforeEach
((
done
)
=>
{
global
.
gon
.
default_avatar_url
=
'
default_avatar
'
;
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
assignees
:
[
new
ListAssignee
({
id
:
1
,
name
:
'
testing 123
'
,
username
:
'
test
'
,
}),
],
},
});
wrapper
.
vm
.
$nextTick
(
done
);
});
afterEach
(()
=>
{
global
.
gon
.
default_avatar_url
=
null
;
});
it
(
'
displays defaults avatar if users avatar is null
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee img
'
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
(
'
.board-card-assignee img
'
).
attributes
(
'
src
'
)).
toBe
(
'
default_avatar?width=24
'
,
);
});
});
});
describe
(
'
multiple assignees
'
,
()
=>
{
beforeEach
((
done
)
=>
{
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
assignees
:
[
new
ListAssignee
({
id
:
2
,
name
:
'
user2
'
,
username
:
'
user2
'
,
avatar
:
'
test_image
'
,
}),
new
ListAssignee
({
id
:
3
,
name
:
'
user3
'
,
username
:
'
user3
'
,
avatar
:
'
test_image
'
,
}),
new
ListAssignee
({
id
:
4
,
name
:
'
user4
'
,
username
:
'
user4
'
,
avatar
:
'
test_image
'
,
}),
],
},
});
wrapper
.
vm
.
$nextTick
(
done
);
});
it
(
'
renders all three assignees
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
'
.board-card-assignee .avatar
'
).
length
).
toEqual
(
3
);
});
describe
(
'
more than three assignees
'
,
()
=>
{
beforeEach
((
done
)
=>
{
const
{
assignees
}
=
wrapper
.
props
(
'
issue
'
);
assignees
.
push
(
new
ListAssignee
({
id
:
5
,
name
:
'
user5
'
,
username
:
'
user5
'
,
avatar
:
'
test_image
'
,
}),
);
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
assignees
,
},
});
wrapper
.
vm
.
$nextTick
(
done
);
});
it
(
'
renders more avatar counter
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee .avatar-counter
'
).
text
().
trim
()).
toEqual
(
'
+2
'
);
});
it
(
'
renders two assignees
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
'
.board-card-assignee .avatar
'
).
length
).
toEqual
(
2
);
});
it
(
'
renders 99+ avatar counter
'
,
(
done
)
=>
{
const
assignees
=
[
...
wrapper
.
props
(
'
issue
'
).
assignees
,
...
range
(
5
,
103
).
map
(
(
i
)
=>
new
ListAssignee
({
id
:
i
,
name
:
'
name
'
,
username
:
'
username
'
,
avatar
:
'
test_image
'
,
}),
),
];
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
assignees
,
},
});
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
'
.board-card-assignee .avatar-counter
'
).
text
().
trim
()).
toEqual
(
'
99+
'
);
done
();
});
});
});
});
describe
(
'
labels
'
,
()
=>
{
beforeEach
((
done
)
=>
{
issue
.
addLabel
(
label1
);
wrapper
.
setProps
({
issue
:
{
...
issue
}
});
wrapper
.
vm
.
$nextTick
(
done
);
});
it
(
'
does not render list label but renders all other labels
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
GlLabel
).
length
).
toBe
(
1
);
const
label
=
wrapper
.
find
(
GlLabel
);
expect
(
label
.
props
(
'
title
'
)).
toEqual
(
label1
.
title
);
expect
(
label
.
props
(
'
description
'
)).
toEqual
(
label1
.
description
);
expect
(
label
.
props
(
'
backgroundColor
'
)).
toEqual
(
label1
.
color
);
});
it
(
'
does not render label if label does not have an ID
'
,
(
done
)
=>
{
issue
.
addLabel
(
new
ListLabel
({
title
:
'
closed
'
,
}),
);
wrapper
.
setProps
({
issue
:
{
...
issue
}
});
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
expect
(
wrapper
.
findAll
(
GlLabel
).
length
).
toBe
(
1
);
expect
(
wrapper
.
text
()).
not
.
toContain
(
'
closed
'
);
done
();
})
.
catch
(
done
.
fail
);
});
});
describe
(
'
blocked
'
,
()
=>
{
beforeEach
((
done
)
=>
{
wrapper
.
setProps
({
issue
:
{
...
wrapper
.
props
(
'
issue
'
),
blocked
:
true
,
},
});
wrapper
.
vm
.
$nextTick
(
done
);
});
it
(
'
renders blocked icon if issue is blocked
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.issue-blocked-icon
'
).
exists
()).
toBe
(
true
);
});
});
});
spec/frontend/boards/project_select_deprecated_spec.js
deleted
100644 → 0
View file @
fe5831c6
import
{
GlDropdown
,
GlDropdownItem
,
GlSearchBoxByType
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
axios
from
'
axios
'
;
import
AxiosMockAdapter
from
'
axios-mock-adapter
'
;
import
ProjectSelect
from
'
~/boards/components/project_select_deprecated.vue
'
;
import
{
ListType
}
from
'
~/boards/constants
'
;
import
eventHub
from
'
~/boards/eventhub
'
;
import
createFlash
from
'
~/flash
'
;
import
httpStatus
from
'
~/lib/utils/http_status
'
;
import
{
featureAccessLevel
}
from
'
~/pages/projects/shared/permissions/constants
'
;
import
{
listObj
,
mockRawGroupProjects
}
from
'
./mock_data
'
;
jest
.
mock
(
'
~/boards/eventhub
'
);
jest
.
mock
(
'
~/flash
'
);
const
dummyGon
=
{
api_version
:
'
v4
'
,
relative_url_root
:
'
/gitlab
'
,
};
const
mockGroupId
=
1
;
const
mockProjectsList1
=
mockRawGroupProjects
.
slice
(
0
,
1
);
const
mockProjectsList2
=
mockRawGroupProjects
.
slice
(
1
);
const
mockDefaultFetchOptions
=
{
with_issues_enabled
:
true
,
with_shared
:
false
,
include_subgroups
:
true
,
order_by
:
'
similarity
'
,
archived
:
false
,
};
const
itemsPerPage
=
20
;
describe
(
'
ProjectSelect component
'
,
()
=>
{
let
wrapper
;
let
axiosMock
;
const
findLabel
=
()
=>
wrapper
.
find
(
"
[data-testid='header-label']
"
);
const
findGlDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
const
findGlDropdownLoadingIcon
=
()
=>
findGlDropdown
().
find
(
'
button:first-child
'
).
find
(
GlLoadingIcon
);
const
findGlSearchBoxByType
=
()
=>
wrapper
.
find
(
GlSearchBoxByType
);
const
findGlDropdownItems
=
()
=>
wrapper
.
findAll
(
GlDropdownItem
);
const
findFirstGlDropdownItem
=
()
=>
findGlDropdownItems
().
at
(
0
);
const
findInMenuLoadingIcon
=
()
=>
wrapper
.
find
(
"
[data-testid='dropdown-text-loading-icon']
"
);
const
findEmptySearchMessage
=
()
=>
wrapper
.
find
(
"
[data-testid='empty-result-message']
"
);
const
mockGetRequest
=
(
data
=
[],
statusCode
=
httpStatus
.
OK
)
=>
{
axiosMock
.
onGet
(
`/gitlab/api/v4/groups/
${
mockGroupId
}
/projects.json`
)
.
replyOnce
(
statusCode
,
data
);
};
const
searchForProject
=
async
(
keyword
,
waitForAll
=
true
)
=>
{
findGlSearchBoxByType
().
vm
.
$emit
(
'
input
'
,
keyword
);
if
(
waitForAll
)
{
await
axios
.
waitForAll
();
}
};
const
createWrapper
=
async
({
list
=
listObj
}
=
{},
waitForAll
=
true
)
=>
{
wrapper
=
mount
(
ProjectSelect
,
{
propsData
:
{
list
,
},
provide
:
{
groupId
:
1
,
},
});
if
(
waitForAll
)
{
await
axios
.
waitForAll
();
}
};
beforeEach
(()
=>
{
axiosMock
=
new
AxiosMockAdapter
(
axios
);
window
.
gon
=
dummyGon
;
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
axiosMock
.
restore
();
jest
.
clearAllMocks
();
});
it
(
'
displays a header title
'
,
async
()
=>
{
createWrapper
({});
expect
(
findLabel
().
text
()).
toBe
(
'
Projects
'
);
});
it
(
'
renders a default dropdown text
'
,
async
()
=>
{
createWrapper
({});
expect
(
findGlDropdown
().
exists
()).
toBe
(
true
);
expect
(
findGlDropdown
().
text
()).
toContain
(
'
Select a project
'
);
});
describe
(
'
when mounted
'
,
()
=>
{
it
(
'
displays a loading icon while projects are being fetched
'
,
async
()
=>
{
mockGetRequest
([]);
createWrapper
({},
false
);
expect
(
findGlDropdownLoadingIcon
().
exists
()).
toBe
(
true
);
await
axios
.
waitForAll
();
expect
(
axiosMock
.
history
.
get
[
0
].
params
).
toMatchObject
({
search
:
''
});
expect
(
axiosMock
.
history
.
get
[
0
].
url
).
toBe
(
`/gitlab/api/v4/groups/
${
mockGroupId
}
/projects.json`
,
);
expect
(
findGlDropdownLoadingIcon
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when dropdown menu is open
'
,
()
=>
{
describe
(
'
by default
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mockGetRequest
(
mockProjectsList1
);
await
createWrapper
();
});
it
(
'
shows GlSearchBoxByType with default attributes
'
,
()
=>
{
expect
(
findGlSearchBoxByType
().
exists
()).
toBe
(
true
);
expect
(
findGlSearchBoxByType
().
vm
.
$attrs
).
toMatchObject
({
placeholder
:
'
Search projects
'
,
debounce
:
'
250
'
,
});
});
it
(
"
displays the fetched project's name
"
,
()
=>
{
expect
(
findFirstGlDropdownItem
().
exists
()).
toBe
(
true
);
expect
(
findFirstGlDropdownItem
().
text
()).
toContain
(
mockProjectsList1
[
0
].
name
);
});
it
(
"
doesn't render loading icon in the menu
"
,
()
=>
{
expect
(
findInMenuLoadingIcon
().
isVisible
()).
toBe
(
false
);
});
it
(
'
renders empty search result message
'
,
async
()
=>
{
await
createWrapper
();
expect
(
findEmptySearchMessage
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
when a project is selected
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mockGetRequest
(
mockProjectsList1
);
await
createWrapper
();
await
findFirstGlDropdownItem
().
find
(
'
button
'
).
trigger
(
'
click
'
);
});
it
(
'
emits setSelectedProject with correct project metadata
'
,
()
=>
{
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
setSelectedProject
'
,
{
id
:
mockProjectsList1
[
0
].
id
,
path
:
mockProjectsList1
[
0
].
path_with_namespace
,
name
:
mockProjectsList1
[
0
].
name
,
namespacedName
:
mockProjectsList1
[
0
].
name_with_namespace
,
});
});
it
(
'
renders the name of the selected project
'
,
()
=>
{
expect
(
findGlDropdown
().
find
(
'
.gl-new-dropdown-button-text
'
).
text
()).
toBe
(
mockProjectsList1
[
0
].
name
,
);
});
});
describe
(
'
when user searches for a project
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mockGetRequest
(
mockProjectsList1
);
await
createWrapper
();
});
it
(
'
calls API with correct parameters with default fetch options
'
,
async
()
=>
{
await
searchForProject
(
'
foobar
'
);
const
expectedApiParams
=
{
search
:
'
foobar
'
,
per_page
:
itemsPerPage
,
...
mockDefaultFetchOptions
,
};
expect
(
axiosMock
.
history
.
get
[
1
].
params
).
toMatchObject
(
expectedApiParams
);
expect
(
axiosMock
.
history
.
get
[
1
].
url
).
toBe
(
`/gitlab/api/v4/groups/
${
mockGroupId
}
/projects.json`
,
);
});
describe
(
"
when list type is defined and isn't backlog
"
,
()
=>
{
it
(
'
calls API with an additional fetch option (min_access_level)
'
,
async
()
=>
{
axiosMock
.
reset
();
await
createWrapper
({
list
:
{
...
listObj
,
type
:
ListType
.
label
}
});
await
searchForProject
(
'
foobar
'
);
const
expectedApiParams
=
{
search
:
'
foobar
'
,
per_page
:
itemsPerPage
,
...
mockDefaultFetchOptions
,
min_access_level
:
featureAccessLevel
.
EVERYONE
,
};
expect
(
axiosMock
.
history
.
get
[
1
].
params
).
toMatchObject
(
expectedApiParams
);
expect
(
axiosMock
.
history
.
get
[
1
].
url
).
toBe
(
`/gitlab/api/v4/groups/
${
mockGroupId
}
/projects.json`
,
);
});
});
it
(
'
displays and hides gl-loading-icon while and after fetching data
'
,
async
()
=>
{
await
searchForProject
(
'
some keyword
'
,
false
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
findInMenuLoadingIcon
().
isVisible
()).
toBe
(
true
);
await
axios
.
waitForAll
();
expect
(
findInMenuLoadingIcon
().
isVisible
()).
toBe
(
false
);
});
it
(
'
flashes an error message when fetching fails
'
,
async
()
=>
{
mockGetRequest
([],
httpStatus
.
INTERNAL_SERVER_ERROR
);
await
searchForProject
(
'
foobar
'
);
expect
(
createFlash
).
toHaveBeenCalledTimes
(
1
);
expect
(
createFlash
).
toHaveBeenCalledWith
({
message
:
'
Something went wrong while fetching projects
'
,
});
});
describe
(
'
with non-empty search result
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mockGetRequest
(
mockProjectsList2
);
await
searchForProject
(
'
foobar
'
);
});
it
(
'
displays the retrieved list of projects
'
,
async
()
=>
{
expect
(
findFirstGlDropdownItem
().
text
()).
toContain
(
mockProjectsList2
[
0
].
name
);
});
it
(
'
does not render empty search result message
'
,
async
()
=>
{
expect
(
findEmptySearchMessage
().
exists
()).
toBe
(
false
);
});
});
});
});
});
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