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
7595ec3c
Commit
7595ec3c
authored
Oct 04, 2021
by
Nicolò Maria Mezzopera
Committed by
Natalia Tepluhina
Oct 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Connect list app to apollo, enable search
parent
ec74935e
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
423 additions
and
113 deletions
+423
-113
app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue
...s_and_registries/package_registry/components/list/app.vue
+53
-30
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
...tries/package_registry/components/list/package_search.vue
+68
-13
app/assets/javascripts/packages_and_registries/package_registry/constants.js
...pts/packages_and_registries/package_registry/constants.js
+4
-0
app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql
..._registry/graphql/fragments/package_data.fragment.graphql
+4
-0
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
...ckage_registry/graphql/queries/get_packages.query.graphql
+27
-0
app/assets/javascripts/packages_and_registries/package_registry/pages/list.js
...ts/packages_and_registries/package_registry/pages/list.js
+5
-0
app/assets/javascripts/packages_and_registries/shared/constants.js
...s/javascripts/packages_and_registries/shared/constants.js
+1
-0
app/helpers/packages_helper.rb
app/helpers/packages_helper.rb
+1
-0
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/app_spec.js.snap
...e_registry/components/list/__snapshots__/app_spec.js.snap
+4
-2
spec/frontend/packages_and_registries/package_registry/components/list/app_spec.js
...d_registries/package_registry/components/list/app_spec.js
+111
-14
spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
.../package_registry/components/list/packages_search_spec.js
+91
-54
spec/frontend/packages_and_registries/package_registry/mock_data.js
...end/packages_and_registries/package_registry/mock_data.js
+24
-0
spec/helpers/packages_helper_spec.rb
spec/helpers/packages_helper_spec.rb
+30
-0
No files found.
app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue
View file @
7595ec3c
...
@@ -10,10 +10,14 @@ import { historyReplaceState } from '~/lib/utils/common_utils';
...
@@ -10,10 +10,14 @@ import { historyReplaceState } from '~/lib/utils/common_utils';
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
~/packages/list/constants
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
~/packages/list/constants
'
;
import
{
SHOW_DELETE_SUCCESS_ALERT
}
from
'
~/packages/shared/constants
'
;
import
{
SHOW_DELETE_SUCCESS_ALERT
}
from
'
~/packages/shared/constants
'
;
import
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
getPackagesQuery
from
'
~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
'
;
import
{
getQueryParams
,
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
import
{
PROJECT_RESOURCE_TYPE
,
GROUP_RESOURCE_TYPE
,
LIST_QUERY_DEBOUNCE_TIME
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
PackageTitle
from
'
./package_title.vue
'
;
import
PackageTitle
from
'
./package_title.vue
'
;
//
import PackageSearch from './package_search.vue';
import
PackageSearch
from
'
./package_search.vue
'
;
// import PackageList from './packages_list.vue';
// import PackageList from './packages_list.vue';
export
default
{
export
default
{
...
@@ -23,28 +27,53 @@ export default {
...
@@ -23,28 +27,53 @@ export default {
// GlSprintf,
// GlSprintf,
// PackageList,
// PackageList,
PackageTitle
,
PackageTitle
,
//
PackageSearch,
PackageSearch
,
},
},
inject
:
[
'
packageHelpUrl
'
,
'
emptyListIllustration
'
,
'
emptyListHelpUrl
'
],
inject
:
[
'
packageHelpUrl
'
,
'
emptyListIllustration
'
,
'
emptyListHelpUrl
'
,
'
isGroupPage
'
,
'
fullPath
'
,
],
data
()
{
data
()
{
return
{
return
{
filter
:
[],
packages
:
{},
sorting
:
{
sort
:
''
,
sort
:
'
desc
'
,
filters
:
{},
orderBy
:
'
created_at
'
,
},
selectedType
:
''
,
pagination
:
{},
};
};
},
},
apollo
:
{
packages
:
{
query
:
getPackagesQuery
,
variables
()
{
return
this
.
queryVariables
;
},
update
(
data
)
{
return
data
[
this
.
graphqlResource
].
packages
;
},
debounce
:
LIST_QUERY_DEBOUNCE_TIME
,
},
},
computed
:
{
computed
:
{
queryVariables
()
{
return
{
isGroupPage
:
this
.
isGroupPage
,
fullPath
:
this
.
fullPath
,
sort
:
this
.
isGroupPage
?
undefined
:
this
.
sort
,
groupSort
:
this
.
isGroupPage
?
this
.
sort
:
undefined
,
packageName
:
this
.
filters
?.
packageName
,
packageType
:
this
.
filters
?.
packageType
,
};
},
graphqlResource
()
{
return
this
.
isGroupPage
?
GROUP_RESOURCE_TYPE
:
PROJECT_RESOURCE_TYPE
;
},
packagesCount
()
{
packagesCount
()
{
return
0
;
return
this
.
packages
?.
count
;
},
},
emptySearch
()
{
hasFilters
()
{
return
(
return
this
.
filters
.
packageName
&&
this
.
filters
.
packageType
;
this
.
filter
.
filter
((
f
)
=>
f
.
type
!==
FILTERED_SEARCH_TERM
||
f
.
value
?.
data
).
length
===
0
);
},
},
emptyStateTitle
()
{
emptyStateTitle
()
{
return
this
.
emptySearch
return
this
.
emptySearch
...
@@ -53,19 +82,9 @@ export default {
...
@@ -53,19 +82,9 @@ export default {
},
},
},
},
mounted
()
{
mounted
()
{
const
queryParams
=
getQueryParams
(
window
.
document
.
location
.
search
);
const
{
sorting
,
filters
}
=
extractFilterAndSorting
(
queryParams
);
this
.
sorting
=
{
...
sorting
};
this
.
filter
=
[...
filters
];
this
.
checkDeleteAlert
();
this
.
checkDeleteAlert
();
},
},
methods
:
{
methods
:
{
onPageChanged
(
page
)
{
return
this
.
requestPackagesList
({
page
});
},
onPackageDeleteRequest
(
item
)
{
return
this
.
requestDeletePackage
(
item
);
},
checkDeleteAlert
()
{
checkDeleteAlert
()
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
const
showAlert
=
urlParams
.
get
(
SHOW_DELETE_SUCCESS_ALERT
);
const
showAlert
=
urlParams
.
get
(
SHOW_DELETE_SUCCESS_ALERT
);
...
@@ -76,6 +95,10 @@ export default {
...
@@ -76,6 +95,10 @@ export default {
historyReplaceState
(
cleanUrl
);
historyReplaceState
(
cleanUrl
);
}
}
},
},
handleSearchUpdate
({
sort
,
filters
})
{
this
.
sort
=
sort
;
this
.
filters
=
{
...
filters
};
},
},
},
i18n
:
{
i18n
:
{
widenFilters
:
s__
(
'
PackageRegistry|To widen your search, change or remove the filters above.
'
),
widenFilters
:
s__
(
'
PackageRegistry|To widen your search, change or remove the filters above.
'
),
...
@@ -91,13 +114,13 @@ export default {
...
@@ -91,13 +114,13 @@ export default {
<
template
>
<
template
>
<div>
<div>
<package-title
:help-url=
"packageHelpUrl"
:count=
"packagesCount"
/>
<package-title
:help-url=
"packageHelpUrl"
:count=
"packagesCount"
/>
<
!--
<package-search
@
update=
"requestPackagesList
"
/>
<
package-search
@
update=
"handleSearchUpdate
"
/>
<package-list
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<
!--
<
package-list
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<template
#empty-state
>
<template
#empty-state
>
<gl-empty-state
:title=
"emptyStateTitle"
:svg-path=
"emptyListIllustration"
>
<gl-empty-state
:title=
"emptyStateTitle"
:svg-path=
"emptyListIllustration"
>
<template
#description
>
<template
#description
>
<gl-sprintf
v-if=
"
!emptySearch
"
:message=
"$options.i18n.widenFilters"
/>
<gl-sprintf
v-if=
"
hasFilters
"
:message=
"$options.i18n.widenFilters"
/>
<gl-sprintf
v-else
:message=
"$options.i18n.noResultsText"
>
<gl-sprintf
v-else
:message=
"$options.i18n.noResultsText"
>
<template
#noPackagesLink
="
{ content }">
<template
#noPackagesLink
="
{ content }">
<gl-link
:href=
"emptyListHelpUrl"
target=
"_blank"
>
{{
content
}}
</gl-link>
<gl-link
:href=
"emptyListHelpUrl"
target=
"_blank"
>
{{
content
}}
</gl-link>
...
...
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
View file @
7595ec3c
<
script
>
<
script
>
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
import
{
OPERATOR_IS_ONLY
}
from
'
~/vue_shared/components/filtered_search_bar/constants
'
;
import
{
OPERATOR_IS_ONLY
}
from
'
~/vue_shared/components/filtered_search_bar/constants
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
import
{
getQueryParams
,
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
import
{
FILTERED_SEARCH_TERM
,
FILTERED_SEARCH_TYPE
,
}
from
'
~/packages_and_registries/shared/constants
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
export
default
{
export
default
{
...
@@ -19,21 +23,71 @@ export default {
...
@@ -19,21 +23,71 @@ export default {
},
},
],
],
components
:
{
RegistrySearch
,
UrlSync
},
components
:
{
RegistrySearch
,
UrlSync
},
inject
:
[
'
isGroupPage
'
],
data
()
{
return
{
filters
:
[],
sorting
:
{
orderBy
:
'
name
'
,
sort
:
'
desc
'
,
},
mountRegistrySearch
:
false
,
};
},
computed
:
{
computed
:
{
...
mapState
({
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
sorting
:
(
state
)
=>
state
.
sorting
,
filter
:
(
state
)
=>
state
.
filter
,
}),
sortableFields
()
{
sortableFields
()
{
return
sortableFields
(
this
.
isGroupPage
);
return
sortableFields
(
this
.
isGroupPage
);
},
},
parsedSorting
()
{
const
cleanOrderBy
=
this
.
sorting
?.
orderBy
.
replace
(
'
_at
'
,
''
);
return
`
${
cleanOrderBy
}
_
${
this
.
sorting
?.
sort
}
`.toUpperCase();
},
parsedFilters() {
const parsed = {
packageName: '',
packageType: undefined,
};
return this.filters.reduce((acc, filter) => {
if (filter.type === FILTERED_SEARCH_TYPE && filter.value?.data) {
return {
...acc,
packageType: filter.value.data.toUpperCase(),
};
}
if (filter.type === FILTERED_SEARCH_TERM) {
return {
...acc,
packageName: `
$
{
acc
.
packageName
}
$
{
filter
.
value
.
data
}
`.trim(),
};
}
return acc;
}, parsed);
},
},
mounted() {
const queryParams = getQueryParams(window.document.location.search);
const { sorting, filters } = extractFilterAndSorting(queryParams);
this.updateSorting(sorting);
this.updateFilters(filters);
this.mountRegistrySearch = true;
this.emitUpdate();
},
},
methods: {
methods: {
...
mapActions
([
'
setSorting
'
,
'
setFilter
'
]),
updateFilters(newValue) {
this.filters = newValue;
},
updateSorting(newValue) {
updateSorting(newValue) {
this
.
setSorting
(
newValue
);
this.sorting = { ...this.sorting, ...newValue };
this
.
$emit
(
'
update
'
);
},
updateSortingAndEmitUpdate(newValue) {
this.updateSorting(newValue);
this.emitUpdate();
},
emitUpdate() {
this.$emit('update', { sort: this.parsedSorting, filters: this.parsedFilters });
},
},
},
},
};
};
...
@@ -43,13 +97,14 @@ export default {
...
@@ -43,13 +97,14 @@ export default {
<url-sync>
<url-sync>
<template
#default
="
{ updateQuery }">
<template
#default
="
{ updateQuery }">
<registry-search
<registry-search
:filter=
"filter"
v-if=
"mountRegistrySearch"
:filter=
"filters"
:sorting=
"sorting"
:sorting=
"sorting"
:tokens=
"$options.tokens"
:tokens=
"$options.tokens"
:sortable-fields=
"sortableFields"
:sortable-fields=
"sortableFields"
@
sorting:changed=
"updateSorting"
@
sorting:changed=
"updateSorting
AndEmitUpdate
"
@
filter:changed=
"
setFilter
"
@
filter:changed=
"
updateFilters
"
@
filter:submit=
"
$emit('update')
"
@
filter:submit=
"
emitUpdate
"
@
query:changed=
"updateQuery"
@
query:changed=
"updateQuery"
/>
/>
</
template
>
</
template
>
...
...
app/assets/javascripts/packages_and_registries/package_registry/constants.js
View file @
7595ec3c
...
@@ -89,3 +89,7 @@ export const YARN_PACKAGE_MANAGER = 'yarn';
...
@@ -89,3 +89,7 @@ export const YARN_PACKAGE_MANAGER = 'yarn';
export
const
PROJECT_PACKAGE_ENDPOINT_TYPE
=
'
project
'
;
export
const
PROJECT_PACKAGE_ENDPOINT_TYPE
=
'
project
'
;
export
const
INSTANCE_PACKAGE_ENDPOINT_TYPE
=
'
instance
'
;
export
const
INSTANCE_PACKAGE_ENDPOINT_TYPE
=
'
instance
'
;
export
const
PROJECT_RESOURCE_TYPE
=
'
project
'
;
export
const
GROUP_RESOURCE_TYPE
=
'
group
'
;
export
const
LIST_QUERY_DEBOUNCE_TIME
=
50
;
app/assets/javascripts/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql
0 → 100644
View file @
7595ec3c
fragment
PackageData
on
Package
{
id
name
}
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
0 → 100644
View file @
7595ec3c
#import "~/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql"
query
getPackages
(
$fullPath
:
ID
!
$isGroupPage
:
Boolean
!
$sort
:
PackageSort
$groupSort
:
PackageGroupSort
$packageName
:
String
$packageType
:
PackageTypeEnum
)
{
project
(
fullPath
:
$fullPath
)
@skip
(
if
:
$isGroupPage
)
{
packages
(
sort
:
$sort
,
packageName
:
$packageName
,
packageType
:
$packageType
)
{
count
nodes
{
...
PackageData
}
}
}
group
(
fullPath
:
$fullPath
)
@include
(
if
:
$isGroupPage
)
{
packages
(
sort
:
$groupSort
,
packageName
:
$packageName
,
packageType
:
$packageType
)
{
count
nodes
{
...
PackageData
}
}
}
}
app/assets/javascripts/packages_and_registries/package_registry/pages/list.js
View file @
7595ec3c
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
Translate
from
'
~/vue_shared/translate
'
;
import
Translate
from
'
~/vue_shared/translate
'
;
import
{
apolloProvider
}
from
'
~/packages_and_registries/package_registry/graphql/index
'
;
import
PackagesListApp
from
'
../components/list/app.vue
'
;
import
PackagesListApp
from
'
../components/list/app.vue
'
;
Vue
.
use
(
Translate
);
Vue
.
use
(
Translate
);
...
@@ -7,10 +8,14 @@ Vue.use(Translate);
...
@@ -7,10 +8,14 @@ Vue.use(Translate);
export
default
()
=>
{
export
default
()
=>
{
const
el
=
document
.
getElementById
(
'
js-vue-packages-list
'
);
const
el
=
document
.
getElementById
(
'
js-vue-packages-list
'
);
const
isGroupPage
=
el
.
dataset
.
pageType
===
'
groups
'
;
return
new
Vue
({
return
new
Vue
({
el
,
el
,
apolloProvider
,
provide
:
{
provide
:
{
...
el
.
dataset
,
...
el
.
dataset
,
isGroupPage
,
},
},
render
(
createElement
)
{
render
(
createElement
)
{
return
createElement
(
PackagesListApp
);
return
createElement
(
PackagesListApp
);
...
...
app/assets/javascripts/packages_and_registries/shared/constants.js
View file @
7595ec3c
export
const
FILTERED_SEARCH_TERM
=
'
filtered-search-term
'
;
export
const
FILTERED_SEARCH_TERM
=
'
filtered-search-term
'
;
export
const
FILTERED_SEARCH_TYPE
=
'
type
'
;
app/helpers/packages_helper.rb
View file @
7595ec3c
...
@@ -41,6 +41,7 @@ module PackagesHelper
...
@@ -41,6 +41,7 @@ module PackagesHelper
def
packages_list_data
(
type
,
resource
)
def
packages_list_data
(
type
,
resource
)
{
{
resource_id:
resource
.
id
,
resource_id:
resource
.
id
,
full_path:
resource
.
full_path
,
page_type:
type
,
page_type:
type
,
empty_list_help_url:
help_page_path
(
'user/packages/package_registry/index'
),
empty_list_help_url:
help_page_path
(
'user/packages/package_registry/index'
),
empty_list_illustration:
image_path
(
'illustrations/no-packages.svg'
),
empty_list_illustration:
image_path
(
'illustrations/no-packages.svg'
),
...
...
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/app_spec.js.snap
View file @
7595ec3c
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`
packages_list_a
pp renders 1`] = `
exports[`
PackagesListA
pp renders 1`] = `
<div>
<div>
<package-title-stub
<package-title-stub
count="
0
"
count="
2
"
helpurl="packageHelpUrl"
helpurl="packageHelpUrl"
/>
/>
<package-search-stub />
</div>
</div>
`;
`;
spec/frontend/packages_and_registries/package_registry/components/list/app_spec.js
View file @
7595ec3c
import
{
GlEmptyState
,
GlSprintf
,
GlLink
}
from
'
@gitlab/ui
'
;
import
{
GlEmptyState
,
GlSprintf
,
GlLink
}
from
'
@gitlab/ui
'
;
import
{
createLocalVue
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
PackageListApp
from
'
~/packages_and_registries/package_registry/components/list/app.vue
'
;
import
PackageListApp
from
'
~/packages_and_registries/package_registry/components/list/app.vue
'
;
import
PackageTitle
from
'
~/packages_and_registries/package_registry/components/list/package_title.vue
'
;
import
PackageTitle
from
'
~/packages_and_registries/package_registry/components/list/package_title.vue
'
;
import
PackageSearch
from
'
~/packages_and_registries/package_registry/components/list/package_search.vue
'
;
import
{
PROJECT_RESOURCE_TYPE
,
GROUP_RESOURCE_TYPE
,
LIST_QUERY_DEBOUNCE_TIME
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
getPackagesQuery
from
'
~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
'
;
import
*
as
packageUtils
from
'
~/packages_and_registries/shared/utils
'
;
import
{
packagesListQuery
}
from
'
../../mock_data
'
;
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/flash
'
);
describe
(
'
packages_list_app
'
,
()
=>
{
const
localVue
=
createLocalVue
();
describe
(
'
PackagesListApp
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
let
apolloProvider
;
const
defaultProvide
=
{
packageHelpUrl
:
'
packageHelpUrl
'
,
emptyListIllustration
:
'
emptyListIllustration
'
,
emptyListHelpUrl
:
'
emptyListHelpUrl
'
,
isGroupPage
:
true
,
fullPath
:
'
gitlab-org
'
,
};
const
PackageList
=
{
const
PackageList
=
{
name
:
'
package-list
'
,
name
:
'
package-list
'
,
...
@@ -18,9 +43,21 @@ describe('packages_list_app', () => {
...
@@ -18,9 +43,21 @@ describe('packages_list_app', () => {
const
GlLoadingIcon
=
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
};
const
GlLoadingIcon
=
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
};
const
findPackageTitle
=
()
=>
wrapper
.
findComponent
(
PackageTitle
);
const
findPackageTitle
=
()
=>
wrapper
.
findComponent
(
PackageTitle
);
const
findSearch
=
()
=>
wrapper
.
findComponent
(
PackageSearch
);
const
mountComponent
=
({
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
()),
provide
=
defaultProvide
,
}
=
{})
=>
{
localVue
.
use
(
VueApollo
);
const
requestHandlers
=
[[
getPackagesQuery
,
resolver
]];
apolloProvider
=
createMockApollo
(
requestHandlers
);
const
mountComponent
=
()
=>
{
wrapper
=
shallowMountExtended
(
PackageListApp
,
{
wrapper
=
shallowMountExtended
(
PackageListApp
,
{
localVue
,
apolloProvider
,
provide
,
stubs
:
{
stubs
:
{
GlEmptyState
,
GlEmptyState
,
GlLoadingIcon
,
GlLoadingIcon
,
...
@@ -28,30 +65,90 @@ describe('packages_list_app', () => {
...
@@ -28,30 +65,90 @@ describe('packages_list_app', () => {
GlSprintf
,
GlSprintf
,
GlLink
,
GlLink
,
},
},
provide
:
{
packageHelpUrl
:
'
packageHelpUrl
'
,
emptyListIllustration
:
'
emptyListIllustration
'
,
emptyListHelpUrl
:
'
emptyListHelpUrl
'
,
},
});
});
};
};
beforeEach
(()
=>
{
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
({});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
it
(
'
renders
'
,
()
=>
{
const
waitForDebouncedApollo
=
()
=>
{
jest
.
advanceTimersByTime
(
LIST_QUERY_DEBOUNCE_TIME
);
return
waitForPromises
();
};
it
(
'
renders
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
await
waitForDebouncedApollo
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
it
(
'
has a package title
'
,
()
=>
{
it
(
'
has a package title
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
await
waitForDebouncedApollo
();
expect
(
findPackageTitle
().
exists
()).
toBe
(
true
);
expect
(
findPackageTitle
().
exists
()).
toBe
(
true
);
expect
(
findPackageTitle
().
props
(
'
count
'
)).
toBe
(
2
);
});
describe
(
'
search component
'
,
()
=>
{
it
(
'
exists
'
,
()
=>
{
mountComponent
();
expect
(
findSearch
().
exists
()).
toBe
(
true
);
});
it
(
'
on update triggers a new query with updated values
'
,
async
()
=>
{
const
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
());
mountComponent
({
resolver
});
const
payload
=
{
sort
:
'
VERSION_DESC
'
,
filters
:
{
packageName
:
'
foo
'
,
packageType
:
'
CONAN
'
},
};
findSearch
().
vm
.
$emit
(
'
update
'
,
payload
);
await
waitForDebouncedApollo
();
jest
.
advanceTimersByTime
(
LIST_QUERY_DEBOUNCE_TIME
);
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
groupSort
:
payload
.
sort
,
...
payload
.
filters
,
}),
);
});
});
describe
.
each
`
type | sortType
${
PROJECT_RESOURCE_TYPE
}
|
${
'
sort
'
}
${
GROUP_RESOURCE_TYPE
}
|
${
'
groupSort
'
}
`
(
'
$type query
'
,
({
type
,
sortType
})
=>
{
let
provide
;
let
resolver
;
const
isGroupPage
=
type
===
GROUP_RESOURCE_TYPE
;
beforeEach
(()
=>
{
provide
=
{
...
defaultProvide
,
isGroupPage
};
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
(
type
));
mountComponent
({
provide
,
resolver
});
return
waitForDebouncedApollo
();
});
it
(
'
succeeds
'
,
()
=>
{
expect
(
findPackageTitle
().
props
(
'
count
'
)).
toBe
(
2
);
});
it
(
'
calls the resolver with the right parameters
'
,
()
=>
{
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
isGroupPage
,
[
sortType
]:
''
}),
);
});
});
});
});
});
spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
View file @
7595ec3c
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
import
component
from
'
~/packages_and_registries/package_registry/components/list/package_search.vue
'
;
import
component
from
'
~/packages_and_registries/package_registry/components/list/package_search.vue
'
;
import
PackageTypeToken
from
'
~/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue
'
;
import
PackageTypeToken
from
'
~/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
import
{
useMockLocationHelper
}
from
'
helpers/mock_window_location_helper
'
;
import
{
getQueryParams
,
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
const
localVue
=
createLocalVue
();
jest
.
mock
(
'
~/packages_and_registries/shared/utils
'
);
localVue
.
use
(
Vuex
);
useMockLocationHelper
();
describe
(
'
Package Search
'
,
()
=>
{
describe
(
'
Package Search
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
let
store
;
const
defaultQueryParamsMock
=
{
filters
:
[
'
foo
'
],
sorting
:
{
sort
:
'
desc
'
},
};
const
findRegistrySearch
=
()
=>
wrapper
.
findComponent
(
RegistrySearch
);
const
findRegistrySearch
=
()
=>
wrapper
.
findComponent
(
RegistrySearch
);
const
findUrlSync
=
()
=>
wrapper
.
findComponent
(
UrlSync
);
const
findUrlSync
=
()
=>
wrapper
.
findComponent
(
UrlSync
);
const
createStore
=
(
isGroupPage
)
=>
{
const
mountComponent
=
(
isGroupPage
=
false
)
=>
{
const
state
=
{
wrapper
=
shallowMountExtended
(
component
,
{
config
:
{
provide
()
{
return
{
isGroupPage
,
isGroupPage
,
},
sorting
:
{
orderBy
:
'
version
'
,
sort
:
'
desc
'
,
},
filter
:
[],
};
store
=
new
Vuex
.
Store
({
state
,
});
store
.
dispatch
=
jest
.
fn
();
};
};
},
const
mountComponent
=
(
isGroupPage
=
false
)
=>
{
createStore
(
isGroupPage
);
wrapper
=
shallowMount
(
component
,
{
localVue
,
store
,
stubs
:
{
stubs
:
{
UrlSync
,
UrlSync
,
},
},
});
});
};
};
beforeEach
(()
=>
{
extractFilterAndSorting
.
mockReturnValue
(
defaultQueryParamsMock
);
});
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
wrapper
=
null
;
});
});
it
(
'
has a registry search component
'
,
()
=>
{
it
(
'
has a registry search component
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
await
nextTick
();
expect
(
findRegistrySearch
().
exists
()).
toBe
(
true
);
expect
(
findRegistrySearch
().
exists
()).
toBe
(
true
);
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
filter
:
store
.
state
.
filter
,
sorting
:
store
.
state
.
sorting
,
tokens
:
expect
.
arrayContaining
([
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
]),
sortableFields
:
sortableFields
(),
});
});
it
(
'
registry search is mounted after mount
'
,
async
()
=>
{
mountComponent
();
expect
(
findRegistrySearch
().
exists
()).
toBe
(
false
);
});
it
(
'
has a UrlSync component
'
,
()
=>
{
mountComponent
();
expect
(
findUrlSync
().
exists
()).
toBe
(
true
);
});
});
it
.
each
`
it
.
each
`
isGroupPage | page
isGroupPage | page
${
false
}
|
${
'
project
'
}
${
false
}
|
${
'
project
'
}
${
true
}
|
${
'
group
'
}
${
true
}
|
${
'
group
'
}
`
(
'
in a $page page binds the right props
'
,
({
isGroupPage
})
=>
{
`
(
'
in a $page page binds the right props
'
,
async
({
isGroupPage
})
=>
{
mountComponent
(
isGroupPage
);
mountComponent
(
isGroupPage
);
await
nextTick
();
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
filter
:
store
.
state
.
filter
,
sorting
:
store
.
state
.
sorting
,
tokens
:
expect
.
arrayContaining
([
tokens
:
expect
.
arrayContaining
([
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
expect
.
objectContaining
({
token
:
PackageTypeToken
,
type
:
'
type
'
,
icon
:
'
package
'
}),
]),
]),
...
@@ -81,48 +81,85 @@ describe('Package Search', () => {
...
@@ -81,48 +81,85 @@ describe('Package Search', () => {
});
});
});
});
it
(
'
on sorting:changed emits update event and
calls vuex setSorting
'
,
()
=>
{
it
(
'
on sorting:changed emits update event and
update internal sort
'
,
async
()
=>
{
const
payload
=
{
sort
:
'
foo
'
};
const
payload
=
{
sort
:
'
foo
'
};
mountComponent
();
mountComponent
();
await
nextTick
();
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
payload
);
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
payload
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
payload
);
await
nextTick
();
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
expect
(
findRegistrySearch
().
props
(
'
sorting
'
)).
toEqual
({
sort
:
'
foo
'
,
orderBy
:
'
name
'
});
// there is always a first call on mounted that emits up default values
expect
(
wrapper
.
emitted
(
'
update
'
)[
1
]).
toEqual
([
{
filters
:
{
packageName
:
''
,
packageType
:
undefined
,
},
sort
:
'
NAME_FOO
'
,
},
]);
});
});
it
(
'
on filter:changed
calls vuex setFilter
'
,
()
=>
{
it
(
'
on filter:changed
updates the filters
'
,
async
()
=>
{
const
payload
=
[
'
foo
'
];
const
payload
=
[
'
foo
'
];
mountComponent
();
mountComponent
();
await
nextTick
();
findRegistrySearch
().
vm
.
$emit
(
'
filter:changed
'
,
payload
);
findRegistrySearch
().
vm
.
$emit
(
'
filter:changed
'
,
payload
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setFilter
'
,
payload
);
await
nextTick
();
expect
(
findRegistrySearch
().
props
(
'
filter
'
)).
toEqual
([
'
foo
'
]);
});
});
it
(
'
on filter:submit emits update event
'
,
()
=>
{
it
(
'
on filter:submit emits update event
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
await
nextTick
(
);
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
});
it
(
'
has a UrlSync component
'
,
()
=>
{
mountComponent
();
expect
(
findUrlSync
().
exists
()).
toBe
(
true
);
expect
(
wrapper
.
emitted
(
'
update
'
)[
1
]).
toEqual
([
{
filters
:
{
packageName
:
''
,
packageType
:
undefined
,
},
sort
:
'
NAME_DESC
'
,
},
]);
});
});
it
(
'
on query:changed calls updateQuery from UrlSync
'
,
()
=>
{
it
(
'
on query:changed calls updateQuery from UrlSync
'
,
async
()
=>
{
jest
.
spyOn
(
UrlSync
.
methods
,
'
updateQuery
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
UrlSync
.
methods
,
'
updateQuery
'
).
mockImplementation
(()
=>
{});
mountComponent
();
mountComponent
();
await
nextTick
();
findRegistrySearch
().
vm
.
$emit
(
'
query:changed
'
);
findRegistrySearch
().
vm
.
$emit
(
'
query:changed
'
);
expect
(
UrlSync
.
methods
.
updateQuery
).
toHaveBeenCalled
();
expect
(
UrlSync
.
methods
.
updateQuery
).
toHaveBeenCalled
();
});
});
it
(
'
sets the component sorting and filtering based on the querystring
'
,
async
()
=>
{
mountComponent
();
await
nextTick
();
expect
(
getQueryParams
).
toHaveBeenCalled
();
expect
(
findRegistrySearch
().
props
()).
toMatchObject
({
filter
:
defaultQueryParamsMock
.
filters
,
sorting
:
defaultQueryParamsMock
.
sorting
,
});
});
});
});
spec/frontend/packages_and_registries/package_registry/mock_data.js
View file @
7595ec3c
...
@@ -249,3 +249,27 @@ export const packageDestroyFileMutationError = () => ({
...
@@ -249,3 +249,27 @@ export const packageDestroyFileMutationError = () => ({
},
},
],
],
});
});
export
const
packagesListQuery
=
(
type
=
'
group
'
)
=>
({
data
:
{
[
type
]:
{
packages
:
{
count
:
2
,
nodes
:
[
{
__typename
:
'
Package
'
,
id
:
'
gid://gitlab/Packages::Package/247
'
,
name
:
'
version_test1
'
,
},
{
__typename
:
'
Package
'
,
id
:
'
gid://gitlab/Packages::Package/246
'
,
name
:
'
version_test1
'
,
},
],
__typename
:
'
PackageConnection
'
,
},
__typename
:
'
Group
'
,
},
},
});
spec/helpers/packages_helper_spec.rb
View file @
7595ec3c
...
@@ -260,4 +260,34 @@ RSpec.describe PackagesHelper do
...
@@ -260,4 +260,34 @@ RSpec.describe PackagesHelper do
end
end
end
end
end
end
describe
'#packages_list_data'
do
let_it_be
(
:resource
)
{
project
}
let_it_be
(
:type
)
{
'project'
}
let
(
:expected_result
)
do
{
resource_id:
resource
.
id
,
full_path:
resource
.
full_path
,
page_type:
type
}
end
subject
(
:result
)
{
helper
.
packages_list_data
(
type
,
resource
)
}
context
'at a project level'
do
it
'populates presenter data'
do
expect
(
result
).
to
match
(
hash_including
(
expected_result
))
end
end
context
'at a group level'
do
let_it_be
(
:resource
)
{
create
(
:group
)
}
let_it_be
(
:type
)
{
'group'
}
it
'populates presenter data'
do
expect
(
result
).
to
match
(
hash_including
(
expected_result
))
end
end
end
end
end
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