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
b8c60584
Commit
b8c60584
authored
Mar 29, 2022
by
Florie Guibert
Committed by
Simon Knox
Mar 29, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Consolidate boards into one Vue app
No user facing changes
parent
abbaf0db
Changes
25
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
413 additions
and
187 deletions
+413
-187
app/assets/javascripts/boards/components/board_app.vue
app/assets/javascripts/boards/components/board_app.vue
+3
-0
app/assets/javascripts/boards/components/board_form.vue
app/assets/javascripts/boards/components/board_form.vue
+2
-2
app/assets/javascripts/boards/components/board_top_bar.vue
app/assets/javascripts/boards/components/board_top_bar.vue
+54
-0
app/assets/javascripts/boards/components/boards_selector.vue
app/assets/javascripts/boards/components/boards_selector.vue
+9
-25
app/assets/javascripts/boards/components/config_toggle.vue
app/assets/javascripts/boards/components/config_toggle.vue
+1
-10
app/assets/javascripts/boards/components/issue_board_filtered_search.vue
...scripts/boards/components/issue_board_filtered_search.vue
+1
-11
app/assets/javascripts/boards/components/toggle_focus.vue
app/assets/javascripts/boards/components/toggle_focus.vue
+1
-7
app/assets/javascripts/boards/index.js
app/assets/javascripts/boards/index.js
+35
-56
app/helpers/boards_helper.rb
app/helpers/boards_helper.rb
+15
-1
app/views/shared/boards/_show.html.haml
app/views/shared/boards/_show.html.haml
+0
-2
ee/app/assets/javascripts/boards/components/epic_filtered_search.vue
...ts/javascripts/boards/components/epic_filtered_search.vue
+1
-10
ee/app/assets/javascripts/epic_boards/index.js
ee/app/assets/javascripts/epic_boards/index.js
+35
-40
ee/app/helpers/ee/boards_helper.rb
ee/app/helpers/ee/boards_helper.rb
+12
-2
ee/app/models/ee/issue.rb
ee/app/models/ee/issue.rb
+1
-1
ee/spec/frontend/boards/components/board_form_spec.js
ee/spec/frontend/boards/components/board_form_spec.js
+1
-1
ee/spec/frontend/boards/components/board_top_bar_spec.js
ee/spec/frontend/boards/components/board_top_bar_spec.js
+116
-0
ee/spec/frontend/boards/components/boards_selector_spec.js
ee/spec/frontend/boards/components/boards_selector_spec.js
+4
-4
ee/spec/frontend/boards/components/epic_filtered_search_spec.js
...c/frontend/boards/components/epic_filtered_search_spec.js
+1
-5
ee/spec/frontend/boards/components/issue_board_filtered_search_spec.js
...end/boards/components/issue_board_filtered_search_spec.js
+2
-1
ee/spec/helpers/boards_helper_spec.rb
ee/spec/helpers/boards_helper_spec.rb
+9
-1
spec/frontend/boards/components/board_form_spec.js
spec/frontend/boards/components/board_form_spec.js
+1
-1
spec/frontend/boards/components/board_top_bar_spec.js
spec/frontend/boards/components/board_top_bar_spec.js
+88
-0
spec/frontend/boards/components/boards_selector_spec.js
spec/frontend/boards/components/boards_selector_spec.js
+4
-4
spec/frontend/boards/components/issue_board_filtered_search_spec.js
...end/boards/components/issue_board_filtered_search_spec.js
+2
-1
spec/helpers/boards_helper_spec.rb
spec/helpers/boards_helper_spec.rb
+15
-2
No files found.
app/assets/javascripts/boards/components/board_app.vue
View file @
b8c60584
...
...
@@ -2,11 +2,13 @@
import
{
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
BoardContent
from
'
~/boards/components/board_content.vue
'
;
import
BoardSettingsSidebar
from
'
~/boards/components/board_settings_sidebar.vue
'
;
import
BoardTopBar
from
'
~/boards/components/board_top_bar.vue
'
;
export
default
{
components
:
{
BoardContent
,
BoardSettingsSidebar
,
BoardTopBar
,
},
inject
:
[
'
disabled
'
],
computed
:
{
...
...
@@ -23,6 +25,7 @@ export default {
<
template
>
<div
class=
"boards-app gl-relative"
:class=
"
{ 'is-compact': isSidebarOpen }">
<board-top-bar
/>
<board-content
:disabled=
"disabled"
/>
<board-settings-sidebar
/>
</div>
...
...
app/assets/javascripts/boards/components/board_form.vue
View file @
b8c60584
...
...
@@ -48,7 +48,7 @@ export default {
fullPath
:
{
default
:
''
,
},
rootPath
:
{
boardBaseUrl
:
{
default
:
''
,
},
},
...
...
@@ -209,7 +209,7 @@ export default {
if
(
this
.
isDeleteForm
)
{
try
{
await
this
.
deleteBoard
();
visitUrl
(
this
.
rootPath
);
visitUrl
(
this
.
boardBaseUrl
);
}
catch
{
this
.
setError
({
message
:
this
.
$options
.
i18n
.
deleteErrorMessage
});
}
finally
{
...
...
app/assets/javascripts/boards/components/board_top_bar.vue
0 → 100644
View file @
b8c60584
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
BoardAddNewColumnTrigger
from
'
~/boards/components/board_add_new_column_trigger.vue
'
;
import
BoardsSelector
from
'
ee_else_ce/boards/components/boards_selector.vue
'
;
import
IssueBoardFilteredSearch
from
'
ee_else_ce/boards/components/issue_board_filtered_search.vue
'
;
import
ConfigToggle
from
'
./config_toggle.vue
'
;
import
NewBoardButton
from
'
./new_board_button.vue
'
;
import
ToggleFocus
from
'
./toggle_focus.vue
'
;
export
default
{
components
:
{
BoardAddNewColumnTrigger
,
BoardsSelector
,
IssueBoardFilteredSearch
,
ConfigToggle
,
NewBoardButton
,
ToggleFocus
,
ToggleLabels
:
()
=>
import
(
'
ee_component/boards/components/toggle_labels.vue
'
),
ToggleEpicsSwimlanes
:
()
=>
import
(
'
ee_component/boards/components/toggle_epics_swimlanes.vue
'
),
EpicBoardFilteredSearch
:
()
=>
import
(
'
ee_component/boards/components/epic_filtered_search.vue
'
),
},
inject
:
[
'
swimlanesFeatureAvailable
'
,
'
canAdminList
'
,
'
isSignedIn
'
],
computed
:
{
...
mapGetters
([
'
isEpicBoard
'
]),
},
};
</
script
>
<
template
>
<div
class=
"issues-filters"
>
<div
class=
"issues-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row row-content-block second-block"
>
<div
class=
"gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0! mb-md-2 mb-sm-0 gl-w-full"
>
<boards-selector
/>
<new-board-button
/>
<epic-board-filtered-search
v-if=
"isEpicBoard"
/>
<issue-board-filtered-search
v-else
/>
</div>
<div
class=
"filter-dropdown-container gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-align-items-flex-start"
>
<toggle-labels
/>
<toggle-epics-swimlanes
v-if=
"swimlanesFeatureAvailable && isSignedIn"
/>
<config-toggle
/>
<board-add-new-column-trigger
v-if=
"canAdminList"
/>
<toggle-focus
/>
</div>
</div>
</div>
</
template
>
app/assets/javascripts/boards/components/boards_selector.vue
View file @
b8c60584
...
...
@@ -40,37 +40,21 @@ export default {
directives
:
{
GlModalDirective
,
},
inject
:
[
'
fullPath
'
],
inject
:
[
'
boardBaseUrl
'
,
'
fullPath
'
,
'
canAdminBoard
'
,
'
multipleIssueBoardsAvailable
'
,
'
hasMissingBoards
'
,
'
scopedIssueBoardFeatureEnabled
'
,
'
weights
'
,
],
props
:
{
throttleDuration
:
{
type
:
Number
,
default
:
200
,
required
:
false
,
},
boardBaseUrl
:
{
type
:
String
,
required
:
true
,
},
hasMissingBoards
:
{
type
:
Boolean
,
required
:
true
,
},
canAdminBoard
:
{
type
:
Boolean
,
required
:
true
,
},
multipleIssueBoardsAvailable
:
{
type
:
Boolean
,
required
:
true
,
},
scopedIssueBoardFeatureEnabled
:
{
type
:
Boolean
,
required
:
true
,
},
weights
:
{
type
:
Array
,
required
:
true
,
},
},
data
()
{
return
{
...
...
app/assets/javascripts/boards/components/config_toggle.vue
View file @
b8c60584
...
...
@@ -14,16 +14,7 @@ export default {
GlModalDirective
,
},
mixins
:
[
Tracking
.
mixin
()],
props
:
{
canAdminList
:
{
type
:
Boolean
,
required
:
true
,
},
hasScope
:
{
type
:
Boolean
,
required
:
true
,
},
},
inject
:
[
'
canAdminList
'
,
'
hasScope
'
],
computed
:
{
buttonText
()
{
return
this
.
canAdminList
?
s__
(
'
Boards|Edit board
'
)
:
s__
(
'
Boards|View scope
'
);
...
...
app/assets/javascripts/boards/components/issue_board_filtered_search.vue
View file @
b8c60584
...
...
@@ -41,17 +41,7 @@ export default {
confidential
:
__
(
'
Confidential
'
),
},
components
:
{
BoardFilteredSearch
},
inject
:
[
'
isSignedIn
'
,
'
releasesFetchPath
'
],
props
:
{
fullPath
:
{
type
:
String
,
required
:
true
,
},
boardType
:
{
type
:
String
,
required
:
true
,
},
},
inject
:
[
'
isSignedIn
'
,
'
releasesFetchPath
'
,
'
fullPath
'
,
'
boardType
'
],
computed
:
{
isGroupBoard
()
{
return
this
.
boardType
===
BoardType
.
group
;
...
...
app/assets/javascripts/boards/components/toggle_focus.vue
View file @
b8c60584
...
...
@@ -10,12 +10,6 @@ export default {
directives
:
{
GlTooltip
,
},
props
:
{
issueBoardsContentSelector
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
isFullscreen
:
false
,
...
...
@@ -25,7 +19,7 @@ export default {
toggleFocusMode
()
{
hide
(
this
.
$refs
.
toggleFocusModeButton
);
const
issueBoardsContent
=
document
.
querySelector
(
this
.
issueBoardsContentSelector
);
const
issueBoardsContent
=
document
.
querySelector
(
'
.content-wrapper > .js-focus-mode-board
'
);
issueBoardsContent
.
classList
.
toggle
(
'
is-focused
'
);
this
.
isFullscreen
=
!
this
.
isFullscreen
;
...
...
app/assets/javascripts/boards/index.js
View file @
b8c60584
import
PortalVue
from
'
portal-vue
'
;
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
toggleEpicsSwimlanes
from
'
ee_else_ce/boards/toggle_epics_swimlanes
'
;
import
toggleLabels
from
'
ee_else_ce/boards/toggle_labels
'
;
import
BoardAddNewColumnTrigger
from
'
~/boards/components/board_add_new_column_trigger.vue
'
;
import
BoardApp
from
'
~/boards/components/board_app.vue
'
;
import
'
~/boards/filters/due_date_filters
'
;
import
{
issuableTypes
}
from
'
~/boards/constants
'
;
import
initBoardsFilteredSearch
from
'
~/boards/mount_filtered_search_issue_boards
'
;
import
store
from
'
~/boards/stores
'
;
import
toggleFocusMode
from
'
~/boards/toggle_focus
'
;
import
{
NavigationType
,
isLoggedIn
,
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
NavigationType
,
isLoggedIn
,
parseBoolean
,
convertObjectPropsToCamelCase
,
}
from
'
~/lib/utils/common_utils
'
;
import
{
queryToObject
}
from
'
~/lib/utils/url_utility
'
;
import
{
fullBoardId
}
from
'
./boards_util
'
;
import
boardConfigToggle
from
'
./config_toggle
'
;
import
initNewBoard
from
'
./new_board
'
;
import
{
gqlClient
}
from
'
./graphql
'
;
import
mountMultipleBoardsSwitcher
from
'
./mount_multiple_boards_switcher
'
;
Vue
.
use
(
VueApollo
);
Vue
.
use
(
PortalVue
);
...
...
@@ -28,6 +26,12 @@ const apolloProvider = new VueApollo({
function
mountBoardApp
(
el
)
{
const
{
boardId
,
groupId
,
fullPath
,
rootPath
}
=
el
.
dataset
;
const
rawFilterParams
=
queryToObject
(
window
.
location
.
search
,
{
gatherArrays
:
true
});
const
initialFilterParams
=
{
...
convertObjectPropsToCamelCase
(
rawFilterParams
),
};
store
.
dispatch
(
'
fetchBoard
'
,
{
fullPath
,
fullBoardId
:
fullBoardId
(
boardId
),
...
...
@@ -54,26 +58,41 @@ function mountBoardApp(el) {
boardId
,
groupId
:
Number
(
groupId
),
rootPath
,
fullPath
,
initialFilterParams
,
boardBaseUrl
:
el
.
dataset
.
boardBaseUrl
,
boardType
:
el
.
dataset
.
parent
,
currentUserId
:
gon
.
current_user_id
||
null
,
canUpdate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
canAdminList
:
parseBoolean
(
el
.
dataset
.
canAdminList
),
boardWeight
:
el
.
dataset
.
boardWeight
?
parseInt
(
el
.
dataset
.
boardWeight
,
10
)
:
null
,
labelsManagePath
:
el
.
dataset
.
labelsManagePath
,
labelsFilterBasePath
:
el
.
dataset
.
labelsFilterBasePath
,
releasesFetchPath
:
el
.
dataset
.
releasesFetchPath
,
timeTrackingLimitToHours
:
parseBoolean
(
el
.
dataset
.
timeTrackingLimitToHours
),
issuableType
:
issuableTypes
.
issue
,
emailsDisabled
:
parseBoolean
(
el
.
dataset
.
emailsDisabled
),
hasScope
:
parseBoolean
(
el
.
dataset
.
hasScope
),
hasMissingBoards
:
parseBoolean
(
el
.
dataset
.
hasMissingBoards
),
weights
:
el
.
dataset
.
weights
?
JSON
.
parse
(
el
.
dataset
.
weights
)
:
[],
// Permissions
canUpdate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
canAdminList
:
parseBoolean
(
el
.
dataset
.
canAdminList
),
canAdminBoard
:
parseBoolean
(
el
.
dataset
.
canAdminBoard
),
allowLabelCreate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowLabelEdit
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
isSignedIn
:
isLoggedIn
(),
// Features
multipleAssigneesFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
multipleAssigneesFeatureAvailable
),
epicFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
epicFeatureAvailable
),
iterationFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
iterationFeatureAvailable
),
weightFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
weightFeatureAvailable
),
boardWeight
:
el
.
dataset
.
boardWeight
?
parseInt
(
el
.
dataset
.
boardWeight
,
10
)
:
null
,
scopedLabelsAvailable
:
parseBoolean
(
el
.
dataset
.
scopedLabels
),
milestoneListsAvailable
:
parseBoolean
(
el
.
dataset
.
milestoneListsAvailable
),
assigneeListsAvailable
:
parseBoolean
(
el
.
dataset
.
assigneeListsAvailable
),
iterationListsAvailable
:
parseBoolean
(
el
.
dataset
.
iterationListsAvailable
),
issuableType
:
issuableTypes
.
issue
,
emailsDisabled
:
parseBoolean
(
el
.
dataset
.
emailsDisabled
),
allowLabelCreate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowLabelEdit
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowScopedLabels
:
parseBoolean
(
el
.
dataset
.
scopedLabels
),
swimlanesFeatureAvailable
:
gon
.
licensed_features
?.
swimlanes
,
multipleIssueBoardsAvailable
:
parseBoolean
(
el
.
dataset
.
multipleBoardsAvailable
),
scopedIssueBoardFeatureEnabled
:
parseBoolean
(
el
.
dataset
.
scopedIssueBoardFeatureEnabled
),
},
render
:
(
createComponent
)
=>
createComponent
(
BoardApp
),
});
...
...
@@ -92,47 +111,7 @@ export default () => {
}
});
const
{
releasesFetchPath
,
epicFeatureAvailable
,
iterationFeatureAvailable
}
=
$boardApp
.
dataset
;
initBoardsFilteredSearch
(
apolloProvider
,
isLoggedIn
(),
releasesFetchPath
,
parseBoolean
(
epicFeatureAvailable
),
parseBoolean
(
iterationFeatureAvailable
),
);
mountBoardApp
(
$boardApp
);
const
createColumnTriggerEl
=
document
.
querySelector
(
'
.js-create-column-trigger
'
);
if
(
createColumnTriggerEl
)
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
createColumnTriggerEl
,
name
:
'
BoardAddNewColumnTriggerRoot
'
,
components
:
{
BoardAddNewColumnTrigger
,
},
store
,
render
(
createElement
)
{
return
createElement
(
'
board-add-new-column-trigger
'
);
},
});
}
boardConfigToggle
();
initNewBoard
();
toggleFocusMode
();
toggleLabels
();
if
(
gon
.
licensed_features
?.
swimlanes
)
{
toggleEpicsSwimlanes
();
}
mountMultipleBoardsSwitcher
({
fullPath
:
$boardApp
.
dataset
.
fullPath
,
rootPath
:
$boardApp
.
dataset
.
boardsEndpoint
,
allowScopedLabels
:
$boardApp
.
dataset
.
scopedLabels
,
labelsManagePath
:
$boardApp
.
dataset
.
labelsManagePath
,
});
};
app/helpers/boards_helper.rb
View file @
b8c60584
...
...
@@ -16,6 +16,7 @@ module BoardsHelper
bulk_update_path:
@bulk_issues_path
,
can_update:
can_update?
.
to_s
,
can_admin_list:
can_admin_list?
.
to_s
,
can_admin_board:
can_admin_board?
.
to_s
,
time_tracking_limit_to_hours:
Gitlab
::
CurrentSettings
.
time_tracking_limit_to_hours
.
to_s
,
parent:
current_board_parent
.
model_name
.
param_key
,
group_id:
group_id
,
...
...
@@ -23,7 +24,11 @@ module BoardsHelper
labels_fetch_path:
labels_fetch_path
,
labels_manage_path:
labels_manage_path
,
releases_fetch_path:
releases_fetch_path
,
board_type:
board
.
to_type
board_type:
board
.
to_type
,
has_scope:
board
.
scoped?
.
to_s
,
has_missing_boards:
has_missing_boards?
.
to_s
,
multiple_boards_available:
multiple_boards_available?
.
to_s
,
board_base_url:
board_base_url
}
end
...
...
@@ -85,6 +90,11 @@ module BoardsHelper
current_board_parent
.
multiple_issue_boards_available?
end
# Boards are hidden when extra boards were created but the license does not allow multiple boards
def
has_missing_boards?
!
multiple_boards_available?
&&
current_board_parent
.
boards
.
size
>
1
end
def
current_board_path
(
board
)
@current_board_path
||=
if
board
.
group_board?
group_board_path
(
current_board_parent
,
board
)
...
...
@@ -109,6 +119,10 @@ module BoardsHelper
can?
(
current_user
,
:admin_issue_board_list
,
current_board_parent
)
end
def
can_admin_board?
can?
(
current_user
,
:admin_issue_board
,
current_board_parent
)
end
def
can_admin_issue?
can?
(
current_user
,
:admin_issue
,
current_board_parent
)
end
...
...
app/views/shared/boards/_show.html.haml
View file @
b8c60584
...
...
@@ -16,6 +16,4 @@
-
page_title
(
"
#{
board
.
name
}
"
,
_
(
"Boards"
))
-
add_page_specific_style
'page_bundles/boards'
=
render
'shared/issuable/search_bar'
,
type: :boards
,
board:
board
#js-issuable-board-app
{
data:
board_data
}
ee/app/assets/javascripts/boards/components/epic_filtered_search.vue
View file @
b8c60584
...
...
@@ -16,16 +16,7 @@ export default {
author
:
__
(
'
Author
'
),
},
components
:
{
BoardFilteredSearch
},
props
:
{
fullPath
:
{
type
:
String
,
required
:
true
,
},
boardType
:
{
type
:
String
,
required
:
true
,
},
},
inject
:
[
'
fullPath
'
,
'
boardType
'
],
computed
:
{
tokens
()
{
const
{
fetchLabels
,
fetchAuthors
}
=
issueBoardFilter
(
...
...
ee/app/assets/javascripts/epic_boards/index.js
View file @
b8c60584
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
initFilteredSearch
from
'
ee/boards/epic_filtered_search
'
;
import
{
fullEpicBoardId
}
from
'
ee_component/boards/boards_util
'
;
import
toggleLabels
from
'
ee_component/boards/toggle_labels
'
;
import
BoardAddNewColumnTrigger
from
'
~/boards/components/board_add_new_column_trigger.vue
'
;
import
BoardApp
from
'
~/boards/components/board_app.vue
'
;
import
boardConfigToggle
from
'
~/boards/config_toggle
'
;
import
{
issuableTypes
}
from
'
~/boards/constants
'
;
import
mountMultipleBoardsSwitcher
from
'
~/boards/mount_multiple_boards_switcher
'
;
import
store
from
'
~/boards/stores
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
'
~/boards/filters/due_date_filters
'
;
import
{
NavigationType
,
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
NavigationType
,
isLoggedIn
,
parseBoolean
,
convertObjectPropsToCamelCase
,
}
from
'
~/lib/utils/common_utils
'
;
import
{
queryToObject
}
from
'
~/lib/utils/url_utility
'
;
Vue
.
use
(
VueApollo
);
...
...
@@ -25,6 +26,12 @@ const apolloProvider = new VueApollo({
function
mountBoardApp
(
el
)
{
const
{
boardId
,
groupId
,
fullPath
,
rootPath
}
=
el
.
dataset
;
const
rawFilterParams
=
queryToObject
(
window
.
location
.
search
,
{
gatherArrays
:
true
});
const
initialFilterParams
=
{
...
convertObjectPropsToCamelCase
(
rawFilterParams
),
};
store
.
dispatch
(
'
setInitialBoardData
'
,
{
allowSubEpics
:
parseBoolean
(
el
.
dataset
.
subEpicsFeatureAvailable
),
boardType
:
el
.
dataset
.
parent
,
...
...
@@ -46,27 +53,41 @@ function mountBoardApp(el) {
boardId
,
groupId
:
parseInt
(
groupId
,
10
),
rootPath
,
fullPath
,
initialFilterParams
,
boardBaseUrl
:
el
.
dataset
.
boardBaseUrl
,
boardType
:
el
.
dataset
.
parent
,
currentUserId
:
gon
.
current_user_id
||
null
,
canUpdate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
canAdminList
:
parseBoolean
(
el
.
dataset
.
canAdminList
),
labelsFetchPath
:
el
.
dataset
.
labelsFetchPath
,
labelsManagePath
:
el
.
dataset
.
labelsManagePath
,
labelsFilterBasePath
:
el
.
dataset
.
labelsFilterBasePath
,
timeTrackingLimitToHours
:
parseBoolean
(
el
.
dataset
.
timeTrackingLimitToHours
),
boardWeight
:
el
.
dataset
.
boardWeight
?
parseInt
(
el
.
dataset
.
boardWeight
,
10
)
:
null
,
issuableType
:
issuableTypes
.
epic
,
emailsDisabled
:
parseBoolean
(
el
.
dataset
.
emailsDisabled
),
hasMissingBoards
:
parseBoolean
(
el
.
dataset
.
hasMissingBoards
),
hasScope
:
parseBoolean
(
el
.
dataset
.
hasScope
),
weights
:
JSON
.
parse
(
el
.
dataset
.
weights
),
// Permissions
canUpdate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
canAdminList
:
parseBoolean
(
el
.
dataset
.
canAdminList
),
canAdminBoard
:
parseBoolean
(
el
.
dataset
.
canAdminBoard
),
allowLabelCreate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowLabelEdit
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowScopedLabels
:
parseBoolean
(
el
.
dataset
.
scopedLabels
),
isSignedIn
:
isLoggedIn
(),
// Features
multipleAssigneesFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
multipleAssigneesFeatureAvailable
),
epicFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
epicFeatureAvailable
),
iterationFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
iterationFeatureAvailable
),
weightFeatureAvailable
:
parseBoolean
(
el
.
dataset
.
weightFeatureAvailable
),
boardWeight
:
el
.
dataset
.
boardWeight
?
parseInt
(
el
.
dataset
.
boardWeight
,
10
)
:
null
,
scopedLabelsAvailable
:
parseBoolean
(
el
.
dataset
.
scopedLabels
),
milestoneListsAvailable
:
false
,
assigneeListsAvailable
:
false
,
iterationListsAvailable
:
false
,
issuableType
:
issuableTypes
.
epic
,
emailsDisabled
:
parseBoolean
(
el
.
dataset
.
emailsDisabled
),
allowLabelCreate
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowLabelEdit
:
parseBoolean
(
el
.
dataset
.
canUpdate
),
allowScopedLabels
:
parseBoolean
(
el
.
dataset
.
scopedLabels
),
swimlanesFeatureAvailable
:
false
,
multipleIssueBoardsAvailable
:
true
,
scopedIssueBoardFeatureEnabled
:
true
,
},
render
:
(
createComponent
)
=>
createComponent
(
BoardApp
),
});
...
...
@@ -85,31 +106,5 @@ export default () => {
}
});
initFilteredSearch
(
apolloProvider
);
mountBoardApp
(
$boardApp
);
const
createColumnTriggerEl
=
document
.
querySelector
(
'
.js-create-column-trigger
'
);
if
(
createColumnTriggerEl
)
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
createColumnTriggerEl
,
name
:
'
BoardAddNewColumnTriggerRoot
'
,
components
:
{
BoardAddNewColumnTrigger
,
},
store
,
render
(
createElement
)
{
return
createElement
(
BoardAddNewColumnTrigger
);
},
});
}
toggleLabels
();
boardConfigToggle
();
mountMultipleBoardsSwitcher
({
fullPath
:
$boardApp
.
dataset
.
fullPath
,
rootPath
:
$boardApp
.
dataset
.
boardsEndpoint
,
});
};
ee/app/helpers/ee/boards_helper.rb
View file @
b8c60584
...
...
@@ -29,8 +29,10 @@ module EE
show_promotion:
show_feature_promotion
,
can_update:
can_update?
.
to_s
,
can_admin_list:
can_admin_list?
.
to_s
,
can_admin_board:
can_admin_board?
.
to_s
,
disabled:
board
.
disabled_for?
(
current_user
).
to_s
,
emails_disabled:
current_board_parent
.
emails_disabled?
.
to_s
emails_disabled:
current_board_parent
.
emails_disabled?
.
to_s
,
weights:
::
Issue
.
weight_options
}
super
.
merge
(
data
).
merge
(
licensed_features
).
merge
(
group_level_features
)
...
...
@@ -43,7 +45,8 @@ module EE
weight_feature_available:
current_board_parent
.
feature_available?
(
:issue_weights
).
to_s
,
milestone_lists_available:
current_board_parent
.
feature_available?
(
:board_milestone_lists
).
to_s
,
assignee_lists_available:
current_board_parent
.
feature_available?
(
:board_assignee_lists
).
to_s
,
scoped_labels:
current_board_parent
.
feature_available?
(
:scoped_labels
)
&
.
to_s
scoped_labels:
current_board_parent
.
feature_available?
(
:scoped_labels
)
&
.
to_s
,
scoped_issue_board_feature_enabled:
current_board_parent
.
feature_available?
(
:scoped_issue_board
).
to_s
}
end
...
...
@@ -71,6 +74,13 @@ module EE
super
end
override
:can_admin_board?
def
can_admin_board?
return
can?
(
current_user
,
:admin_epic_board
,
current_board_parent
)
if
board
.
is_a?
(
::
Boards
::
EpicBoard
)
super
end
override
:build_issue_link_base
def
build_issue_link_base
return
group_epics_path
(
@group
)
if
board
.
is_a?
(
::
Boards
::
EpicBoard
)
...
...
ee/app/models/ee/issue.rb
View file @
b8c60584
...
...
@@ -220,7 +220,7 @@ module EE
end
def
weight_options
[
WEIGHT_NONE
]
+
WEIGHT_RANGE
.
to_a
[
WEIGHT_NONE
,
WEIGHT_ANY
]
+
WEIGHT_RANGE
.
to_a
end
end
...
...
ee/spec/frontend/boards/components/board_form_spec.js
View file @
b8c60584
...
...
@@ -73,7 +73,7 @@ describe('BoardForm', () => {
wrapper
=
shallowMountExtended
(
BoardForm
,
{
propsData
:
{
...
defaultProps
,
...
props
},
provide
:
{
rootPath
:
'
root
'
,
boardBaseUrl
:
'
root
'
,
glFeatures
:
{
iterationCadences
},
},
mocks
:
{
...
...
ee/spec/frontend/boards/components/board_top_bar_spec.js
0 → 100644
View file @
b8c60584
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
ToggleEpicsSwimlanes
from
'
ee/boards/components/toggle_epics_swimlanes.vue
'
;
import
IssueBoardFilteredSearch
from
'
ee/boards/components/issue_board_filtered_search.vue
'
;
import
EpicBoardFilteredSearch
from
'
ee/boards/components/epic_filtered_search.vue
'
;
import
ToggleLabels
from
'
ee/boards/components/toggle_labels.vue
'
;
import
BoardTopBar
from
'
~/boards/components/board_top_bar.vue
'
;
import
BoardsSelector
from
'
~/boards/components/boards_selector.vue
'
;
import
ConfigToggle
from
'
~/boards/components/config_toggle.vue
'
;
import
NewBoardButton
from
'
~/boards/components/new_board_button.vue
'
;
import
ToggleFocus
from
'
~/boards/components/toggle_focus.vue
'
;
describe
(
'
BoardTopBar
'
,
()
=>
{
let
wrapper
;
Vue
.
use
(
Vuex
);
const
createStore
=
({
mockGetters
=
{}
}
=
{})
=>
{
return
new
Vuex
.
Store
({
state
:
{},
getters
:
{
isEpicBoard
:
()
=>
false
,
...
mockGetters
,
},
});
};
const
createComponent
=
({
provide
=
{},
mockGetters
=
{}
}
=
{})
=>
{
const
store
=
createStore
({
mockGetters
});
wrapper
=
shallowMount
(
BoardTopBar
,
{
store
,
provide
:
{
swimlanesFeatureAvailable
:
false
,
canAdminList
:
false
,
isSignedIn
:
false
,
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
,
releasesFetchPath
:
'
/releases
'
,
epicFeatureAvailable
:
true
,
iterationFeatureAvailable
:
true
,
...
provide
,
},
stubs
:
{
IssueBoardFilteredSearch
,
EpicBoardFilteredSearch
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
base template
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders BoardsSelector component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardsSelector
).
exists
()).
toBe
(
true
);
});
it
(
'
renders NewBoardButton component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
NewBoardButton
).
exists
()).
toBe
(
true
);
});
it
(
'
renders ConfigToggle component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ConfigToggle
).
exists
()).
toBe
(
true
);
});
it
(
'
renders ToggleFocus component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ToggleFocus
).
exists
()).
toBe
(
true
);
});
it
(
'
renders ToggleLabels component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ToggleLabels
).
exists
()).
toBe
(
true
);
});
it
(
'
does not render ToggleEpicsSwimlanes component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ToggleEpicsSwimlanes
).
exists
()).
toBe
(
false
);
});
});
describe
(
'
filter bar
'
,
()
=>
{
it
.
each
`
isEpicBoard | filterBarComponent | filterBarName | otherFilterBar
${
false
}
|
${
IssueBoardFilteredSearch
}
|
${
'
IssueBoardFilteredSearch
'
}
|
${
EpicBoardFilteredSearch
}
${
true
}
|
${
EpicBoardFilteredSearch
}
|
${
'
EpicBoardFilteredSearch
'
}
|
${
IssueBoardFilteredSearch
}
`
(
'
renders $filterBarName when isEpicBoard is $isEpicBoard
'
,
async
({
isEpicBoard
,
filterBarComponent
,
otherFilterBar
})
=>
{
createComponent
({
mockGetters
:
{
isEpicBoard
:
()
=>
isEpicBoard
}
});
await
nextTick
();
expect
(
wrapper
.
findComponent
(
filterBarComponent
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
findComponent
(
otherFilterBar
).
exists
()).
toBe
(
false
);
},
);
});
describe
(
'
when user is logged in and swimlanes are available
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
provide
:
{
swimlanesFeatureAvailable
:
true
,
isSignedIn
:
true
,
},
});
it
(
'
renders ToggleEpicsSwimlanes component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ToggleEpicsSwimlanes
).
exists
()).
toBe
(
true
);
});
});
});
});
ee/spec/frontend/boards/components/boards_selector_spec.js
View file @
b8c60584
...
...
@@ -80,6 +80,10 @@ describe('BoardsSelector', () => {
apolloProvider
:
fakeApollo
,
propsData
:
{
throttleDuration
,
},
attachTo
:
document
.
body
,
provide
:
{
fullPath
:
''
,
boardBaseUrl
:
`
${
TEST_HOST
}
/board/base/url`
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
...
...
@@ -87,10 +91,6 @@ describe('BoardsSelector', () => {
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
},
attachTo
:
document
.
body
,
provide
:
{
fullPath
:
''
,
},
});
};
...
...
ee/spec/frontend/boards/components/epic_filtered_search_spec.js
View file @
b8c60584
...
...
@@ -13,11 +13,7 @@ describe('EpicFilteredSearch', () => {
const
createComponent
=
({
initialFilterParams
=
{}
}
=
{})
=>
{
wrapper
=
shallowMount
(
EpicFilteredSearch
,
{
provide
:
{
initialFilterParams
},
props
:
{
fullPath
:
''
,
boardType
:
''
,
},
provide
:
{
initialFilterParams
,
fullPath
:
''
,
boardType
:
''
},
});
};
...
...
ee/spec/frontend/boards/components/issue_board_filtered_search_spec.js
View file @
b8c60584
...
...
@@ -12,10 +12,11 @@ describe('IssueBoardFilter', () => {
const
createComponent
=
()
=>
{
wrapper
=
shallowMount
(
IssueBoardFilteredSpec
,
{
propsData
:
{
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
},
provide
:
{
isSignedIn
:
true
,
releasesFetchPath
:
'
/releases
'
,
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
,
epicFeatureAvailable
:
true
,
iterationFeatureAvailable
:
true
,
},
...
...
ee/spec/helpers/boards_helper_spec.rb
View file @
b8c60584
...
...
@@ -74,6 +74,7 @@ RSpec.describe BoardsHelper do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:create_non_backlog_issues
,
project_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue
,
project_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
project
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board
,
project
).
and_return
(
false
)
end
shared_examples
'serializes the availability of a licensed feature'
do
|
feature_name
,
feature_key
|
...
...
@@ -123,7 +124,8 @@ RSpec.describe BoardsHelper do
[
:issue_weights
,
:weight_feature_available
],
[
:board_milestone_lists
,
:milestone_lists_available
],
[
:board_assignee_lists
,
:assignee_lists_available
],
[
:scoped_labels
,
:scoped_labels
]].
each
do
|
feature_name
,
feature_key
|
[
:scoped_labels
,
:scoped_labels
],
[
:scoped_issue_board
,
:scoped_issue_board_feature_enabled
]].
each
do
|
feature_name
,
feature_key
|
include_examples
"serializes the availability of a licensed feature"
,
feature_name
,
feature_key
end
end
...
...
@@ -148,9 +150,11 @@ RSpec.describe BoardsHelper do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:create_non_backlog_issues
,
epic_board
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_epic
,
epic_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_epic_board_list
,
group
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_epic_board
,
group
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue
,
group
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
group
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board
,
group
).
and_return
(
false
)
end
it
'returns the correct permission for updating the board'
do
...
...
@@ -160,6 +164,10 @@ RSpec.describe BoardsHelper do
it
'returns the correct permission for administering the boards lists'
do
expect
(
board_data
[
:can_admin_list
]).
to
eq
"true"
end
it
'returns the correct permission for administering the boards'
do
expect
(
board_data
[
:can_admin_board
]).
to
eq
"true"
end
end
end
end
spec/frontend/boards/components/board_form_spec.js
View file @
b8c60584
...
...
@@ -62,7 +62,7 @@ describe('BoardForm', () => {
};
},
provide
:
{
rootPath
:
'
root
'
,
boardBaseUrl
:
'
root
'
,
},
mocks
:
{
$apollo
:
{
...
...
spec/frontend/boards/components/board_top_bar_spec.js
0 → 100644
View file @
b8c60584
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
BoardTopBar
from
'
~/boards/components/board_top_bar.vue
'
;
import
BoardAddNewColumnTrigger
from
'
~/boards/components/board_add_new_column_trigger.vue
'
;
import
BoardsSelector
from
'
~/boards/components/boards_selector.vue
'
;
import
ConfigToggle
from
'
~/boards/components/config_toggle.vue
'
;
import
IssueBoardFilteredSearch
from
'
~/boards/components/issue_board_filtered_search.vue
'
;
import
NewBoardButton
from
'
~/boards/components/new_board_button.vue
'
;
import
ToggleFocus
from
'
~/boards/components/toggle_focus.vue
'
;
describe
(
'
BoardTopBar
'
,
()
=>
{
let
wrapper
;
Vue
.
use
(
Vuex
);
const
createStore
=
({
mockGetters
=
{}
}
=
{})
=>
{
return
new
Vuex
.
Store
({
state
:
{},
getters
:
{
isEpicBoard
:
()
=>
false
,
...
mockGetters
,
},
});
};
const
createComponent
=
({
provide
=
{},
mockGetters
=
{}
}
=
{})
=>
{
const
store
=
createStore
({
mockGetters
});
wrapper
=
shallowMount
(
BoardTopBar
,
{
store
,
provide
:
{
swimlanesFeatureAvailable
:
false
,
canAdminList
:
false
,
isSignedIn
:
false
,
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
,
releasesFetchPath
:
'
/releases
'
,
...
provide
,
},
stubs
:
{
IssueBoardFilteredSearch
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
base template
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders BoardsSelector component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardsSelector
).
exists
()).
toBe
(
true
);
});
it
(
'
renders IssueBoardFilteredSearch component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
IssueBoardFilteredSearch
).
exists
()).
toBe
(
true
);
});
it
(
'
renders NewBoardButton component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
NewBoardButton
).
exists
()).
toBe
(
true
);
});
it
(
'
renders ConfigToggle component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ConfigToggle
).
exists
()).
toBe
(
true
);
});
it
(
'
renders ToggleFocus component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
ToggleFocus
).
exists
()).
toBe
(
true
);
});
it
(
'
does not render BoardAddNewColumnTrigger component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardAddNewColumnTrigger
).
exists
()).
toBe
(
false
);
});
});
describe
(
'
when user can admin list
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
provide
:
{
canAdminList
:
true
}
});
});
it
(
'
renders BoardAddNewColumnTrigger component
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
BoardAddNewColumnTrigger
).
exists
()).
toBe
(
true
);
});
});
});
spec/frontend/boards/components/boards_selector_spec.js
View file @
b8c60584
...
...
@@ -105,6 +105,10 @@ describe('BoardsSelector', () => {
apolloProvider
:
fakeApollo
,
propsData
:
{
throttleDuration
,
},
attachTo
:
document
.
body
,
provide
:
{
fullPath
:
''
,
boardBaseUrl
:
`
${
TEST_HOST
}
/board/base/url`
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
...
...
@@ -112,10 +116,6 @@ describe('BoardsSelector', () => {
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
},
attachTo
:
document
.
body
,
provide
:
{
fullPath
:
''
,
},
});
};
...
...
spec/frontend/boards/components/issue_board_filtered_search_spec.js
View file @
b8c60584
...
...
@@ -14,10 +14,11 @@ describe('IssueBoardFilter', () => {
const
createComponent
=
({
isSignedIn
=
false
}
=
{})
=>
{
wrapper
=
shallowMount
(
IssueBoardFilteredSpec
,
{
propsData
:
{
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
},
provide
:
{
isSignedIn
,
releasesFetchPath
:
'
/releases
'
,
fullPath
:
'
gitlab-org
'
,
boardType
:
'
group
'
,
},
});
};
...
...
spec/helpers/boards_helper_spec.rb
View file @
b8c60584
...
...
@@ -102,6 +102,7 @@ RSpec.describe BoardsHelper do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:create_non_backlog_issues
,
project_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue
,
project_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
project
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board
,
project
).
and_return
(
false
)
end
it
'returns a board_lists_path as lists_endpoint'
do
...
...
@@ -129,12 +130,23 @@ RSpec.describe BoardsHelper do
it
'returns can_admin_list as false by default'
do
expect
(
helper
.
board_data
[
:can_admin_list
]).
to
eq
(
'false'
)
end
it
'returns can_admin_list as true when user can admin the board'
do
it
'returns can_admin_list as true when user can admin the board
lists
'
do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
project
).
and_return
(
true
)
expect
(
helper
.
board_data
[
:can_admin_list
]).
to
eq
(
'true'
)
end
end
context
'can_admin_board'
do
it
'returns can_admin_board as false by default'
do
expect
(
helper
.
board_data
[
:can_admin_board
]).
to
eq
(
'false'
)
end
it
'returns can_admin_board as true when user can admin the board'
do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board
,
project
).
and_return
(
true
)
expect
(
helper
.
board_data
[
:can_admin_board
]).
to
eq
(
'true'
)
end
end
end
context
'group board'
do
...
...
@@ -146,6 +158,7 @@ RSpec.describe BoardsHelper do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:create_non_backlog_issues
,
group_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue
,
group_board
).
and_return
(
true
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
base_group
).
and_return
(
false
)
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board
,
base_group
).
and_return
(
false
)
end
it
'returns correct path for base group'
do
...
...
@@ -165,7 +178,7 @@ RSpec.describe BoardsHelper do
it
'returns can_admin_list as false by default'
do
expect
(
helper
.
board_data
[
:can_admin_list
]).
to
eq
(
'false'
)
end
it
'returns can_admin_list as true when user can admin the board'
do
it
'returns can_admin_list as true when user can admin the board
lists
'
do
allow
(
helper
).
to
receive
(
:can?
).
with
(
user
,
:admin_issue_board_list
,
base_group
).
and_return
(
true
)
expect
(
helper
.
board_data
[
:can_admin_list
]).
to
eq
(
'true'
)
...
...
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