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
703f89df
Commit
703f89df
authored
Nov 08, 2021
by
Florie Guibert
Committed by
Simon Knox
Nov 08, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor fetching board scope to GraphQL
Changelog: changed EE: true
parent
bff6c914
Changes
24
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
590 additions
and
255 deletions
+590
-255
app/assets/javascripts/boards/components/board_form.vue
app/assets/javascripts/boards/components/board_form.vue
+23
-20
app/assets/javascripts/boards/components/boards_selector.vue
app/assets/javascripts/boards/components/boards_selector.vue
+49
-14
app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
...s/javascripts/boards/graphql/board_scope.fragment.graphql
+6
-0
app/assets/javascripts/boards/graphql/group_board.query.graphql
...sets/javascripts/boards/graphql/group_board.query.graphql
+9
-0
app/assets/javascripts/boards/graphql/project_board.query.graphql
...ts/javascripts/boards/graphql/project_board.query.graphql
+9
-0
app/assets/javascripts/boards/mount_multiple_boards_switcher.js
...sets/javascripts/boards/mount_multiple_boards_switcher.js
+0
-1
app/assets/javascripts/graphql_shared/fragments/iteration.fragment.graphql
...ripts/graphql_shared/fragments/iteration.fragment.graphql
+4
-0
app/assets/javascripts/graphql_shared/utils.js
app/assets/javascripts/graphql_shared/utils.js
+8
-2
app/views/shared/boards/_switcher.html.haml
app/views/shared/boards/_switcher.html.haml
+1
-2
ee/app/assets/javascripts/boards/components/board_form.vue
ee/app/assets/javascripts/boards/components/board_form.vue
+1
-6
ee/app/assets/javascripts/boards/components/board_scope.vue
ee/app/assets/javascripts/boards/components/board_scope.vue
+5
-1
ee/app/assets/javascripts/boards/components/boards_selector.vue
.../assets/javascripts/boards/components/boards_selector.vue
+4
-0
ee/app/assets/javascripts/boards/components/labels_select.vue
...pp/assets/javascripts/boards/components/labels_select.vue
+2
-6
ee/app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
...s/javascripts/boards/graphql/board_scope.fragment.graphql
+26
-0
ee/app/assets/javascripts/boards/graphql/epic_board.query.graphql
...ssets/javascripts/boards/graphql/epic_board.query.graphql
+17
-0
ee/spec/features/boards/scoped_issue_board_spec.rb
ee/spec/features/boards/scoped_issue_board_spec.rb
+5
-0
ee/spec/frontend/boards/components/board_form_spec.js
ee/spec/frontend/boards/components/board_form_spec.js
+46
-16
ee/spec/frontend/boards/components/boards_selector_spec.js
ee/spec/frontend/boards/components/boards_selector_spec.js
+125
-70
ee/spec/frontend/boards/mock_data.js
ee/spec/frontend/boards/mock_data.js
+12
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/boards/components/board_form_spec.js
spec/frontend/boards/components/board_form_spec.js
+7
-8
spec/frontend/boards/components/boards_selector_spec.js
spec/frontend/boards/components/boards_selector_spec.js
+151
-96
spec/frontend/boards/mock_data.js
spec/frontend/boards/mock_data.js
+23
-13
spec/frontend/graphql_shared/utils_spec.js
spec/frontend/graphql_shared/utils_spec.js
+54
-0
No files found.
app/assets/javascripts/boards/components/board_form.vue
View file @
703f89df
...
@@ -2,10 +2,10 @@
...
@@ -2,10 +2,10 @@
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
mapGetters
,
mapActions
,
mapState
}
from
'
vuex
'
;
import
{
mapGetters
,
mapActions
,
mapState
}
from
'
vuex
'
;
import
{
TYPE_USER
,
TYPE_ITERATION
,
TYPE_MILESTONE
}
from
'
~/graphql_shared/constants
'
;
import
{
TYPE_USER
,
TYPE_ITERATION
,
TYPE_MILESTONE
}
from
'
~/graphql_shared/constants
'
;
import
{
convertToGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
convertToGraphQLId
,
getZeroBasedIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
getParameterByName
,
visitUrl
}
from
'
~/lib/utils/url_utility
'
;
import
{
getParameterByName
,
visitUrl
}
from
'
~/lib/utils/url_utility
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
fullLabelId
,
fullBoardId
}
from
'
../boards_util
'
;
import
{
fullLabelId
}
from
'
../boards_util
'
;
import
{
formType
}
from
'
../constants
'
;
import
{
formType
}
from
'
../constants
'
;
import
createBoardMutation
from
'
../graphql/board_create.mutation.graphql
'
;
import
createBoardMutation
from
'
../graphql/board_create.mutation.graphql
'
;
...
@@ -18,11 +18,11 @@ const boardDefaults = {
...
@@ -18,11 +18,11 @@ const boardDefaults = {
name
:
''
,
name
:
''
,
labels
:
[],
labels
:
[],
milestone
:
{},
milestone
:
{},
iteration
_id
:
undefined
,
iteration
:
{}
,
assignee
:
{},
assignee
:
{},
weight
:
null
,
weight
:
null
,
hide
_backlog_l
ist
:
false
,
hide
BacklogL
ist
:
false
,
hide
_closed_l
ist
:
false
,
hide
ClosedL
ist
:
false
,
};
};
export
default
{
export
default
{
...
@@ -144,17 +144,16 @@ export default {
...
@@ -144,17 +144,16 @@ export default {
return
destroyBoardMutation
;
return
destroyBoardMutation
;
},
},
baseMutationVariables
()
{
baseMutationVariables
()
{
const
{
board
}
=
this
;
const
{
const
variables
=
{
board
:
{
name
,
hideBacklogList
,
hideClosedList
,
id
},
name
:
board
.
name
,
}
=
this
;
hideBacklogList
:
board
.
hide_backlog_list
,
hideClosedList
:
board
.
hide_closed_list
,
const
variables
=
{
name
,
hideBacklogList
,
hideClosedList
};
};
return
board
.
id
return
id
?
{
?
{
...
variables
,
...
variables
,
id
:
fullBoardId
(
board
.
id
)
,
id
,
}
}
:
{
:
{
...
variables
,
...
variables
,
...
@@ -168,11 +167,13 @@ export default {
...
@@ -168,11 +167,13 @@ export default {
assigneeId
:
this
.
board
.
assignee
?.
id
assigneeId
:
this
.
board
.
assignee
?.
id
?
convertToGraphQLId
(
TYPE_USER
,
this
.
board
.
assignee
.
id
)
?
convertToGraphQLId
(
TYPE_USER
,
this
.
board
.
assignee
.
id
)
:
null
,
:
null
,
// Temporarily converting to milestone ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
milestoneId
:
this
.
board
.
milestone
?.
id
milestoneId
:
this
.
board
.
milestone
?.
id
?
convertToGraphQLId
(
TYPE_MILESTONE
,
this
.
board
.
milestone
.
id
)
?
convertToGraphQLId
(
TYPE_MILESTONE
,
getZeroBasedIdFromGraphQLId
(
this
.
board
.
milestone
.
id
)
)
:
null
,
:
null
,
iterationId
:
this
.
board
.
iteration_id
// Temporarily converting to iteration ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
?
convertToGraphQLId
(
TYPE_ITERATION
,
this
.
board
.
iteration_id
)
iterationId
:
this
.
board
.
iteration
?.
id
?
convertToGraphQLId
(
TYPE_ITERATION
,
getZeroBasedIdFromGraphQLId
(
this
.
board
.
iteration
.
id
))
:
null
,
:
null
,
};
};
},
},
...
@@ -226,7 +227,7 @@ export default {
...
@@ -226,7 +227,7 @@ export default {
await
this
.
$apollo
.
mutate
({
await
this
.
$apollo
.
mutate
({
mutation
:
this
.
deleteMutation
,
mutation
:
this
.
deleteMutation
,
variables
:
{
variables
:
{
id
:
fullBoardId
(
this
.
board
.
id
)
,
id
:
this
.
board
.
id
,
},
},
});
});
},
},
...
@@ -262,7 +263,9 @@ export default {
...
@@ -262,7 +263,9 @@ export default {
}
}
},
},
setIteration
(
iterationId
)
{
setIteration
(
iterationId
)
{
this
.
board
.
iteration_id
=
iterationId
;
this
.
$set
(
this
.
board
,
'
iteration
'
,
{
id
:
iterationId
,
});
},
},
setBoardLabels
(
labels
)
{
setBoardLabels
(
labels
)
{
this
.
board
.
labels
=
labels
;
this
.
board
.
labels
=
labels
;
...
@@ -329,8 +332,8 @@ export default {
...
@@ -329,8 +332,8 @@ export default {
</div>
</div>
<board-configuration-options
<board-configuration-options
:hide-backlog-list.sync=
"board.hide
_backlog_l
ist"
:hide-backlog-list.sync=
"board.hide
BacklogL
ist"
:hide-closed-list.sync=
"board.hide
_closed_l
ist"
:hide-closed-list.sync=
"board.hide
ClosedL
ist"
:readonly=
"readonly"
:readonly=
"readonly"
/>
/>
...
...
app/assets/javascripts/boards/components/boards_selector.vue
View file @
703f89df
...
@@ -9,17 +9,20 @@ import {
...
@@ -9,17 +9,20 @@ import {
GlModalDirective
,
GlModalDirective
,
}
from
'
@gitlab/ui
'
;
}
from
'
@gitlab/ui
'
;
import
{
throttle
}
from
'
lodash
'
;
import
{
throttle
}
from
'
lodash
'
;
import
{
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
map
Actions
,
map
Getters
,
mapState
}
from
'
vuex
'
;
import
BoardForm
from
'
ee_else_ce/boards/components/board_form.vue
'
;
import
BoardForm
from
'
ee_else_ce/boards/components/board_form.vue
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
httpStatusCodes
from
'
~/lib/utils/http_status
'
;
import
httpStatusCodes
from
'
~/lib/utils/http_status
'
;
import
{
s__
}
from
'
~/locale
'
;
import
eventHub
from
'
../eventhub
'
;
import
eventHub
from
'
../eventhub
'
;
import
groupQuery
from
'
../graphql/group_boards.query.graphql
'
;
import
groupBoardsQuery
from
'
../graphql/group_boards.query.graphql
'
;
import
projectQuery
from
'
../graphql/project_boards.query.graphql
'
;
import
projectBoardsQuery
from
'
../graphql/project_boards.query.graphql
'
;
import
groupBoardQuery
from
'
../graphql/group_board.query.graphql
'
;
import
projectBoardQuery
from
'
../graphql/project_board.query.graphql
'
;
const
MIN_BOARDS_TO_VIEW_RECENT
=
10
;
const
MIN_BOARDS_TO_VIEW_RECENT
=
10
;
...
@@ -39,10 +42,6 @@ export default {
...
@@ -39,10 +42,6 @@ export default {
},
},
inject
:
[
'
fullPath
'
,
'
recentBoardsEndpoint
'
],
inject
:
[
'
fullPath
'
,
'
recentBoardsEndpoint
'
],
props
:
{
props
:
{
currentBoard
:
{
type
:
Object
,
required
:
true
,
},
throttleDuration
:
{
throttleDuration
:
{
type
:
Number
,
type
:
Number
,
default
:
200
,
default
:
200
,
...
@@ -86,14 +85,47 @@ export default {
...
@@ -86,14 +85,47 @@ export default {
maxPosition
:
0
,
maxPosition
:
0
,
filterTerm
:
''
,
filterTerm
:
''
,
currentPage
:
''
,
currentPage
:
''
,
board
:
{},
};
};
},
},
apollo
:
{
board
:
{
query
()
{
return
this
.
currentBoardQuery
;
},
variables
()
{
return
{
fullPath
:
this
.
fullPath
,
boardId
:
this
.
fullBoardId
,
};
},
update
(
data
)
{
const
board
=
data
.
workspace
?.
board
;
return
{
...
board
,
labels
:
board
?.
labels
?.
nodes
,
};
},
error
()
{
this
.
setError
({
message
:
this
.
$options
.
i18n
.
errorFetchingBoard
});
},
},
},
computed
:
{
computed
:
{
...
mapState
([
'
boardType
'
]),
...
mapState
([
'
boardType
'
,
'
fullBoardId
'
]),
...
mapGetters
([
'
isGroupBoard
'
]),
...
mapGetters
([
'
isGroupBoard
'
,
'
isProjectBoard
'
]),
parentType
()
{
parentType
()
{
return
this
.
boardType
;
return
this
.
boardType
;
},
},
currentBoardQueryCE
()
{
return
this
.
isGroupBoard
?
groupBoardQuery
:
projectBoardQuery
;
},
currentBoardQuery
()
{
return
this
.
currentBoardQueryCE
;
},
isBoardLoading
()
{
return
this
.
$apollo
.
queries
.
board
.
loading
;
},
loading
()
{
loading
()
{
return
this
.
loadingRecentBoards
||
Boolean
(
this
.
loadingBoards
);
return
this
.
loadingRecentBoards
||
Boolean
(
this
.
loadingBoards
);
},
},
...
@@ -102,9 +134,6 @@ export default {
...
@@ -102,9 +134,6 @@ export default {
board
.
name
.
toLowerCase
().
includes
(
this
.
filterTerm
.
toLowerCase
()),
board
.
name
.
toLowerCase
().
includes
(
this
.
filterTerm
.
toLowerCase
()),
);
);
},
},
board
()
{
return
this
.
currentBoard
;
},
showCreate
()
{
showCreate
()
{
return
this
.
multipleIssueBoardsAvailable
;
return
this
.
multipleIssueBoardsAvailable
;
},
},
...
@@ -137,6 +166,7 @@ export default {
...
@@ -137,6 +166,7 @@ export default {
eventHub
.
$off
(
'
showBoardModal
'
,
this
.
showPage
);
eventHub
.
$off
(
'
showBoardModal
'
,
this
.
showPage
);
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
setError
'
]),
showPage
(
page
)
{
showPage
(
page
)
{
this
.
currentPage
=
page
;
this
.
currentPage
=
page
;
},
},
...
@@ -153,7 +183,7 @@ export default {
...
@@ -153,7 +183,7 @@ export default {
}));
}));
},
},
boardQuery
()
{
boardQuery
()
{
return
this
.
isGroupBoard
?
group
Query
:
project
Query
;
return
this
.
isGroupBoard
?
group
BoardsQuery
:
projectBoards
Query
;
},
},
loadBoards
(
toggleDropdown
=
true
)
{
loadBoards
(
toggleDropdown
=
true
)
{
if
(
toggleDropdown
&&
this
.
boards
.
length
>
0
)
{
if
(
toggleDropdown
&&
this
.
boards
.
length
>
0
)
{
...
@@ -229,13 +259,18 @@ export default {
...
@@ -229,13 +259,18 @@ export default {
this
.
hasScrollFade
=
this
.
isScrolledUp
();
this
.
hasScrollFade
=
this
.
isScrolledUp
();
},
},
},
},
i18n
:
{
errorFetchingBoard
:
s__
(
'
Board|An error occurred while fetching the board, please try again.
'
),
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"boards-switcher js-boards-selector gl-mr-3"
>
<div
class=
"boards-switcher js-boards-selector gl-mr-3"
>
<span
class=
"boards-selector-wrapper js-boards-selector-wrapper"
>
<span
class=
"boards-selector-wrapper js-boards-selector-wrapper"
>
<gl-loading-icon
v-if=
"isBoardLoading"
size=
"md"
class=
"gl-mt-2"
/>
<gl-dropdown
<gl-dropdown
v-else
data-qa-selector=
"boards_dropdown"
data-qa-selector=
"boards_dropdown"
toggle-class=
"dropdown-menu-toggle js-dropdown-toggle"
toggle-class=
"dropdown-menu-toggle js-dropdown-toggle"
menu-class=
"flex-column dropdown-extended-height"
menu-class=
"flex-column dropdown-extended-height"
...
@@ -336,7 +371,7 @@ export default {
...
@@ -336,7 +371,7 @@ export default {
:can-admin-board=
"canAdminBoard"
:can-admin-board=
"canAdminBoard"
:scoped-issue-board-feature-enabled=
"scopedIssueBoardFeatureEnabled"
:scoped-issue-board-feature-enabled=
"scopedIssueBoardFeatureEnabled"
:weights=
"weights"
:weights=
"weights"
:current-board=
"
currentB
oard"
:current-board=
"
b
oard"
:current-page=
"currentPage"
:current-page=
"currentPage"
@
cancel=
"cancel"
@
cancel=
"cancel"
/>
/>
...
...
app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
0 → 100644
View file @
703f89df
fragment
BoardScopeFragment
on
Board
{
id
name
hideBacklogList
hideClosedList
}
app/assets/javascripts/boards/graphql/group_board.query.graphql
0 → 100644
View file @
703f89df
#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
query
GroupBoard
(
$fullPath
:
ID
!,
$boardId
:
ID
!)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
board
(
id
:
$boardId
)
{
...
BoardScopeFragment
}
}
}
app/assets/javascripts/boards/graphql/project_board.query.graphql
0 → 100644
View file @
703f89df
#import "ee_else_ce/boards/graphql/board_scope.fragment.graphql"
query
ProjectBoard
(
$fullPath
:
ID
!,
$boardId
:
ID
!)
{
workspace
:
project
(
fullPath
:
$fullPath
)
{
board
(
id
:
$boardId
)
{
...
BoardScopeFragment
}
}
}
app/assets/javascripts/boards/mount_multiple_boards_switcher.js
View file @
703f89df
...
@@ -32,7 +32,6 @@ export default (params = {}) => {
...
@@ -32,7 +32,6 @@ export default (params = {}) => {
data
()
{
data
()
{
const
boardsSelectorProps
=
{
const
boardsSelectorProps
=
{
...
dataset
,
...
dataset
,
currentBoard
:
JSON
.
parse
(
dataset
.
currentBoard
),
hasMissingBoards
:
parseBoolean
(
dataset
.
hasMissingBoards
),
hasMissingBoards
:
parseBoolean
(
dataset
.
hasMissingBoards
),
canAdminBoard
:
parseBoolean
(
dataset
.
canAdminBoard
),
canAdminBoard
:
parseBoolean
(
dataset
.
canAdminBoard
),
multipleIssueBoardsAvailable
:
parseBoolean
(
dataset
.
multipleIssueBoardsAvailable
),
multipleIssueBoardsAvailable
:
parseBoolean
(
dataset
.
multipleIssueBoardsAvailable
),
...
...
app/assets/javascripts/graphql_shared/fragments/iteration.fragment.graphql
0 → 100644
View file @
703f89df
fragment
Iteration
on
Iteration
{
id
title
}
app/assets/javascripts/graphql_shared/utils.js
View file @
703f89df
...
@@ -15,6 +15,8 @@ export const isGid = (id) => {
...
@@ -15,6 +15,8 @@ export const isGid = (id) => {
return
false
;
return
false
;
};
};
const
parseGid
=
(
gid
)
=>
parseInt
(
`
${
gid
}
`
.
replace
(
/gid:
\/\/
gitlab
\/
.*
\/
/g
,
''
),
10
);
/**
/**
* Ids generated by GraphQL endpoints are usually in the format
* Ids generated by GraphQL endpoints are usually in the format
* gid://gitlab/Environments/123. This method extracts Id number
* gid://gitlab/Environments/123. This method extracts Id number
...
@@ -23,8 +25,12 @@ export const isGid = (id) => {
...
@@ -23,8 +25,12 @@ export const isGid = (id) => {
* @param {String} gid GraphQL global ID
* @param {String} gid GraphQL global ID
* @returns {Number}
* @returns {Number}
*/
*/
export
const
getIdFromGraphQLId
=
(
gid
=
''
)
=>
export
const
getIdFromGraphQLId
=
(
gid
=
''
)
=>
parseGid
(
gid
)
||
null
;
parseInt
(
`
${
gid
}
`
.
replace
(
/gid:
\/\/
gitlab
\/
.*
\/
/g
,
''
),
10
)
||
null
;
export
const
getZeroBasedIdFromGraphQLId
=
(
gid
=
''
)
=>
{
const
parsedGid
=
parseGid
(
gid
);
return
Number
.
isInteger
(
parsedGid
)
?
parsedGid
:
null
;
};
export
const
MutationOperationMode
=
{
export
const
MutationOperationMode
=
{
Append
:
'
APPEND
'
,
Append
:
'
APPEND
'
,
...
...
app/views/shared/boards/_switcher.html.haml
View file @
703f89df
...
@@ -3,8 +3,7 @@
...
@@ -3,8 +3,7 @@
-
milestone_filter_opts
=
milestone_filter_opts
.
merge
(
only_group_milestones:
true
)
if
board
.
group_board?
-
milestone_filter_opts
=
milestone_filter_opts
.
merge
(
only_group_milestones:
true
)
if
board
.
group_board?
-
weights
=
Gitlab
.
ee?
?
([
Issue
::
WEIGHT_ANY
]
+
Issue
.
weight_options
)
:
[]
-
weights
=
Gitlab
.
ee?
?
([
Issue
::
WEIGHT_ANY
]
+
Issue
.
weight_options
)
:
[]
#js-multiple-boards-switcher
.inline.boards-switcher
{
data:
{
current_board:
current_board_json
.
to_json
,
#js-multiple-boards-switcher
.inline.boards-switcher
{
data:
{
milestone_path:
milestones_filter_path
(
milestone_filter_opts
),
milestone_path:
milestones_filter_path
(
milestone_filter_opts
),
board_base_url:
board_base_url
,
board_base_url:
board_base_url
,
has_missing_boards:
(
!
multiple_boards_available?
&&
current_board_parent
.
boards
.
size
>
1
).
to_s
,
has_missing_boards:
(
!
multiple_boards_available?
&&
current_board_parent
.
boards
.
size
>
1
).
to_s
,
can_admin_board:
can?
(
current_user
,
:admin_issue_board
,
parent
).
to_s
,
can_admin_board:
can?
(
current_user
,
:admin_issue_board
,
parent
).
to_s
,
...
...
ee/app/assets/javascripts/boards/components/board_form.vue
View file @
703f89df
...
@@ -3,9 +3,7 @@
...
@@ -3,9 +3,7 @@
// extends a valid Vue single file component.
// extends a valid Vue single file component.
/* eslint-disable @gitlab/no-runtime-template-compiler */
/* eslint-disable @gitlab/no-runtime-template-compiler */
import
{
mapGetters
}
from
'
vuex
'
;
import
{
mapGetters
}
from
'
vuex
'
;
import
{
fullBoardId
}
from
'
~/boards/boards_util
'
;
import
BoardFormFoss
from
'
~/boards/components/board_form.vue
'
;
import
BoardFormFoss
from
'
~/boards/components/board_form.vue
'
;
import
{
fullEpicBoardId
}
from
'
../boards_util
'
;
import
createEpicBoardMutation
from
'
../graphql/epic_board_create.mutation.graphql
'
;
import
createEpicBoardMutation
from
'
../graphql/epic_board_create.mutation.graphql
'
;
import
destroyEpicBoardMutation
from
'
../graphql/epic_board_destroy.mutation.graphql
'
;
import
destroyEpicBoardMutation
from
'
../graphql/epic_board_destroy.mutation.graphql
'
;
import
updateEpicBoardMutation
from
'
../graphql/epic_board_update.mutation.graphql
'
;
import
updateEpicBoardMutation
from
'
../graphql/epic_board_update.mutation.graphql
'
;
...
@@ -18,11 +16,8 @@ export default {
...
@@ -18,11 +16,8 @@ export default {
return
this
.
board
.
id
?
updateEpicBoardMutation
:
createEpicBoardMutation
;
return
this
.
board
.
id
?
updateEpicBoardMutation
:
createEpicBoardMutation
;
},
},
mutationVariables
()
{
mutationVariables
()
{
const
{
board
}
=
this
;
return
{
return
{
...
this
.
baseMutationVariables
,
...
this
.
baseMutationVariables
,
...(
board
.
id
&&
this
.
isEpicBoard
&&
{
id
:
fullEpicBoardId
(
board
.
id
)
}),
...(
this
.
scopedIssueBoardFeatureEnabled
||
this
.
isEpicBoard
...(
this
.
scopedIssueBoardFeatureEnabled
||
this
.
isEpicBoard
?
this
.
boardScopeMutationVariables
?
this
.
boardScopeMutationVariables
:
{}),
:
{}),
...
@@ -56,7 +51,7 @@ export default {
...
@@ -56,7 +51,7 @@ export default {
await
this
.
$apollo
.
mutate
({
await
this
.
$apollo
.
mutate
({
mutation
:
this
.
isEpicBoard
?
destroyEpicBoardMutation
:
this
.
deleteMutation
,
mutation
:
this
.
isEpicBoard
?
destroyEpicBoardMutation
:
this
.
deleteMutation
,
variables
:
{
variables
:
{
id
:
this
.
isEpicBoard
?
fullEpicBoardId
(
this
.
board
.
id
)
:
fullBoardId
(
this
.
board
.
id
)
,
id
:
this
.
board
.
id
,
},
},
});
});
},
},
...
...
ee/app/assets/javascripts/boards/components/board_scope.vue
View file @
703f89df
<
script
>
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
{
mapGetters
}
from
'
vuex
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
AssigneeSelect
from
'
./assignee_select.vue
'
;
import
AssigneeSelect
from
'
./assignee_select.vue
'
;
import
BoardScopeCurrentIteration
from
'
./board_scope_current_iteration.vue
'
;
import
BoardScopeCurrentIteration
from
'
./board_scope_current_iteration.vue
'
;
import
BoardLabelsSelect
from
'
./labels_select.vue
'
;
import
BoardLabelsSelect
from
'
./labels_select.vue
'
;
...
@@ -51,6 +52,9 @@ export default {
...
@@ -51,6 +52,9 @@ export default {
?
__
(
'
Board scope affects which issues are displayed for anyone who visits this board
'
)
?
__
(
'
Board scope affects which issues are displayed for anyone who visits this board
'
)
:
__
(
'
Board scope affects which epics are displayed for anyone who visits this board
'
);
:
__
(
'
Board scope affects which epics are displayed for anyone who visits this board
'
);
},
},
iterationId
()
{
return
getIdFromGraphQLId
(
this
.
board
.
iteration
?.
id
)
||
null
;
},
},
},
methods
:
{
methods
:
{
...
@@ -87,7 +91,7 @@ export default {
...
@@ -87,7 +91,7 @@ export default {
<board-scope-current-iteration
<board-scope-current-iteration
v-if=
"isIssueBoard"
v-if=
"isIssueBoard"
:can-admin-board=
"canAdminBoard"
:can-admin-board=
"canAdminBoard"
:iteration-id=
"
board.iteration_i
d"
:iteration-id=
"
iterationI
d"
@
set-iteration=
"$emit('set-iteration', $event)"
@
set-iteration=
"$emit('set-iteration', $event)"
/>
/>
...
...
ee/app/assets/javascripts/boards/components/boards_selector.vue
View file @
703f89df
...
@@ -7,6 +7,7 @@ import BoardsSelectorFoss from '~/boards/components/boards_selector.vue';
...
@@ -7,6 +7,7 @@ import BoardsSelectorFoss from '~/boards/components/boards_selector.vue';
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
Tracking
from
'
~/tracking
'
;
import
Tracking
from
'
~/tracking
'
;
import
epicBoardsQuery
from
'
../graphql/epic_boards.query.graphql
'
;
import
epicBoardsQuery
from
'
../graphql/epic_boards.query.graphql
'
;
import
epicBoardQuery
from
'
../graphql/epic_board.query.graphql
'
;
export
default
{
export
default
{
extends
:
BoardsSelectorFoss
,
extends
:
BoardsSelectorFoss
,
...
@@ -19,6 +20,9 @@ export default {
...
@@ -19,6 +20,9 @@ export default {
showDelete
()
{
showDelete
()
{
return
this
.
boards
.
length
>
1
;
return
this
.
boards
.
length
>
1
;
},
},
currentBoardQuery
()
{
return
this
.
isEpicBoard
?
epicBoardQuery
:
this
.
currentBoardQueryCE
;
},
},
},
methods
:
{
methods
:
{
epicBoardUpdate
(
data
)
{
epicBoardUpdate
(
data
)
{
...
...
ee/app/assets/javascripts/boards/components/labels_select.vue
View file @
703f89df
...
@@ -2,7 +2,6 @@
...
@@ -2,7 +2,6 @@
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
debounce
}
from
'
lodash
'
;
import
{
debounce
}
from
'
lodash
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
LabelItem
from
'
~/vue_shared/components/sidebar/labels_select_widget/label_item.vue
'
;
import
LabelItem
from
'
~/vue_shared/components/sidebar/labels_select_widget/label_item.vue
'
;
import
searchGroupLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
'
;
import
searchGroupLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
'
;
import
searchProjectLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
'
;
import
searchProjectLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
'
;
...
@@ -60,10 +59,7 @@ export default {
...
@@ -60,10 +59,7 @@ export default {
return
!
this
.
isEditing
;
return
!
this
.
isEditing
;
},
},
update
(
data
)
{
update
(
data
)
{
return
data
.
workspace
?.
labels
?.
nodes
.
map
((
label
)
=>
({
return
data
.
workspace
?.
labels
?.
nodes
;
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
),
}));
},
},
error
()
{
error
()
{
this
.
setError
({
message
:
this
.
$options
.
i18n
.
errorSearchingLabels
});
this
.
setError
({
message
:
this
.
$options
.
i18n
.
errorSearchingLabels
});
...
@@ -123,7 +119,7 @@ export default {
...
@@ -123,7 +119,7 @@ export default {
this
.
$emit
(
'
set-labels
'
,
labels
);
this
.
$emit
(
'
set-labels
'
,
labels
);
},
},
onLabelRemove
(
labelId
)
{
onLabelRemove
(
labelId
)
{
const
labels
=
this
.
selected
.
filter
(({
id
})
=>
getIdFromGraphQLId
(
id
)
!==
labelId
);
const
labels
=
this
.
selected
.
filter
(({
id
})
=>
id
!==
labelId
);
this
.
selected
=
labels
;
this
.
selected
=
labels
;
this
.
$emit
(
'
set-labels
'
,
labels
);
this
.
$emit
(
'
set-labels
'
,
labels
);
},
},
...
...
ee/app/assets/javascripts/boards/graphql/board_scope.fragment.graphql
0 → 100644
View file @
703f89df
#import "~/graphql_shared/fragments/user.fragment.graphql"
#import "~/graphql_shared/fragments/label.fragment.graphql"
#import "~/graphql_shared/fragments/iteration.fragment.graphql"
fragment
BoardScopeFragment
on
Board
{
id
name
hideBacklogList
hideClosedList
assignee
{
...
User
}
milestone
{
id
title
}
labels
{
nodes
{
...
Label
}
}
iteration
{
...
Iteration
}
weight
}
ee/app/assets/javascripts/boards/graphql/epic_board.query.graphql
0 → 100644
View file @
703f89df
#import "~/graphql_shared/fragments/label.fragment.graphql"
query
EpicBoard
(
$fullPath
:
ID
!,
$boardId
:
BoardsEpicBoardID
!)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
board
:
epicBoard
(
id
:
$boardId
)
{
id
name
hideBacklogList
hideClosedList
labels
{
nodes
{
...
Label
}
}
}
}
}
ee/spec/features/boards/scoped_issue_board_spec.rb
View file @
703f89df
...
@@ -225,6 +225,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -225,6 +225,7 @@ RSpec.describe 'Scoped issue boards', :js do
it
'prefills fields'
do
it
'prefills fields'
do
visit
project_boards_path
(
project_2
)
visit
project_boards_path
(
project_2
)
wait_for_all_requests
edit_board
.
click
edit_board
.
click
...
@@ -332,6 +333,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -332,6 +333,7 @@ RSpec.describe 'Scoped issue boards', :js do
it
'adds label to board'
do
it
'adds label to board'
do
label_title
=
issue
.
labels
.
first
.
title
label_title
=
issue
.
labels
.
first
.
title
visit
project_boards_path
(
project
)
visit
project_boards_path
(
project
)
wait_for_all_requests
update_board_label
(
label_title
)
update_board_label
(
label_title
)
...
@@ -346,6 +348,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -346,6 +348,7 @@ RSpec.describe 'Scoped issue boards', :js do
label_2_title
=
issue_2
.
labels
.
first
.
title
label_2_title
=
issue_2
.
labels
.
first
.
title
visit
project_boards_path
(
project
)
visit
project_boards_path
(
project
)
wait_for_all_requests
update_board_label
(
label_title
)
update_board_label
(
label_title
)
...
@@ -365,6 +368,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -365,6 +368,7 @@ RSpec.describe 'Scoped issue boards', :js do
label_2_title
=
issue_2
.
labels
.
first
.
title
label_2_title
=
issue_2
.
labels
.
first
.
title
visit
project_boards_path
(
project
)
visit
project_boards_path
(
project
)
wait_for_all_requests
update_board_label
(
label_title
)
update_board_label
(
label_title
)
...
@@ -378,6 +382,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -378,6 +382,7 @@ RSpec.describe 'Scoped issue boards', :js do
stub_licensed_features
(
multiple_group_issue_boards:
true
)
stub_licensed_features
(
multiple_group_issue_boards:
true
)
visit
group_boards_path
(
group
)
visit
group_boards_path
(
group
)
wait_for_all_requests
edit_board
.
click
edit_board
.
click
page
.
within
(
'.labels'
)
do
page
.
within
(
'.labels'
)
do
...
...
ee/spec/frontend/boards/components/board_form_spec.js
View file @
703f89df
...
@@ -23,15 +23,19 @@ jest.mock('~/lib/utils/url_utility', () => ({
...
@@ -23,15 +23,19 @@ jest.mock('~/lib/utils/url_utility', () => ({
Vue
.
use
(
Vuex
);
Vue
.
use
(
Vuex
);
const
currentBoard
=
{
const
currentBoard
=
{
id
:
1
,
id
:
'
gid://gitlab/Board/1
'
,
name
:
'
test
'
,
name
:
'
test
'
,
labels
:
[],
labels
:
[],
milestone
_id
:
undefined
,
milestone
:
{}
,
assignee
:
{},
assignee
:
{},
assignee_id
:
undefined
,
weight
:
null
,
weight
:
null
,
hide_backlog_list
:
false
,
hideBacklogList
:
false
,
hide_closed_list
:
false
,
hideClosedList
:
false
,
};
const
currentEpicBoard
=
{
...
currentBoard
,
id
:
'
gid://gitlab/Boards::EpicBoard/321
'
,
};
};
const
defaultProps
=
{
const
defaultProps
=
{
...
@@ -206,7 +210,9 @@ describe('BoardForm', () => {
...
@@ -206,7 +210,9 @@ describe('BoardForm', () => {
milestone
:
{
milestone
:
{
id
:
2
,
id
:
2
,
},
},
iteration_id
:
3
,
iteration
:
{
id
:
'
gid://gitlab/Iteration/3
'
,
},
},
},
canAdminBoard
:
true
,
canAdminBoard
:
true
,
currentPage
:
formType
.
edit
,
currentPage
:
formType
.
edit
,
...
@@ -221,7 +227,7 @@ describe('BoardForm', () => {
...
@@ -221,7 +227,7 @@ describe('BoardForm', () => {
mutation
:
updateBoardMutation
,
mutation
:
updateBoardMutation
,
variables
:
{
variables
:
{
input
:
expect
.
objectContaining
({
input
:
expect
.
objectContaining
({
id
:
`gid://gitlab/Board/
${
currentBoard
.
id
}
`
,
id
:
currentBoard
.
id
,
assigneeId
:
'
gid://gitlab/User/1
'
,
assigneeId
:
'
gid://gitlab/User/1
'
,
milestoneId
:
'
gid://gitlab/Milestone/2
'
,
milestoneId
:
'
gid://gitlab/Milestone/2
'
,
iterationId
:
'
gid://gitlab/Iteration/3
'
,
iterationId
:
'
gid://gitlab/Iteration/3
'
,
...
@@ -240,11 +246,15 @@ describe('BoardForm', () => {
...
@@ -240,11 +246,15 @@ describe('BoardForm', () => {
mutate
=
jest
.
fn
().
mockResolvedValue
({
mutate
=
jest
.
fn
().
mockResolvedValue
({
data
:
{
data
:
{
epicBoardUpdate
:
{
epicBoardUpdate
:
{
epicBoard
:
{
id
:
'
gid://gitlab/Boards::EpicBoard/321
'
,
webPath
:
'
test-path
'
},
epicBoard
:
{
id
:
currentEpicBoard
.
id
,
webPath
:
'
test-path
'
},
},
},
},
},
});
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
edit
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
edit
,
currentBoard
:
currentEpicBoard
,
});
findInput
().
trigger
(
'
keyup.enter
'
,
{
metaKey
:
true
});
findInput
().
trigger
(
'
keyup.enter
'
,
{
metaKey
:
true
});
...
@@ -254,7 +264,7 @@ describe('BoardForm', () => {
...
@@ -254,7 +264,7 @@ describe('BoardForm', () => {
mutation
:
updateEpicBoardMutation
,
mutation
:
updateEpicBoardMutation
,
variables
:
{
variables
:
{
input
:
expect
.
objectContaining
({
input
:
expect
.
objectContaining
({
id
:
`gid://gitlab/Boards::EpicBoard/
${
currentBoard
.
id
}
`
,
id
:
currentEpicBoard
.
id
,
}),
}),
},
},
});
});
...
@@ -265,7 +275,11 @@ describe('BoardForm', () => {
...
@@ -265,7 +275,11 @@ describe('BoardForm', () => {
it
(
'
shows a GlAlert if GraphQL mutation fails
'
,
async
()
=>
{
it
(
'
shows a GlAlert if GraphQL mutation fails
'
,
async
()
=>
{
mutate
=
jest
.
fn
().
mockRejectedValue
(
'
Houston, we have a problem
'
);
mutate
=
jest
.
fn
().
mockRejectedValue
(
'
Houston, we have a problem
'
);
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
edit
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
edit
,
currentBoard
:
currentEpicBoard
,
});
jest
.
spyOn
(
wrapper
.
vm
,
'
setError
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
wrapper
.
vm
,
'
setError
'
).
mockImplementation
(()
=>
{});
findInput
().
trigger
(
'
keyup.enter
'
,
{
metaKey
:
true
});
findInput
().
trigger
(
'
keyup.enter
'
,
{
metaKey
:
true
});
...
@@ -285,19 +299,31 @@ describe('BoardForm', () => {
...
@@ -285,19 +299,31 @@ describe('BoardForm', () => {
});
});
it
(
'
passes correct primary action text and variant
'
,
()
=>
{
it
(
'
passes correct primary action text and variant
'
,
()
=>
{
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
,
currentBoard
:
currentEpicBoard
,
});
expect
(
findModalActionPrimary
().
text
).
toBe
(
'
Delete
'
);
expect
(
findModalActionPrimary
().
text
).
toBe
(
'
Delete
'
);
expect
(
findModalActionPrimary
().
attributes
[
0
].
variant
).
toBe
(
'
danger
'
);
expect
(
findModalActionPrimary
().
attributes
[
0
].
variant
).
toBe
(
'
danger
'
);
});
});
it
(
'
renders delete confirmation message
'
,
()
=>
{
it
(
'
renders delete confirmation message
'
,
()
=>
{
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
,
currentBoard
:
currentEpicBoard
,
});
expect
(
findDeleteConfirmation
().
exists
()).
toBe
(
true
);
expect
(
findDeleteConfirmation
().
exists
()).
toBe
(
true
);
});
});
it
(
'
calls a correct GraphQL mutation and redirects to correct page after deleting board
'
,
async
()
=>
{
it
(
'
calls a correct GraphQL mutation and redirects to correct page after deleting board
'
,
async
()
=>
{
mutate
=
jest
.
fn
().
mockResolvedValue
({});
mutate
=
jest
.
fn
().
mockResolvedValue
({});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
,
currentBoard
:
currentEpicBoard
,
});
findModal
().
vm
.
$emit
(
'
primary
'
);
findModal
().
vm
.
$emit
(
'
primary
'
);
await
waitForPromises
();
await
waitForPromises
();
...
@@ -305,7 +331,7 @@ describe('BoardForm', () => {
...
@@ -305,7 +331,7 @@ describe('BoardForm', () => {
expect
(
mutate
).
toHaveBeenCalledWith
({
expect
(
mutate
).
toHaveBeenCalledWith
({
mutation
:
destroyEpicBoardMutation
,
mutation
:
destroyEpicBoardMutation
,
variables
:
{
variables
:
{
id
:
'
gid://gitlab/Boards::EpicBoard/1
'
,
id
:
currentEpicBoard
.
id
,
},
},
});
});
...
@@ -315,7 +341,11 @@ describe('BoardForm', () => {
...
@@ -315,7 +341,11 @@ describe('BoardForm', () => {
it
(
'
shows a GlAlert if GraphQL mutation fails
'
,
async
()
=>
{
it
(
'
shows a GlAlert if GraphQL mutation fails
'
,
async
()
=>
{
mutate
=
jest
.
fn
().
mockRejectedValue
(
'
Houston, we have a problem
'
);
mutate
=
jest
.
fn
().
mockRejectedValue
(
'
Houston, we have a problem
'
);
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
});
createComponent
({
canAdminBoard
:
true
,
currentPage
:
formType
.
delete
,
currentBoard
:
currentEpicBoard
,
});
jest
.
spyOn
(
wrapper
.
vm
,
'
setError
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
wrapper
.
vm
,
'
setError
'
).
mockImplementation
(()
=>
{});
findModal
().
vm
.
$emit
(
'
primary
'
);
findModal
().
vm
.
$emit
(
'
primary
'
);
...
...
ee/spec/frontend/boards/components/boards_selector_spec.js
View file @
703f89df
import
{
GlDropdown
,
GlLoadingIcon
,
GlDropdownSectionHeader
}
from
'
@gitlab/ui
'
;
import
{
GlDropdown
,
GlLoadingIcon
,
GlDropdownSectionHeader
}
from
'
@gitlab/ui
'
;
import
{
createLocalVue
,
mount
}
from
'
@vue/test-utils
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
{
nextTick
}
from
'
vue
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
BoardsSelector
from
'
ee/boards/components/boards_selector.vue
'
;
import
BoardsSelector
from
'
ee/boards/components/boards_selector.vue
'
;
import
{
BoardType
}
from
'
~/boards/constants
'
;
import
epicBoardQuery
from
'
ee/boards/graphql/epic_board.query.graphql
'
;
import
groupBoardQuery
from
'
~/boards/graphql/group_board.query.graphql
'
;
import
projectBoardQuery
from
'
~/boards/graphql/project_board.query.graphql
'
;
import
defaultStore
from
'
~/boards/stores
'
;
import
{
TEST_HOST
}
from
'
spec/test_constants
'
;
import
{
TEST_HOST
}
from
'
spec/test_constants
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
{
mockGroupBoardResponse
,
mockProjectBoardResponse
}
from
'
jest/boards/mock_data
'
;
import
{
mockEpicBoardResponse
}
from
'
../mock_data
'
;
const
throttleDuration
=
1
;
const
throttleDuration
=
1
;
const
localVue
=
createLocalVue
();
Vue
.
use
(
VueApollo
);
localVue
.
use
(
Vuex
);
function
boardGenerator
(
n
)
{
function
boardGenerator
(
n
)
{
return
new
Array
(
n
).
fill
().
map
((
board
,
index
)
=>
{
return
new
Array
(
n
).
fill
().
map
((
board
,
index
)
=>
{
...
@@ -29,13 +37,28 @@ describe('BoardsSelector', () => {
...
@@ -29,13 +37,28 @@ describe('BoardsSelector', () => {
let
allBoardsResponse
;
let
allBoardsResponse
;
let
recentBoardsResponse
;
let
recentBoardsResponse
;
let
mock
;
let
mock
;
let
fakeApollo
;
let
store
;
const
boards
=
boardGenerator
(
20
);
const
boards
=
boardGenerator
(
20
);
const
recentBoards
=
boardGenerator
(
5
);
const
recentBoards
=
boardGenerator
(
5
);
const
createStore
=
()
=>
{
const
createStore
=
({
return
new
Vuex
.
Store
({
isGroupBoard
=
false
,
isProjectBoard
=
false
,
isEpicBoard
=
false
,
}
=
{})
=>
{
store
=
new
Vuex
.
Store
({
...
defaultStore
,
actions
:
{
setError
:
jest
.
fn
(),
},
getters
:
{
getters
:
{
isEpicBoard
:
()
=>
false
,
isEpicBoard
:
()
=>
isEpicBoard
,
isGroupBoard
:
()
=>
isGroupBoard
,
isProjectBoard
:
()
=>
isProjectBoard
,
},
state
:
{
boardType
:
isGroupBoard
?
BoardType
.
group
:
BoardType
.
project
,
},
},
});
});
};
};
...
@@ -45,43 +68,22 @@ describe('BoardsSelector', () => {
...
@@ -45,43 +68,22 @@ describe('BoardsSelector', () => {
const
getLoadingIcon
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
getLoadingIcon
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
findDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
const
findDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
beforeEach
(()
=>
{
const
projectBoardQueryHandlerSuccess
=
jest
.
fn
().
mockResolvedValue
(
mockProjectBoardResponse
);
mock
=
new
MockAdapter
(
axios
);
const
groupBoardQueryHandlerSuccess
=
jest
.
fn
().
mockResolvedValue
(
mockGroupBoardResponse
);
const
$apollo
=
{
const
epicBoardQueryHandlerSuccess
=
jest
.
fn
().
mockResolvedValue
(
mockEpicBoardResponse
);
queries
:
{
boards
:
{
loading
:
false
,
},
},
};
allBoardsResponse
=
Promise
.
resolve
({
data
:
{
group
:
{
boards
:
{
edges
:
boards
.
map
((
board
)
=>
({
node
:
board
})),
},
},
},
});
recentBoardsResponse
=
Promise
.
resolve
({
data
:
recentBoards
,
});
const
store
=
createStore
();
const
createComponent
=
()
=>
{
fakeApollo
=
createMockApollo
([
[
projectBoardQuery
,
projectBoardQueryHandlerSuccess
],
[
groupBoardQuery
,
groupBoardQueryHandlerSuccess
],
[
epicBoardQuery
,
epicBoardQueryHandlerSuccess
],
]);
wrapper
=
mount
(
BoardsSelector
,
{
wrapper
=
mount
(
BoardsSelector
,
{
localVue
,
store
,
apolloProvider
:
fakeApollo
,
propsData
:
{
propsData
:
{
throttleDuration
,
throttleDuration
,
currentBoard
:
{
id
:
1
,
name
:
'
Development
'
,
milestone_id
:
null
,
weight
:
null
,
assignee_id
:
null
,
labels
:
[],
},
boardBaseUrl
:
`
${
TEST_HOST
}
/board/base/url`
,
boardBaseUrl
:
`
${
TEST_HOST
}
/board/base/url`
,
hasMissingBoards
:
false
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
canAdminBoard
:
true
,
...
@@ -89,13 +91,11 @@ describe('BoardsSelector', () => {
...
@@ -89,13 +91,11 @@ describe('BoardsSelector', () => {
scopedIssueBoardFeatureEnabled
:
true
,
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
weights
:
[],
},
},
mocks
:
{
$apollo
},
attachTo
:
document
.
body
,
attachTo
:
document
.
body
,
provide
:
{
provide
:
{
fullPath
:
''
,
fullPath
:
''
,
recentBoardsEndpoint
:
`
${
TEST_HOST
}
/recent`
,
recentBoardsEndpoint
:
`
${
TEST_HOST
}
/recent`
,
},
},
store
,
});
});
wrapper
.
vm
.
$apollo
.
addSmartQuery
=
jest
.
fn
((
_
,
options
)
=>
{
wrapper
.
vm
.
$apollo
.
addSmartQuery
=
jest
.
fn
((
_
,
options
)
=>
{
...
@@ -103,48 +103,103 @@ describe('BoardsSelector', () => {
...
@@ -103,48 +103,103 @@ describe('BoardsSelector', () => {
[
options
.
loadingKey
]:
true
,
[
options
.
loadingKey
]:
true
,
});
});
});
});
};
mock
.
onGet
(
`
${
TEST_HOST
}
/recent`
).
replyOnce
(
200
,
recentBoards
);
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
findDropdown
().
vm
.
$emit
(
'
show
'
);
});
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
wrapper
=
null
;
wrapper
=
null
;
fakeApollo
=
null
;
store
=
null
;
mock
.
restore
();
mock
.
restore
();
});
});
describe
(
'
loading
'
,
()
=>
{
describe
(
'
fetching all board
'
,
()
=>
{
// we are testing loading state, so don't resolve responses until after the tests
beforeEach
(()
=>
{
afterEach
(
async
()
=>
{
mock
=
new
MockAdapter
(
axios
);
await
Promise
.
all
([
allBoardsResponse
,
recentBoardsResponse
]);
return
nextTick
();
});
it
(
'
shows loading spinner
'
,
()
=>
{
allBoardsResponse
=
Promise
.
resolve
({
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
data
:
{
expect
(
getDropdownItems
()).
toHaveLength
(
0
);
group
:
{
expect
(
getLoadingIcon
().
exists
()).
toBe
(
true
);
boards
:
{
edges
:
boards
.
map
((
board
)
=>
({
node
:
board
})),
},
},
},
});
recentBoardsResponse
=
Promise
.
resolve
({
data
:
recentBoards
,
});
createStore
();
createComponent
();
mock
.
onGet
(
`
${
TEST_HOST
}
/recent`
).
replyOnce
(
200
,
recentBoards
);
});
});
});
describe
(
'
loaded
'
,
()
=>
{
describe
(
'
loading
'
,
()
=>
{
beforeEach
(
async
()
=>
{
beforeEach
(
async
()
=>
{
await
wrapper
.
setData
({
// Wait for current board to be loaded
loadingBoards
:
false
,
await
nextTick
();
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
findDropdown
().
vm
.
$emit
(
'
show
'
);
});
// we are testing loading state, so don't resolve responses until after the tests
afterEach
(
async
()
=>
{
await
Promise
.
all
([
allBoardsResponse
,
recentBoardsResponse
]);
await
nextTick
();
});
it
(
'
shows loading spinner
'
,
()
=>
{
expect
(
getDropdownHeaders
()).
toHaveLength
(
0
);
expect
(
getDropdownItems
()).
toHaveLength
(
0
);
expect
(
getLoadingIcon
().
exists
()).
toBe
(
true
);
});
});
// NOTE: Due to timing issues, this `return` of `Promise.all` is required because
// `app/assets/javascripts/boards/components/boards_selector.vue` does a `$nextTick()` in
// loadRecentBoards. For some unknown reason it doesn't work with `await`, the `return`
// is required.
return
Promise
.
all
([
allBoardsResponse
,
recentBoardsResponse
]).
then
(()
=>
nextTick
());
});
});
it
(
'
hides loading spinner
'
,
async
()
=>
{
describe
(
'
loaded
'
,
()
=>
{
await
wrapper
.
vm
.
$nextTick
();
beforeEach
(
async
()
=>
{
expect
(
getLoadingIcon
().
exists
()).
toBe
(
false
);
// Wait for current board to be loaded
await
nextTick
();
// Emits gl-dropdown show event to simulate the dropdown is opened at initialization time
findDropdown
().
vm
.
$emit
(
'
show
'
);
await
wrapper
.
setData
({
loadingBoards
:
false
,
loadingRecentBoards
:
false
,
});
});
it
(
'
hides loading spinner
'
,
async
()
=>
{
await
nextTick
();
expect
(
getLoadingIcon
().
exists
()).
toBe
(
false
);
});
});
});
});
});
describe
(
'
fetching current board
'
,
()
=>
{
it
.
each
`
boardType | isEpicBoard | queryHandler | notCalledHandler
${
BoardType
.
group
}
|
${
false
}
|
${
groupBoardQueryHandlerSuccess
}
|
${
projectBoardQueryHandlerSuccess
}
${
BoardType
.
project
}
|
${
false
}
|
${
projectBoardQueryHandlerSuccess
}
|
${
groupBoardQueryHandlerSuccess
}
${
BoardType
.
group
}
|
${
true
}
|
${
epicBoardQueryHandlerSuccess
}
|
${
groupBoardQueryHandlerSuccess
}
`
(
'
fetches $boardType board when isEpicBoard is $isEpicBoard
'
,
async
({
boardType
,
isEpicBoard
,
queryHandler
,
notCalledHandler
})
=>
{
createStore
({
isProjectBoard
:
boardType
===
BoardType
.
project
,
isGroupBoard
:
boardType
===
BoardType
.
group
,
isEpicBoard
,
});
createComponent
();
await
nextTick
();
expect
(
queryHandler
).
toHaveBeenCalled
();
expect
(
notCalledHandler
).
not
.
toHaveBeenCalled
();
},
);
});
});
});
ee/spec/frontend/boards/mock_data.js
View file @
703f89df
...
@@ -7,6 +7,18 @@ import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label
...
@@ -7,6 +7,18 @@ import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
MilestoneToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
'
;
import
WeightToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
'
;
import
WeightToken
from
'
~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue
'
;
export
const
mockEpicBoardResponse
=
{
data
:
{
workspace
:
{
epicBoard
:
{
id
:
'
gid://gitlab/Boards::EpicBoard/1
'
,
name
:
'
Development
'
,
},
__typename
:
'
Group
'
,
},
},
};
export
const
mockLabel
=
{
export
const
mockLabel
=
{
id
:
'
gid://gitlab/GroupLabel/121
'
,
id
:
'
gid://gitlab/GroupLabel/121
'
,
title
:
'
To Do
'
,
title
:
'
To Do
'
,
...
...
locale/gitlab.pot
View file @
703f89df
...
@@ -5630,6 +5630,9 @@ msgstr ""
...
@@ -5630,6 +5630,9 @@ msgstr ""
msgid "Boards|View scope"
msgid "Boards|View scope"
msgstr ""
msgstr ""
msgid "Board|An error occurred while fetching the board, please try again."
msgstr ""
msgid "Board|Are you sure you want to delete this board?"
msgid "Board|Are you sure you want to delete this board?"
msgstr ""
msgstr ""
...
...
spec/frontend/boards/components/board_form_spec.js
View file @
703f89df
...
@@ -17,15 +17,14 @@ jest.mock('~/lib/utils/url_utility', () => ({
...
@@ -17,15 +17,14 @@ jest.mock('~/lib/utils/url_utility', () => ({
}));
}));
const
currentBoard
=
{
const
currentBoard
=
{
id
:
1
,
id
:
'
gid://gitlab/Board/1
'
,
name
:
'
test
'
,
name
:
'
test
'
,
labels
:
[],
labels
:
[],
milestone
_id
:
undefined
,
milestone
:
{}
,
assignee
:
{},
assignee
:
{},
assignee_id
:
undefined
,
weight
:
null
,
weight
:
null
,
hide
_backlog_l
ist
:
false
,
hide
BacklogL
ist
:
false
,
hide
_closed_l
ist
:
false
,
hide
ClosedL
ist
:
false
,
};
};
const
defaultProps
=
{
const
defaultProps
=
{
...
@@ -249,7 +248,7 @@ describe('BoardForm', () => {
...
@@ -249,7 +248,7 @@ describe('BoardForm', () => {
mutation
:
updateBoardMutation
,
mutation
:
updateBoardMutation
,
variables
:
{
variables
:
{
input
:
expect
.
objectContaining
({
input
:
expect
.
objectContaining
({
id
:
`gid://gitlab/Board/
${
currentBoard
.
id
}
`
,
id
:
currentBoard
.
id
,
}),
}),
},
},
});
});
...
@@ -275,7 +274,7 @@ describe('BoardForm', () => {
...
@@ -275,7 +274,7 @@ describe('BoardForm', () => {
mutation
:
updateBoardMutation
,
mutation
:
updateBoardMutation
,
variables
:
{
variables
:
{
input
:
expect
.
objectContaining
({
input
:
expect
.
objectContaining
({
id
:
`gid://gitlab/Board/
${
currentBoard
.
id
}
`
,
id
:
currentBoard
.
id
,
}),
}),
},
},
});
});
...
@@ -323,7 +322,7 @@ describe('BoardForm', () => {
...
@@ -323,7 +322,7 @@ describe('BoardForm', () => {
expect
(
mutate
).
toHaveBeenCalledWith
({
expect
(
mutate
).
toHaveBeenCalledWith
({
mutation
:
destroyBoardMutation
,
mutation
:
destroyBoardMutation
,
variables
:
{
variables
:
{
id
:
'
gid://gitlab/Board/1
'
,
id
:
currentBoard
.
id
,
},
},
});
});
...
...
spec/frontend/boards/components/boards_selector_spec.js
View file @
703f89df
This diff is collapsed.
Click to expand it.
spec/frontend/boards/mock_data.js
View file @
703f89df
...
@@ -30,17 +30,27 @@ export const listObj = {
...
@@ -30,17 +30,27 @@ export const listObj = {
},
},
};
};
export
const
listObjDuplicate
=
{
export
const
mockGroupBoardResponse
=
{
id
:
listObj
.
id
,
data
:
{
position
:
1
,
workspace
:
{
title
:
'
Test
'
,
board
:
{
list_type
:
'
label
'
,
id
:
'
gid://gitlab/Board/1
'
,
weight
:
3
,
name
:
'
Development
'
,
label
:
{
},
id
:
listObj
.
label
.
id
,
__typename
:
'
Group
'
,
title
:
'
Test
'
,
},
color
:
'
#ff0000
'
,
},
description
:
'
testing;
'
,
};
export
const
mockProjectBoardResponse
=
{
data
:
{
workspace
:
{
board
:
{
id
:
'
gid://gitlab/Board/2
'
,
name
:
'
Development
'
,
},
__typename
:
'
Project
'
,
},
},
},
};
};
...
@@ -634,8 +644,8 @@ export const mockProjectLabelsResponse = {
...
@@ -634,8 +644,8 @@ export const mockProjectLabelsResponse = {
labels
:
{
labels
:
{
nodes
:
[
mockLabel1
,
mockLabel2
],
nodes
:
[
mockLabel1
,
mockLabel2
],
},
},
__typename
:
'
Project
'
,
},
},
__typename
:
'
Project
'
,
},
},
};
};
...
@@ -646,7 +656,7 @@ export const mockGroupLabelsResponse = {
...
@@ -646,7 +656,7 @@ export const mockGroupLabelsResponse = {
labels
:
{
labels
:
{
nodes
:
[
mockLabel1
,
mockLabel2
],
nodes
:
[
mockLabel1
,
mockLabel2
],
},
},
__typename
:
'
Group
'
,
},
},
__typename
:
'
Group
'
,
},
},
};
};
spec/frontend/graphql_shared/utils_spec.js
View file @
703f89df
import
{
import
{
isGid
,
isGid
,
getIdFromGraphQLId
,
getIdFromGraphQLId
,
getZeroBasedIdFromGraphQLId
,
convertToGraphQLId
,
convertToGraphQLId
,
convertToGraphQLIds
,
convertToGraphQLIds
,
convertFromGraphQLIds
,
convertFromGraphQLIds
,
...
@@ -51,6 +52,10 @@ describe('getIdFromGraphQLId', () => {
...
@@ -51,6 +52,10 @@ describe('getIdFromGraphQLId', () => {
input
:
'
gid://gitlab/Environments/
'
,
input
:
'
gid://gitlab/Environments/
'
,
output
:
null
,
output
:
null
,
},
},
{
input
:
'
gid://gitlab/Environments/0
'
,
output
:
null
,
},
{
{
input
:
'
gid://gitlab/Environments/123
'
,
input
:
'
gid://gitlab/Environments/123
'
,
output
:
123
,
output
:
123
,
...
@@ -66,6 +71,55 @@ describe('getIdFromGraphQLId', () => {
...
@@ -66,6 +71,55 @@ describe('getIdFromGraphQLId', () => {
});
});
});
});
describe
(
'
getZeroBasedIdFromGraphQLId
'
,
()
=>
{
[
{
input
:
''
,
output
:
null
,
},
{
input
:
null
,
output
:
null
,
},
{
input
:
2
,
output
:
2
,
},
{
input
:
'
gid://
'
,
output
:
null
,
},
{
input
:
'
gid://gitlab/
'
,
output
:
null
,
},
{
input
:
'
gid://gitlab/Environments
'
,
output
:
null
,
},
{
input
:
'
gid://gitlab/Environments/
'
,
output
:
null
,
},
{
input
:
'
gid://gitlab/Environments/0
'
,
output
:
0
,
},
{
input
:
'
gid://gitlab/Environments/123
'
,
output
:
123
,
},
{
input
:
'
gid://gitlab/DesignManagement::Version/2
'
,
output
:
2
,
},
].
forEach
(({
input
,
output
})
=>
{
it
(
`getZeroBasedIdFromGraphQLId returns
${
output
}
when passed
${
input
}
`
,
()
=>
{
expect
(
getZeroBasedIdFromGraphQLId
(
input
)).
toBe
(
output
);
});
});
});
describe
(
'
convertToGraphQLId
'
,
()
=>
{
describe
(
'
convertToGraphQLId
'
,
()
=>
{
it
(
'
combines $type and $id into $result
'
,
()
=>
{
it
(
'
combines $type and $id into $result
'
,
()
=>
{
expect
(
convertToGraphQLId
(
mockType
,
mockId
)).
toBe
(
mockGid
);
expect
(
convertToGraphQLId
(
mockType
,
mockId
)).
toBe
(
mockGid
);
...
...
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