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
5495c584
Commit
5495c584
authored
Feb 07, 2020
by
Nicolò Maria Mezzopera
Committed by
Martin Wortschack
Feb 07, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update constants entries
- explicit sort - event tracking
parent
b5176790
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
374 additions
and
163 deletions
+374
-163
ee/app/assets/javascripts/packages/list/components/packages_list.vue
...ts/javascripts/packages/list/components/packages_list.vue
+66
-42
ee/app/assets/javascripts/packages/list/components/packages_list_app.vue
...avascripts/packages/list/components/packages_list_app.vue
+6
-1
ee/app/assets/javascripts/packages/list/constants.js
ee/app/assets/javascripts/packages/list/constants.js
+6
-1
ee/app/assets/javascripts/packages/list/stores/actions.js
ee/app/assets/javascripts/packages/list/stores/actions.js
+6
-2
ee/app/assets/javascripts/packages/list/stores/getters.js
ee/app/assets/javascripts/packages/list/stores/getters.js
+6
-0
ee/app/assets/javascripts/packages/list/stores/index.js
ee/app/assets/javascripts/packages/list/stores/index.js
+2
-0
ee/app/assets/javascripts/packages/list/stores/mutation_types.js
...assets/javascripts/packages/list/stores/mutation_types.js
+1
-0
ee/app/assets/javascripts/packages/list/stores/mutations.js
ee/app/assets/javascripts/packages/list/stores/mutations.js
+4
-0
ee/app/assets/javascripts/packages/list/stores/state.js
ee/app/assets/javascripts/packages/list/stores/state.js
+11
-0
ee/app/assets/javascripts/packages/shared/constants.js
ee/app/assets/javascripts/packages/shared/constants.js
+2
-0
ee/app/assets/javascripts/packages/shared/utils.js
ee/app/assets/javascripts/packages/shared/utils.js
+2
-1
ee/spec/frontend/packages/list/components/packages_list_app_spec.js
...ontend/packages/list/components/packages_list_app_spec.js
+67
-48
ee/spec/frontend/packages/list/components/packages_list_spec.js
...c/frontend/packages/list/components/packages_list_spec.js
+131
-62
ee/spec/frontend/packages/list/stores/actions_spec.js
ee/spec/frontend/packages/list/stores/actions_spec.js
+22
-5
ee/spec/frontend/packages/list/stores/getters_spec.js
ee/spec/frontend/packages/list/stores/getters_spec.js
+18
-0
ee/spec/frontend/packages/list/stores/mutations_spec.js
ee/spec/frontend/packages/list/stores/mutations_spec.js
+12
-0
ee/spec/frontend/packages/mock_data.js
ee/spec/frontend/packages/mock_data.js
+3
-0
ee/spec/frontend/packages/shared/utils_spec.js
ee/spec/frontend/packages/shared/utils_spec.js
+9
-1
No files found.
ee/app/assets/javascripts/packages/list/components/packages_list.vue
View file @
5495c584
<
script
>
import
{
mapState
}
from
'
vuex
'
;
import
{
GlTable
,
GlPagination
,
GlButton
,
GlSorting
,
GlSortingItem
,
GlModal
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
GlTable
,
GlPagination
,
GlButton
,
GlSorting
,
GlSortingItem
,
GlModal
,
GlLink
,
GlIcon
,
GlTooltipDirective
,
}
from
'
@gitlab/ui
'
;
import
Tracking
from
'
~/tracking
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
TimeAgoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
{
LIST_KEY_NAME
,
LIST_KEY_PROJECT
,
...
...
@@ -18,6 +27,9 @@ import {
LIST_LABEL_PACKAGE_TYPE
,
LIST_LABEL_CREATED_AT
,
LIST_LABEL_ACTIONS
,
LIST_ORDER_BY_PACKAGE_TYPE
,
ASCENDING_ODER
,
DESCENDING_ORDER
,
}
from
'
../constants
'
;
import
{
TrackingActions
}
from
'
../../shared/constants
'
;
import
{
packageTypeToTrackCategory
}
from
'
../../shared/utils
'
;
...
...
@@ -30,26 +42,29 @@ export default {
GlSorting
,
GlSortingItem
,
GlButton
,
GlLink
,
TimeAgoTooltip
,
GlModal
,
Icon
,
Gl
Icon
,
PackageTags
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
},
mixins
:
[
Tracking
.
mixin
()],
data
()
{
return
{
modalId
:
'
confirm-delete-pacakge
'
,
itemToBeDeleted
:
null
,
};
},
computed
:
{
...
mapState
({
list
:
'
packages
'
,
perPage
:
state
=>
state
.
pagination
.
perPage
,
totalItems
:
state
=>
state
.
pagination
.
total
,
page
:
state
=>
state
.
pagination
.
page
,
isGroupPage
:
state
=>
state
.
config
.
isGroupPage
,
orderBy
:
state
=>
state
.
sorting
.
orderBy
,
sort
:
state
=>
state
.
sorting
.
sort
,
}),
...
mapGetters
({
list
:
'
getList
'
}),
currentPage
:
{
get
()
{
return
this
.
page
;
...
...
@@ -58,19 +73,12 @@ export default {
this
.
$emit
(
'
page:changed
'
,
value
);
},
},
orderBy
()
{
return
'
name
'
;
},
sort
()
{
return
'
asc
'
;
},
// end of vuex placeholder
sortText
()
{
const
field
=
this
.
sortableFields
.
find
(
s
=>
s
.
ke
y
===
this
.
orderBy
);
const
field
=
this
.
sortableFields
.
find
(
s
=>
s
.
orderB
y
===
this
.
orderBy
);
return
field
?
field
.
label
:
''
;
},
isSortAscending
()
{
return
this
.
sort
===
'
asc
'
;
return
this
.
sort
===
ASCENDING_ODER
;
},
isListEmpty
()
{
return
!
this
.
list
||
this
.
list
.
length
===
0
;
...
...
@@ -84,26 +92,31 @@ export default {
{
key
:
LIST_KEY_NAME
,
label
:
LIST_LABEL_NAME
,
orderBy
:
LIST_KEY_NAME
,
class
:
[
'
text-left
'
],
},
{
key
:
LIST_KEY_PROJECT
,
label
:
LIST_LABEL_PROJECT
,
orderBy
:
LIST_KEY_PROJECT
,
class
:
[
'
text-center
'
],
},
{
key
:
LIST_KEY_VERSION
,
label
:
LIST_LABEL_VERSION
,
orderBy
:
LIST_KEY_VERSION
,
class
:
[
'
text-center
'
],
},
{
key
:
LIST_KEY_PACKAGE_TYPE
,
label
:
LIST_LABEL_PACKAGE_TYPE
,
orderBy
:
LIST_ORDER_BY_PACKAGE_TYPE
,
class
:
[
'
text-center
'
],
},
{
key
:
LIST_KEY_CREATED_AT
,
label
:
LIST_LABEL_CREATED_AT
,
orderBy
:
LIST_KEY_CREATED_AT
,
class
:
this
.
showActions
?
[
'
text-center
'
]
:
[
'
text-right
'
],
},
].
filter
(
f
=>
f
.
key
!==
LIST_KEY_PROJECT
||
this
.
isGroupPage
);
...
...
@@ -141,14 +154,19 @@ export default {
},
},
methods
:
{
...
mapActions
([
'
setSorting
'
]),
onDirectionChange
()
{
// to be connected to the sorting api when the api is ready
const
sort
=
this
.
isSortAscending
?
DESCENDING_ORDER
:
ASCENDING_ODER
;
this
.
setSorting
({
sort
});
this
.
$emit
(
'
sort:changed
'
);
},
onSortItemClick
()
{
// to be connected to the sorting api when the api is ready
onSortItemClick
(
item
)
{
this
.
setSorting
({
orderBy
:
item
});
this
.
$emit
(
'
sort:changed
'
);
},
setItemToBeDeleted
(
item
)
{
this
.
itemToBeDeleted
=
{
...
item
};
this
.
track
(
TrackingActions
.
REQUEST_DELETE_PACKAGE
);
this
.
$refs
.
packageListDeleteModal
.
show
();
},
deleteItemConfirmation
()
{
...
...
@@ -157,7 +175,7 @@ export default {
this
.
itemToBeDeleted
=
null
;
},
deleteItemCanceled
()
{
// this is going to be used to support ui tracking in the future
this
.
track
(
TrackingActions
.
CANCEL_DELETE_PACKAGE
);
this
.
itemToBeDeleted
=
null
;
},
},
...
...
@@ -169,7 +187,6 @@ export default {
<slot
v-if=
"isListEmpty"
name=
"empty-state"
></slot>
<template
v-else
>
<gl-sorting
ref=
"packageListSorting"
class=
"my-3"
:text=
"sortText"
:is-ascending=
"isSortAscending"
...
...
@@ -179,39 +196,46 @@ export default {
v-for=
"item in sortableFields"
ref=
"packageListSortItem"
:key=
"item.key"
@
click=
"onSortItemClick(item.
ke
y)"
@
click=
"onSortItemClick(item.
orderB
y)"
>
{{
item
.
label
}}
</gl-sorting-item>
</gl-sorting>
<gl-table
ref=
"packageListTable"
:items=
"list"
:fields=
"headerFields"
:no-local-sorting=
"true"
stacked=
"md"
>
<gl-table
:items=
"list"
:fields=
"headerFields"
:no-local-sorting=
"true"
stacked=
"md"
>
<template
#cell(name)=
"
{value, item}">
<div
class=
"flex-truncate-parent d-flex align-items-center justify-content-end justify-content-md-start"
>
<a
:href=
"item._links.web_path"
data-qa-selector=
"package_link"
>
<gl-link
v-gl-tooltip
.
hover
:title=
"value"
class=
"flex-truncate-child"
:href=
"item._links.web_path"
data-qa-selector=
"package_link"
>
{{
value
}}
</a>
<package-tags
v-if=
"item.tags && item.tags.length"
class=
"prepend-left-8"
:tags=
"item.tags"
hide-label
:tag-display-limit=
"1"
/>
</gl-link>
</div>
<package-tags
v-if=
"item.tags && item.tags.length"
class=
"prepend-left-8"
:tags=
"item.tags"
hide-label
:tag-display-limit=
"1"
/>
</
template
>
<
template
#cell(project
)=
"{value
}"
>
<
template
#cell(project
_path)=
"{item
}"
>
<div
ref=
"col-project"
class=
"flex-truncate-parent"
>
<a
:href=
"value"
class=
"flex-truncate-child"
>
{{
value
}}
</a>
<gl-link
v-gl-tooltip
.
hover
:title=
"item.projectPathName"
:href=
"item.project_path"
class=
"flex-truncate-child"
>
{{
item
.
projectPathName
}}
</gl-link>
</div>
</
template
>
<
template
#cell(version)=
"{value}"
>
...
...
@@ -224,6 +248,7 @@ export default {
<time-ago-tooltip
:time=
"value"
/>
</
template
>
<
template
#cell(actions)=
"{item}"
>
<!-- _links contains the urls needed to navigate to the page details and to perform a package deletion and it comes straight from the API -->
<gl-button
ref=
"action-delete"
variant=
"danger"
...
...
@@ -232,12 +257,11 @@ export default {
:disabled=
"!item._links.delete_api_path"
@
click=
"setItemToBeDeleted(item)"
>
<icon
name=
"remove"
/>
<
gl-
icon
name=
"remove"
/>
</gl-button>
</
template
>
</gl-table>
<gl-pagination
ref=
"packageListPagination"
v-model=
"currentPage"
:per-page=
"perPage"
:total-items=
"totalItems"
...
...
@@ -247,7 +271,7 @@ export default {
<gl-modal
ref=
"packageListDeleteModal"
:modal-id=
"modalId
"
modal-id=
"confirm-delete-pacakge
"
ok-variant=
"danger"
@
ok=
"deleteItemConfirmation"
@
cancel=
"deleteItemCanceled"
...
...
ee/app/assets/javascripts/packages/list/components/packages_list_app.vue
View file @
5495c584
...
...
@@ -47,7 +47,12 @@ export default {
<
template
>
<gl-loading-icon
v-if=
"isLoading"
class=
"mt-2"
/>
<package-list
v-else
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<package-list
v-else
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
@
sort:changed=
"requestPackagesList"
>
<template
#empty-state
>
<gl-empty-state
:title=
"s__('PackageRegistry|There are no packages yet')"
...
...
ee/app/assets/javascripts/packages/list/constants.js
View file @
5495c584
...
...
@@ -13,7 +13,7 @@ export const DEFAULT_PAGE_SIZE = 20;
export
const
GROUP_PAGE_TYPE
=
'
groups
'
;
export
const
LIST_KEY_NAME
=
'
name
'
;
export
const
LIST_KEY_PROJECT
=
'
project
'
;
export
const
LIST_KEY_PROJECT
=
'
project
_path
'
;
export
const
LIST_KEY_VERSION
=
'
version
'
;
export
const
LIST_KEY_PACKAGE_TYPE
=
'
package_type
'
;
export
const
LIST_KEY_CREATED_AT
=
'
created_at
'
;
...
...
@@ -26,5 +26,10 @@ export const LIST_LABEL_PACKAGE_TYPE = __('Type');
export
const
LIST_LABEL_CREATED_AT
=
__
(
'
Created
'
);
export
const
LIST_LABEL_ACTIONS
=
''
;
export
const
LIST_ORDER_BY_PACKAGE_TYPE
=
'
type
'
;
export
const
ASCENDING_ODER
=
'
asc
'
;
export
const
DESCENDING_ORDER
=
'
desc
'
;
// The following is not translated because it is used to build a JavaScript exception error message
export
const
MISSING_DELETE_PATH_ERROR
=
'
Missing delete_api_path link
'
;
ee/app/assets/javascripts/packages/list/stores/actions.js
View file @
5495c584
...
...
@@ -13,6 +13,7 @@ import {
export
const
setInitialState
=
({
commit
},
data
)
=>
commit
(
types
.
SET_INITIAL_STATE
,
data
);
export
const
setLoading
=
({
commit
},
data
)
=>
commit
(
types
.
SET_MAIN_LOADING
,
data
);
export
const
setSorting
=
({
commit
},
data
)
=>
commit
(
types
.
SET_SORTING
,
data
);
export
const
receivePackagesListSuccess
=
({
commit
},
{
data
,
headers
})
=>
{
commit
(
types
.
SET_PACKAGE_LIST_SUCCESS
,
data
);
...
...
@@ -22,9 +23,12 @@ export const receivePackagesListSuccess = ({ commit }, { data, headers }) => {
export
const
requestPackagesList
=
({
dispatch
,
state
},
pagination
=
{})
=>
{
dispatch
(
'
setLoading
'
,
true
);
const
{
page
=
DEFAULT_PAGE
,
perPage
=
DEFAULT_PAGE_SIZE
}
=
pagination
;
const
{
page
=
DEFAULT_PAGE
,
per_page
=
DEFAULT_PAGE_SIZE
}
=
pagination
;
const
{
sort
,
orderBy
}
=
state
.
sorting
;
const
apiMethod
=
state
.
config
.
isGroupPage
?
'
groupPackages
'
:
'
projectPackages
'
;
return
Api
[
apiMethod
](
state
.
config
.
resourceId
,
{
params
:
{
page
,
per_page
:
perPage
}
})
return
Api
[
apiMethod
](
state
.
config
.
resourceId
,
{
params
:
{
page
,
per_page
,
sort
,
order_by
:
orderBy
},
})
.
then
(({
data
,
headers
})
=>
{
dispatch
(
'
receivePackagesListSuccess
'
,
{
data
,
headers
});
})
...
...
ee/app/assets/javascripts/packages/list/stores/getters.js
0 → 100644
View file @
5495c584
import
{
LIST_KEY_PROJECT
}
from
'
../constants
'
;
import
{
beautifyPath
}
from
'
../../shared/utils
'
;
// eslint-disable-next-line import/prefer-default-export
export
const
getList
=
state
=>
state
.
packages
.
map
(
p
=>
({
...
p
,
projectPathName
:
beautifyPath
(
p
[
LIST_KEY_PROJECT
])
}));
ee/app/assets/javascripts/packages/list/stores/index.js
View file @
5495c584
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
*
as
actions
from
'
./actions
'
;
import
*
as
getters
from
'
./getters
'
;
import
mutations
from
'
./mutations
'
;
import
state
from
'
./state
'
;
...
...
@@ -9,6 +10,7 @@ Vue.use(Vuex);
export
const
createStore
=
()
=>
new
Vuex
.
Store
({
state
,
getters
,
actions
,
mutations
,
});
...
...
ee/app/assets/javascripts/packages/list/stores/mutation_types.js
View file @
5495c584
...
...
@@ -3,3 +3,4 @@ export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
export
const
SET_PACKAGE_LIST_SUCCESS
=
'
SET_PACKAGE_LIST_SUCCESS
'
;
export
const
SET_PAGINATION
=
'
SET_PAGINATION
'
;
export
const
SET_MAIN_LOADING
=
'
SET_MAIN_LOADING
'
;
export
const
SET_SORTING
=
'
SET_SORTING
'
;
ee/app/assets/javascripts/packages/list/stores/mutations.js
View file @
5495c584
...
...
@@ -22,4 +22,8 @@ export default {
const
normalizedHeaders
=
normalizeHeaders
(
headers
);
state
.
pagination
=
parseIntPagination
(
normalizedHeaders
);
},
[
types
.
SET_SORTING
](
state
,
sorting
)
{
state
.
sorting
=
{
...
state
.
sorting
,
...
sorting
};
},
};
ee/app/assets/javascripts/packages/list/stores/state.js
View file @
5495c584
...
...
@@ -32,4 +32,15 @@ export default () => ({
* }
*/
pagination
:
{},
/**
* Sorting object has the following structure:
* {
* sort: String,
* orderBy: String
* }
*/
sorting
:
{
sort
:
'
desc
'
,
orderBy
:
'
version
'
,
},
});
ee/app/assets/javascripts/packages/shared/constants.js
View file @
5495c584
...
...
@@ -7,6 +7,8 @@ export const PackageType = {
export
const
TrackingActions
=
{
DELETE_PACKAGE
:
'
delete_package
'
,
REQUEST_DELETE_PACKAGE
:
'
request_delete_package
'
,
CANCEL_DELETE_PACKAGE
:
'
cancel_delete_package
'
,
PULL_PACKAGE
:
'
pull_package
'
,
};
...
...
ee/app/assets/javascripts/packages/shared/utils.js
View file @
5495c584
import
{
TrackingCategories
}
from
'
./constants
'
;
// eslint-disable-next-line import/prefer-default-export
export
const
packageTypeToTrackCategory
=
type
=>
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
`UI::
${
TrackingCategories
[
type
]}
`
;
export
const
beautifyPath
=
path
=>
(
path
?
path
.
split
(
'
/
'
).
join
(
'
/
'
)
:
''
);
ee/spec/frontend/packages/list/components/packages_list_app_spec.js
View file @
5495c584
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
PackageListApp
from
'
ee/packages/list/components/packages_list_app.vue
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
packages_list_app
'
,
()
=>
{
let
wrapper
;
let
store
;
const
PackageList
=
{
name
:
'
package-list
'
,
template
:
'
<div><slot name="empty-state"></slot></div>
'
,
};
const
GlLoadingIcon
=
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
};
const
emptyListHelpUrl
=
'
helpUrl
'
;
const
findListComponent
=
()
=>
wrapper
.
find
({
name
:
'
package-list
'
});
const
findLoadingComponent
=
()
=>
wrapper
.
find
({
name
:
'
gl-loading-icon
'
});
const
componentConfig
=
{
stubs
:
{
GlEmptyState
,
'
package-list
'
:
{
name
:
'
package-list
'
,
template
:
'
<div><slot name="empty-state"></slot></div>
'
,
const
findListComponent
=
()
=>
wrapper
.
find
(
PackageList
);
const
findLoadingComponent
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
mountComponent
=
()
=>
{
wrapper
=
shallowMount
(
PackageListApp
,
{
localVue
,
store
,
stubs
:
{
GlEmptyState
,
GlLoadingIcon
,
PackageList
,
},
'
gl-loading-icon
'
:
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
},
},
computed
:
{
isLoading
:
()
=>
false
,
emptyListIllustration
:
()
=>
'
helpSvg
'
,
emptyListHelpUrl
:
()
=>
emptyListHelpUrl
,
resourceId
:
()
=>
'
project_id
'
,
},
methods
:
{
requestPackagesList
:
jest
.
fn
(),
requestDeletePackage
:
jest
.
fn
(),
setProjectId
:
jest
.
fn
(),
setGroupId
:
jest
.
fn
(),
setUserCanDelete
:
jest
.
fn
(),
},
});
};
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
PackageListApp
,
componentConfig
);
store
=
new
Vuex
.
Store
({
state
:
{
isLoading
:
false
,
config
:
{
resourceId
:
'
project_id
'
,
emptyListIllustration
:
'
helpSvg
'
,
emptyListHelpUrl
:
'
helpUrl
'
,
},
},
});
store
.
dispatch
=
jest
.
fn
();
});
afterEach
(()
=>
{
...
...
@@ -42,42 +51,52 @@ describe('packages_list_app', () => {
});
it
(
'
renders
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
describe
(
'
when isLoading is true
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
shallowMount
(
PackageListApp
,
{
...
componentConfig
,
computed
:
{
isLoading
:
()
=>
true
,
},
});
store
.
state
.
isLoading
=
true
;
mountComponent
();
});
it
(
'
shows the loading component
'
,
()
=>
{
const
loader
=
findLoadingComponent
();
expect
(
loader
.
exists
()).
toBe
(
true
);
});
});
it
(
'
generate the correct empty list link
'
,
()
=>
{
const
emptyState
=
findListComponent
();
const
link
=
emptyState
.
find
(
'
a
'
);
describe
(
'
when isLoading is false
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
expect
(
link
.
html
()).
toMatchInlineSnapshot
(
`"<a href=\\"
${
emptyListHelpUrl
}
\\" target=\\"_blank\\">publish and share your packages</a>"`
,
);
});
it
(
'
generate the correct empty list link
'
,
()
=>
{
const
emptyState
=
findListComponent
();
const
link
=
emptyState
.
find
(
'
a
'
);
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
expect
(
componentConfig
.
methods
.
requestPackagesList
).
toHaveBeenCalledWith
({
page
:
1
});
});
expect
(
link
.
html
()).
toMatchInlineSnapshot
(
`"<a href=\\"
${
emptyListHelpUrl
}
\\" target=\\"_blank\\">publish and share your packages</a>"`
,
);
});
it
(
'
call requestDeletePackage on package:delete
'
,
()
=>
{
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
package:delete
'
,
'
foo
'
);
expect
(
componentConfig
.
methods
.
requestDeletePackage
).
toHaveBeenCalledWith
(
'
foo
'
);
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
,
{
page
:
1
});
});
it
(
'
call requestDeletePackage on package:delete
'
,
()
=>
{
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
package:delete
'
,
'
foo
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestDeletePackage
'
,
'
foo
'
);
});
it
(
'
calls requestPackagesList on sort:changed
'
,
()
=>
{
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
sort:changed
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
);
});
});
});
ee/spec/frontend/packages/list/components/packages_list_spec.js
View file @
5495c584
import
Vue
from
'
vue
'
;
import
_
from
'
underscore
'
;
import
Vuex
from
'
vuex
'
;
import
{
last
}
from
'
lodash
'
;
import
{
GlTable
,
GlSorting
,
GlPagination
,
GlModal
}
from
'
@gitlab/ui
'
;
import
Tracking
from
'
~/tracking
'
;
import
{
mount
}
from
'
@vue/test-utils
'
;
import
{
mount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
PackagesList
from
'
ee/packages/list/components/packages_list.vue
'
;
import
PackageTags
from
'
ee/packages/shared/components/package_tags.vue
'
;
import
*
as
SharedUtils
from
'
ee/packages/shared/utils
'
;
...
...
@@ -9,55 +10,76 @@ import { TrackingActions } from 'ee/packages/shared/constants';
import
stubChildren
from
'
helpers/stub_children
'
;
import
{
packageList
}
from
'
../../mock_data
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
packages_list
'
,
()
=>
{
let
wrapper
;
let
store
;
let
state
;
let
getListSpy
;
const
GlSortingItem
=
{
name
:
'
sorting-item-stub
'
,
template
:
'
<div><slot></slot></div>
'
};
const
EmptySlotStub
=
{
name
:
'
empty-slot-stub
'
,
template
:
'
<div>bar</div>
'
};
const
findFirstActionColumn
=
()
=>
wrapper
.
find
({
ref
:
'
action-delete
'
});
const
findPackageListTable
=
()
=>
wrapper
.
find
(
{
ref
:
'
packageListTable
'
}
);
const
findPackageListSorting
=
()
=>
wrapper
.
find
(
{
ref
:
'
packageListSorting
'
}
);
const
findPackageListPagination
=
()
=>
wrapper
.
find
(
{
ref
:
'
packageListPagination
'
}
);
const
findPackageListDeleteModal
=
()
=>
wrapper
.
find
(
{
ref
:
'
packageListDeleteModal
'
}
);
const
findSortingItems
=
()
=>
wrapper
.
findAll
(
{
name
:
'
sorting-item-stub
'
}
);
const
findPackageListTable
=
()
=>
wrapper
.
find
(
GlTable
);
const
findPackageListSorting
=
()
=>
wrapper
.
find
(
GlSorting
);
const
findPackageListPagination
=
()
=>
wrapper
.
find
(
GlPagination
);
const
findPackageListDeleteModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
findSortingItems
=
()
=>
wrapper
.
findAll
(
GlSortingItem
);
const
findFirstProjectColumn
=
()
=>
wrapper
.
find
({
ref
:
'
col-project
'
});
const
findPackageTags
=
()
=>
wrapper
.
findAll
(
PackageTags
);
const
findEmptySlot
=
()
=>
wrapper
.
find
({
name
:
'
empty-slot-stub
'
});
const
mountOptions
=
{
stubs
:
{
...
stubChildren
(
PackagesList
),
GlTable
:
false
,
GlSortingItem
:
{
name
:
'
sorting-item-stub
'
,
template
:
'
<div><slot></slot></div>
'
},
},
computed
:
{
list
:
()
=>
[...
packageList
],
perPage
:
()
=>
1
,
totalItems
:
()
=>
1
,
page
:
()
=>
1
,
canDestroyPackage
:
()
=>
true
,
isGroupPage
:
()
=>
false
,
},
const
mountComponent
=
options
=>
{
wrapper
=
mount
(
PackagesList
,
{
localVue
,
store
,
stubs
:
{
...
stubChildren
(
PackagesList
),
GlTable
,
GlSortingItem
,
},
...
options
,
});
};
beforeEach
(()
=>
{
// This is needed due to console.error called by vue to emit a warning that stop the tests
// see https://github.com/vuejs/vue-test-utils/issues/532
Vue
.
config
.
silent
=
true
;
wrapper
=
mount
(
PackagesList
,
mountOptions
);
getListSpy
=
jest
.
fn
();
getListSpy
.
mockReturnValue
(
packageList
);
state
=
{
packages
:
[...
packageList
],
pagination
:
{
perPage
:
1
,
total
:
1
,
page
:
1
,
},
config
:
{
isGroupPage
:
false
,
},
sorting
:
{
orderBy
:
'
version
'
,
sort
:
'
desc
'
,
},
};
store
=
new
Vuex
.
Store
({
state
,
getters
:
{
getList
:
getListSpy
,
},
});
store
.
dispatch
=
jest
.
fn
();
});
afterEach
(()
=>
{
Vue
.
config
.
silent
=
false
;
wrapper
.
destroy
();
});
describe
(
'
when is isGroupPage
'
,
()
=>
{
beforeEach
(()
=>
{
wrapper
=
mount
(
PackagesList
,
{
...
mountOptions
,
computed
:
{
...
mountOptions
.
computed
,
isGroupPage
:
()
=>
true
,
},
});
state
.
config
.
isGroupPage
=
true
;
mountComponent
();
});
it
(
'
has project field
'
,
()
=>
{
...
...
@@ -71,31 +93,41 @@ describe('packages_list', () => {
});
});
it
(
'
contains a sorting componen
t
'
,
()
=>
{
const
sorting
=
findPackageListSorting
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
describe
(
'
layou
t
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
(
);
});
it
(
'
contains a table
component
'
,
()
=>
{
const
sorting
=
findPackageListTable
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a sorting
component
'
,
()
=>
{
const
sorting
=
findPackageListSorting
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a pagination
component
'
,
()
=>
{
const
sorting
=
findPackageListPagination
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a table
component
'
,
()
=>
{
const
sorting
=
findPackageListTable
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a modal
component
'
,
()
=>
{
const
sorting
=
findPackageListDeleteModal
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a pagination
component
'
,
()
=>
{
const
sorting
=
findPackageListPagination
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
renders package tags when a package has tags
'
,
()
=>
{
expect
(
findPackageTags
()).
toHaveLength
(
1
);
it
(
'
contains a modal component
'
,
()
=>
{
const
sorting
=
findPackageListDeleteModal
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
renders package tags when a package has tags
'
,
()
=>
{
expect
(
findPackageTags
()).
toHaveLength
(
1
);
});
});
describe
(
'
when the user can destroy the package
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
show the action column
'
,
()
=>
{
const
action
=
findFirstActionColumn
();
expect
(
action
.
exists
()).
toBe
(
true
);
...
...
@@ -112,10 +144,10 @@ describe('packages_list', () => {
it
(
'
delete button set itemToBeDeleted and open the modal
'
,
()
=>
{
wrapper
.
vm
.
$refs
.
packageListDeleteModal
.
show
=
jest
.
fn
();
const
item
=
_
.
last
(
packageL
ist
);
const
item
=
last
(
wrapper
.
vm
.
l
ist
);
const
action
=
findFirstActionColumn
();
action
.
vm
.
$emit
(
'
click
'
);
return
Vue
.
nextTick
().
then
(()
=>
{
return
wrapper
.
vm
.
$
nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
item
);
expect
(
wrapper
.
vm
.
$refs
.
packageListDeleteModal
.
show
).
toHaveBeenCalled
();
});
...
...
@@ -144,14 +176,11 @@ describe('packages_list', () => {
});
describe
(
'
when the list is empty
'
,
()
=>
{
const
findEmptySlot
=
()
=>
wrapper
.
find
({
name
:
'
empty-slot-stub
'
});
beforeEach
(()
=>
{
wrapper
=
mount
(
PackagesList
,
{
...
mountOptions
,
computed
:
{
list
:
()
=>
[]
},
getListSpy
.
mockReturnValue
([]);
mountComponent
({
slots
:
{
'
empty-state
'
:
{
name
:
'
empty-slot-stub
'
,
template
:
'
<div>bar</div>
'
}
,
'
empty-state
'
:
EmptySlotStub
,
},
});
});
...
...
@@ -165,17 +194,56 @@ describe('packages_list', () => {
});
describe
(
'
sorting component
'
,
()
=>
{
let
sorting
;
let
sortingItems
;
beforeEach
(()
=>
{
mountComponent
();
sorting
=
findPackageListSorting
();
sortingItems
=
findSortingItems
();
});
it
(
'
has all the sortable items
'
,
()
=>
{
const
sortingItems
=
findSortingItems
();
expect
(
sortingItems
.
length
).
toEqual
(
wrapper
.
vm
.
sortableFields
.
length
);
});
it
(
'
on sort change set sorting in vuex and emit event
'
,
()
=>
{
sorting
.
vm
.
$emit
(
'
sortDirectionChange
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
{
sort
:
'
asc
'
});
expect
(
wrapper
.
emitted
(
'
sort:changed
'
)).
toBeTruthy
();
});
it
(
'
on sort item click set sorting and emit event
'
,
()
=>
{
const
item
=
sortingItems
.
at
(
0
);
const
{
orderBy
}
=
wrapper
.
vm
.
sortableFields
[
0
];
item
.
vm
.
$emit
(
'
click
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
{
orderBy
});
expect
(
wrapper
.
emitted
(
'
sort:changed
'
)).
toBeTruthy
();
});
});
describe
(
'
pagination component
'
,
()
=>
{
let
pagination
;
let
modelEvent
;
beforeEach
(()
=>
{
mountComponent
();
pagination
=
findPackageListPagination
();
// retrieve the event used by v-model, a more sturdy approach than hardcoding it
modelEvent
=
pagination
.
vm
.
$options
.
model
.
event
;
});
it
(
'
emits page:changed events when the page changes
'
,
()
=>
{
wrapper
.
vm
.
currentPage
=
2
;
pagination
.
vm
.
$emit
(
modelEvent
,
2
)
;
expect
(
wrapper
.
emitted
(
'
page:changed
'
)).
toEqual
([[
2
]]);
});
});
describe
(
'
table component
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
has stacked-md class
'
,
()
=>
{
const
table
=
findPackageListTable
();
expect
(
table
.
classes
()).
toContain
(
'
b-table-stacked-md
'
);
...
...
@@ -188,6 +256,7 @@ describe('packages_list', () => {
const
category
=
'
foo
'
;
beforeEach
(()
=>
{
mountComponent
();
eventSpy
=
jest
.
spyOn
(
Tracking
,
'
event
'
);
utilSpy
=
jest
.
spyOn
(
SharedUtils
,
'
packageTypeToTrackCategory
'
).
mockReturnValue
(
category
);
wrapper
.
setData
({
itemToBeDeleted
:
{
package_type
:
'
conan
'
}
});
...
...
ee/spec/frontend/packages/list/stores/actions_spec.js
View file @
5495c584
...
...
@@ -28,11 +28,15 @@ describe('Actions Package list store', () => {
});
describe
(
'
requestPackagesList
'
,
()
=>
{
const
sorting
=
{
sort
:
'
asc
'
,
orderBy
:
'
version
'
,
};
it
(
'
should fetch the project packages list when isGroupPage is false
'
,
done
=>
{
testAction
(
actions
.
requestPackagesList
,
undefined
,
{
config
:
{
isGroupPage
:
false
,
resourceId
:
1
}
},
{
config
:
{
isGroupPage
:
false
,
resourceId
:
1
}
,
sorting
},
[],
[
{
type
:
'
setLoading
'
,
payload
:
true
},
...
...
@@ -41,7 +45,7 @@ describe('Actions Package list store', () => {
],
()
=>
{
expect
(
Api
.
projectPackages
).
toHaveBeenCalledWith
(
1
,
{
params
:
{
page
:
1
,
per_page
:
20
},
params
:
{
page
:
1
,
per_page
:
20
,
sort
:
sorting
.
sort
,
order_by
:
sorting
.
orderBy
},
});
done
();
},
...
...
@@ -52,7 +56,7 @@ describe('Actions Package list store', () => {
testAction
(
actions
.
requestPackagesList
,
undefined
,
{
config
:
{
isGroupPage
:
true
,
resourceId
:
2
}
},
{
config
:
{
isGroupPage
:
true
,
resourceId
:
2
}
,
sorting
},
[],
[
{
type
:
'
setLoading
'
,
payload
:
true
},
...
...
@@ -61,7 +65,7 @@ describe('Actions Package list store', () => {
],
()
=>
{
expect
(
Api
.
groupPackages
).
toHaveBeenCalledWith
(
2
,
{
params
:
{
page
:
1
,
per_page
:
20
},
params
:
{
page
:
1
,
per_page
:
20
,
sort
:
sorting
.
sort
,
order_by
:
sorting
.
orderBy
},
});
done
();
},
...
...
@@ -73,7 +77,7 @@ describe('Actions Package list store', () => {
testAction
(
actions
.
requestPackagesList
,
undefined
,
{
config
:
{
isGroupPage
:
false
,
resourceId
:
2
}
},
{
config
:
{
isGroupPage
:
false
,
resourceId
:
2
}
,
sorting
},
[],
[{
type
:
'
setLoading
'
,
payload
:
true
},
{
type
:
'
setLoading
'
,
payload
:
false
}],
()
=>
{
...
...
@@ -179,4 +183,17 @@ describe('Actions Package list store', () => {
});
});
});
describe('setSorting', () => {
it('should commit SET_SORTING', done => {
testAction(
actions.setSorting,
'foo',
null,
[{ type: types.SET_SORTING, payload: 'foo' }],
[],
done,
);
});
});
});
ee/spec/frontend/packages/list/stores/getters_spec.js
0 → 100644
View file @
5495c584
import
*
as
getters
from
'
ee/packages/list/stores/getters
'
;
import
{
packageList
}
from
'
../../mock_data
'
;
describe
(
'
Getters registry list store
'
,
()
=>
{
const
state
=
{
packages
:
packageList
,
};
describe
(
'
getList
'
,
()
=>
{
const
result
=
getters
.
getList
(
state
);
it
(
'
returns a list of packages
'
,
()
=>
{
expect
(
result
).
toHaveLength
(
packageList
.
length
);
expect
(
result
[
0
].
name
).
toBe
(
'
Test package
'
);
});
it
(
'
adds projectPathName
'
,
()
=>
{
expect
(
result
[
0
].
projectPathName
).
toMatchInlineSnapshot
(
`"foo / bar / baz"`
);
});
});
});
ee/spec/frontend/packages/list/stores/mutations_spec.js
View file @
5495c584
...
...
@@ -65,4 +65,16 @@ describe('Mutations Registry Store', () => {
expect
(
mockState
.
pagination
).
toEqual
(
mockPagination
);
});
});
describe
(
'
SET_SORTING
'
,
()
=>
{
it
(
'
should merge the sorting object with sort value
'
,
()
=>
{
mutations
[
types
.
SET_SORTING
](
mockState
,
{
sort
:
'
desc
'
});
expect
(
mockState
.
sorting
).
toEqual
({
...
mockState
.
sorting
,
sort
:
'
desc
'
});
});
it
(
'
should merge the sorting object with order_by value
'
,
()
=>
{
mutations
[
types
.
SET_SORTING
](
mockState
,
{
orderBy
:
'
foo
'
});
expect
(
mockState
.
sorting
).
toEqual
({
...
mockState
.
sorting
,
orderBy
:
'
foo
'
});
});
});
});
ee/spec/frontend/packages/mock_data.js
View file @
5495c584
...
...
@@ -13,6 +13,7 @@ export const mavenPackage = {
},
name
:
'
Test package
'
,
package_type
:
'
maven
'
,
project_path
:
'
foo/bar/baz
'
,
project_id
:
1
,
updated_at
:
'
2015-12-10
'
,
version
:
'
1.0.0
'
,
...
...
@@ -41,6 +42,7 @@ export const npmPackage = {
id
:
2
,
name
:
'
@Test/package
'
,
package_type
:
'
npm
'
,
project_path
:
'
foo/bar/baz
'
,
project_id
:
1
,
updated_at
:
'
2015-12-10
'
,
version
:
''
,
...
...
@@ -68,6 +70,7 @@ export const conanPackage = {
created_at
:
'
2015-12-10
'
,
id
:
3
,
name
:
'
conan-package
'
,
project_path
:
'
foo/bar/baz
'
,
package_files
:
[],
package_type
:
'
conan
'
,
project_id
:
1
,
...
...
ee/spec/frontend/packages/shared/utils_spec.js
View file @
5495c584
import
{
packageTypeToTrackCategory
}
from
'
ee/packages/shared/utils
'
;
import
{
packageTypeToTrackCategory
,
beautifyPath
}
from
'
ee/packages/shared/utils
'
;
import
{
PackageType
,
TrackingCategories
}
from
'
ee/packages/shared/constants
'
;
describe
(
'
Packages shared utils
'
,
()
=>
{
...
...
@@ -14,4 +14,12 @@ describe('Packages shared utils', () => {
);
});
});
describe
(
'
beautifyPath
'
,
()
=>
{
it
(
'
returns a string with spaces around /
'
,
()
=>
{
expect
(
beautifyPath
(
'
foo/bar
'
)).
toBe
(
'
foo / bar
'
);
});
it
(
'
does not fail for empty string
'
,
()
=>
{
expect
(
beautifyPath
()).
toBe
(
''
);
});
});
});
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