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
269fbf52
Commit
269fbf52
authored
Mar 30, 2021
by
Nicolò Maria Mezzopera
Committed by
Mark Florian
Mar 30, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add url-sync to package_search
- source - tests
parent
fce0a476
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
269 additions
and
52 deletions
+269
-52
app/assets/javascripts/packages/list/components/package_search.vue
...s/javascripts/packages/list/components/package_search.vue
+16
-10
app/assets/javascripts/packages/list/components/packages_list_app.vue
...avascripts/packages/list/components/packages_list_app.vue
+12
-1
app/assets/javascripts/packages_and_registries/shared/utils.js
...ssets/javascripts/packages_and_registries/shared/utils.js
+20
-0
app/assets/javascripts/registry/explorer/pages/list.vue
app/assets/javascripts/registry/explorer/pages/list.vue
+23
-4
changelogs/unreleased/324864-make-it-easy-to-share-a-filtered-view-of-the-registry-2.yml
...ke-it-easy-to-share-a-filtered-view-of-the-registry-2.yml
+5
-0
spec/frontend/packages/list/components/packages_list_app_spec.js
...ontend/packages/list/components/packages_list_app_spec.js
+70
-21
spec/frontend/packages/list/components/packages_search_spec.js
...frontend/packages/list/components/packages_search_spec.js
+22
-1
spec/frontend/packages_and_registries/shared/utils_spec.js
spec/frontend/packages_and_registries/shared/utils_spec.js
+26
-2
spec/frontend/registry/explorer/pages/list_spec.js
spec/frontend/registry/explorer/pages/list_spec.js
+75
-13
No files found.
app/assets/javascripts/packages/list/components/package_search.vue
View file @
269fbf52
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
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
getTableHeaders
from
'
../utils
'
;
import
getTableHeaders
from
'
../utils
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
...
@@ -16,7 +17,7 @@ export default {
...
@@ -16,7 +17,7 @@ export default {
operators
:
[{
value
:
'
=
'
,
description
:
__
(
'
is
'
),
default
:
'
true
'
}],
operators
:
[{
value
:
'
=
'
,
description
:
__
(
'
is
'
),
default
:
'
true
'
}],
},
},
],
],
components
:
{
RegistrySearch
},
components
:
{
RegistrySearch
,
UrlSync
},
computed
:
{
computed
:
{
...
mapState
({
...
mapState
({
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
...
@@ -38,6 +39,8 @@ export default {
...
@@ -38,6 +39,8 @@ export default {
</
script
>
</
script
>
<
template
>
<
template
>
<url-sync>
<template
#default
="
{ updateQuery }">
<registry-search
<registry-search
:filter=
"filter"
:filter=
"filter"
:sorting=
"sorting"
:sorting=
"sorting"
...
@@ -46,5 +49,8 @@ export default {
...
@@ -46,5 +49,8 @@ export default {
@
sorting:changed=
"updateSorting"
@
sorting:changed=
"updateSorting"
@
filter:changed=
"setFilter"
@
filter:changed=
"setFilter"
@
filter:submit=
"$emit('update')"
@
filter:submit=
"$emit('update')"
@
query:changed=
"updateQuery"
/>
/>
</
template
>
</url-sync>
</template>
</template>
app/assets/javascripts/packages/list/components/packages_list_app.vue
View file @
269fbf52
...
@@ -6,6 +6,7 @@ import { historyReplaceState } from '~/lib/utils/common_utils';
...
@@ -6,6 +6,7 @@ import { historyReplaceState } from '~/lib/utils/common_utils';
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
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
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
{
getQueryParams
,
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
../constants
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
../constants
'
;
import
PackageSearch
from
'
./package_search.vue
'
;
import
PackageSearch
from
'
./package_search.vue
'
;
import
PackageTitle
from
'
./package_title.vue
'
;
import
PackageTitle
from
'
./package_title.vue
'
;
...
@@ -42,11 +43,21 @@ export default {
...
@@ -42,11 +43,21 @@ export default {
},
},
},
},
mounted
()
{
mounted
()
{
const
queryParams
=
getQueryParams
(
window
.
document
.
location
.
search
);
const
{
sorting
,
filters
}
=
extractFilterAndSorting
(
queryParams
);
this
.
setSorting
(
sorting
);
this
.
setFilter
(
filters
);
this
.
requestPackagesList
();
this
.
requestPackagesList
();
this
.
checkDeleteAlert
();
this
.
checkDeleteAlert
();
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
requestPackagesList
'
,
'
requestDeletePackage
'
,
'
setSelectedType
'
]),
...
mapActions
([
'
requestPackagesList
'
,
'
requestDeletePackage
'
,
'
setSelectedType
'
,
'
setSorting
'
,
'
setFilter
'
,
]),
onPageChanged
(
page
)
{
onPageChanged
(
page
)
{
return
this
.
requestPackagesList
({
page
});
return
this
.
requestPackagesList
({
page
});
},
},
...
...
app/assets/javascripts/packages_and_registries/shared/utils.js
View file @
269fbf52
...
@@ -7,3 +7,23 @@ export const keyValueToFilterToken = (type, data) => ({ type, value: { data } })
...
@@ -7,3 +7,23 @@ export const keyValueToFilterToken = (type, data) => ({ type, value: { data } })
export
const
searchArrayToFilterTokens
=
(
search
)
=>
export
const
searchArrayToFilterTokens
=
(
search
)
=>
search
.
map
((
s
)
=>
keyValueToFilterToken
(
FILTERED_SEARCH_TERM
,
s
));
search
.
map
((
s
)
=>
keyValueToFilterToken
(
FILTERED_SEARCH_TERM
,
s
));
export
const
extractFilterAndSorting
=
(
queryObject
)
=>
{
const
{
type
,
search
,
sort
,
orderBy
}
=
queryObject
;
const
filters
=
[];
const
sorting
=
{};
if
(
type
)
{
filters
.
push
(
keyValueToFilterToken
(
'
type
'
,
type
));
}
if
(
search
)
{
filters
.
push
(...
searchArrayToFilterTokens
(
search
));
}
if
(
sort
)
{
sorting
.
sort
=
sort
;
}
if
(
orderBy
)
{
sorting
.
orderBy
=
orderBy
;
}
return
{
filters
,
sorting
};
};
app/assets/javascripts/registry/explorer/pages/list.vue
View file @
269fbf52
...
@@ -12,6 +12,7 @@ import { get } from 'lodash';
...
@@ -12,6 +12,7 @@ import { get } from 'lodash';
import
getContainerRepositoriesQuery
from
'
shared_queries/container_registry/get_container_repositories.query.graphql
'
;
import
getContainerRepositoriesQuery
from
'
shared_queries/container_registry/get_container_repositories.query.graphql
'
;
import
createFlash
from
'
~/flash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
{
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
import
Tracking
from
'
~/tracking
'
;
import
Tracking
from
'
~/tracking
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
DeleteImage
from
'
../components/delete_image.vue
'
;
import
DeleteImage
from
'
../components/delete_image.vue
'
;
...
@@ -82,6 +83,9 @@ export default {
...
@@ -82,6 +83,9 @@ export default {
searchConfig
:
SORT_FIELDS
,
searchConfig
:
SORT_FIELDS
,
apollo
:
{
apollo
:
{
baseImages
:
{
baseImages
:
{
skip
()
{
return
!
this
.
fetchBaseQuery
;
},
query
:
getContainerRepositoriesQuery
,
query
:
getContainerRepositoriesQuery
,
variables
()
{
variables
()
{
return
this
.
queryVariables
;
return
this
.
queryVariables
;
...
@@ -125,15 +129,19 @@ export default {
...
@@ -125,15 +129,19 @@ export default {
sorting
:
{
orderBy
:
'
UPDATED
'
,
sort
:
'
desc
'
},
sorting
:
{
orderBy
:
'
UPDATED
'
,
sort
:
'
desc
'
},
name
:
null
,
name
:
null
,
mutationLoading
:
false
,
mutationLoading
:
false
,
fetchBaseQuery
:
false
,
fetchAdditionalDetails
:
false
,
fetchAdditionalDetails
:
false
,
};
};
},
},
computed
:
{
computed
:
{
images
()
{
images
()
{
if
(
this
.
baseImages
)
{
return
this
.
baseImages
.
map
((
image
,
index
)
=>
({
return
this
.
baseImages
.
map
((
image
,
index
)
=>
({
...
image
,
...
image
,
...
get
(
this
.
additionalDetails
,
index
,
{}),
...
get
(
this
.
additionalDetails
,
index
,
{}),
}));
}));
}
return
[];
},
},
graphqlResource
()
{
graphqlResource
()
{
return
this
.
config
.
isGroupPage
?
'
group
'
:
'
project
'
;
return
this
.
config
.
isGroupPage
?
'
group
'
:
'
project
'
;
...
@@ -172,8 +180,15 @@ export default {
...
@@ -172,8 +180,15 @@ export default {
},
},
},
},
mounted
()
{
mounted
()
{
const
{
sorting
,
filters
}
=
extractFilterAndSorting
(
this
.
$route
.
query
);
this
.
filter
=
[...
filters
];
this
.
name
=
filters
[
0
]?.
value
.
data
;
this
.
sorting
=
{
...
this
.
sorting
,
...
sorting
};
// If the two graphql calls - which are not batched - resolve togheter we will have a race
// If the two graphql calls - which are not batched - resolve togheter we will have a race
// condition when apollo sets the cache, with this we give the 'base' call an headstart
// condition when apollo sets the cache, with this we give the 'base' call an headstart
this
.
fetchBaseQuery
=
true
;
setTimeout
(()
=>
{
setTimeout
(()
=>
{
this
.
fetchAdditionalDetails
=
true
;
this
.
fetchAdditionalDetails
=
true
;
},
200
);
},
200
);
...
@@ -245,6 +260,9 @@ export default {
...
@@ -245,6 +260,9 @@ export default {
const
search
=
this
.
filter
.
find
((
i
)
=>
i
.
type
===
FILTERED_SEARCH_TERM
);
const
search
=
this
.
filter
.
find
((
i
)
=>
i
.
type
===
FILTERED_SEARCH_TERM
);
this
.
name
=
search
?.
value
?.
data
;
this
.
name
=
search
?.
value
?.
data
;
},
},
updateUrlQueryString
(
query
)
{
this
.
$router
.
push
({
query
});
},
},
},
};
};
</
script
>
</
script
>
...
@@ -304,6 +322,7 @@ export default {
...
@@ -304,6 +322,7 @@ export default {
@
sorting:changed=
"updateSorting"
@
sorting:changed=
"updateSorting"
@
filter:changed=
"filter = $event"
@
filter:changed=
"filter = $event"
@
filter:submit=
"doFilter"
@
filter:submit=
"doFilter"
@
query:changed=
"updateUrlQueryString"
/>
/>
<div
v-if=
"isLoading"
class=
"gl-mt-5"
>
<div
v-if=
"isLoading"
class=
"gl-mt-5"
>
...
...
changelogs/unreleased/324864-make-it-easy-to-share-a-filtered-view-of-the-registry-2.yml
0 → 100644
View file @
269fbf52
---
title
:
Connect Registries searches to URL
merge_request
:
57251
author
:
type
:
added
spec/frontend/packages/list/components/packages_list_app_spec.js
View file @
269fbf52
...
@@ -7,6 +7,8 @@ import PackageSearch from '~/packages/list/components/package_search.vue';
...
@@ -7,6 +7,8 @@ import PackageSearch from '~/packages/list/components/package_search.vue';
import
PackageListApp
from
'
~/packages/list/components/packages_list_app.vue
'
;
import
PackageListApp
from
'
~/packages/list/components/packages_list_app.vue
'
;
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
*
as
packageUtils
from
'
~/packages_and_registries/shared/utils
'
;
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
jest
.
mock
(
'
~/flash
'
);
jest
.
mock
(
'
~/flash
'
);
...
@@ -61,6 +63,7 @@ describe('packages_list_app', () => {
...
@@ -61,6 +63,7 @@ describe('packages_list_app', () => {
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createStore
();
createStore
();
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
({});
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
...
@@ -72,46 +75,92 @@ describe('packages_list_app', () => {
...
@@ -72,46 +75,92 @@ describe('packages_list_app', () => {
expect
(
wrapper
.
element
).
toMatchSnapshot
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
describe
(
'
empty state
'
,
()
=>
{
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
it
(
'
generate the correct empty list link
'
,
()
=>
{
mountComponent
();
mountComponent
();
store
.
dispatch
.
mockClear
();
const
link
=
findListComponent
().
find
(
GlLink
);
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
,
{
page
:
1
});
});
expect
(
link
.
attributes
(
'
href
'
)).
toBe
(
emptyListHelpUrl
);
it
(
'
call requestDeletePackage on package:delete
'
,
()
=>
{
expect
(
link
.
text
()).
toBe
(
'
publish and share your packages
'
);
mountComponent
();
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
package:delete
'
,
'
foo
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestDeletePackage
'
,
'
foo
'
);
});
});
it
(
'
includes the right content on the default tab
'
,
()
=>
{
it
(
'
does call requestPackagesList only one time on render
'
,
()
=>
{
mountComponent
();
mountComponent
();
const
heading
=
findEmptyState
().
find
(
'
h1
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledTimes
(
3
);
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
1
,
'
setSorting
'
,
expect
.
any
(
Object
));
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
2
,
'
setFilter
'
,
expect
.
any
(
Array
));
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
3
,
'
requestPackagesList
'
);
});
expect
(
heading
.
text
()).
toBe
(
'
There are no packages yet
'
);
describe
(
'
url query string handling
'
,
()
=>
{
const
defaultQueryParamsMock
=
{
search
:
[
1
,
2
],
type
:
'
npm
'
,
sort
:
'
asc
'
,
orderBy
:
'
created
'
,
};
it
(
'
calls setSorting with the query string based sorting
'
,
()
=>
{
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
(
defaultQueryParamsMock
);
mountComponent
();
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
1
,
'
setSorting
'
,
{
orderBy
:
defaultQueryParamsMock
.
orderBy
,
sort
:
defaultQueryParamsMock
.
sort
,
});
});
});
});
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
it
(
'
calls setFilter with the query string based filters
'
,
()
=>
{
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
(
defaultQueryParamsMock
);
mountComponent
();
mountComponent
();
store
.
dispatch
.
mockClear
();
const
list
=
findListComponent
();
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
2
,
'
setFilter
'
,
[
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
{
type
:
'
type
'
,
value
:
{
data
:
defaultQueryParamsMock
.
type
}
},
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
,
{
page
:
1
});
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
defaultQueryParamsMock
.
search
[
0
]
}
},
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
defaultQueryParamsMock
.
search
[
1
]
}
},
]);
});
});
it
(
'
call requestDeletePackage on package:delete
'
,
()
=>
{
it
(
'
calls setSorting and setFilters with the results of extractFilterAndSorting
'
,
()
=>
{
jest
.
spyOn
(
packageUtils
,
'
extractFilterAndSorting
'
)
.
mockReturnValue
({
filters
:
[
'
foo
'
],
sorting
:
{
sort
:
'
desc
'
}
});
mountComponent
();
mountComponent
();
const
list
=
findListComponent
();
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
1
,
'
setSorting
'
,
{
sort
:
'
desc
'
});
list
.
vm
.
$emit
(
'
package:delete
'
,
'
foo
'
);
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
2
,
'
setFilter
'
,
[
'
foo
'
]);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestDeletePackage
'
,
'
foo
'
);
});
});
describe
(
'
empty state
'
,
()
=>
{
it
(
'
generate the correct empty list link
'
,
()
=>
{
mountComponent
();
const
link
=
findListComponent
().
find
(
GlLink
);
expect
(
link
.
attributes
(
'
href
'
)).
toBe
(
emptyListHelpUrl
);
expect
(
link
.
text
()).
toBe
(
'
publish and share your packages
'
);
});
});
it
(
'
does not call requestPackagesList two times on render
'
,
()
=>
{
it
(
'
includes the right content on the default tab
'
,
()
=>
{
mountComponent
();
mountComponent
();
expect
(
store
.
dispatch
).
toHaveBeenCalledTimes
(
1
);
const
heading
=
findEmptyState
().
find
(
'
h1
'
);
expect
(
heading
.
text
()).
toBe
(
'
There are no packages yet
'
);
});
});
});
describe
(
'
filter without results
'
,
()
=>
{
describe
(
'
filter without results
'
,
()
=>
{
...
...
spec/frontend/packages/list/components/packages_search_spec.js
View file @
269fbf52
...
@@ -4,6 +4,7 @@ import component from '~/packages/list/components/package_search.vue';
...
@@ -4,6 +4,7 @@ import component from '~/packages/list/components/package_search.vue';
import
PackageTypeToken
from
'
~/packages/list/components/tokens/package_type_token.vue
'
;
import
PackageTypeToken
from
'
~/packages/list/components/tokens/package_type_token.vue
'
;
import
getTableHeaders
from
'
~/packages/list/utils
'
;
import
getTableHeaders
from
'
~/packages/list/utils
'
;
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
'
;
const
localVue
=
createLocalVue
();
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
localVue
.
use
(
Vuex
);
...
@@ -12,7 +13,8 @@ describe('Package Search', () => {
...
@@ -12,7 +13,8 @@ describe('Package Search', () => {
let
wrapper
;
let
wrapper
;
let
store
;
let
store
;
const
findRegistrySearch
=
()
=>
wrapper
.
find
(
RegistrySearch
);
const
findRegistrySearch
=
()
=>
wrapper
.
findComponent
(
RegistrySearch
);
const
findUrlSync
=
()
=>
wrapper
.
findComponent
(
UrlSync
);
const
createStore
=
(
isGroupPage
)
=>
{
const
createStore
=
(
isGroupPage
)
=>
{
const
state
=
{
const
state
=
{
...
@@ -37,6 +39,9 @@ describe('Package Search', () => {
...
@@ -37,6 +39,9 @@ describe('Package Search', () => {
wrapper
=
shallowMount
(
component
,
{
wrapper
=
shallowMount
(
component
,
{
localVue
,
localVue
,
store
,
store
,
stubs
:
{
UrlSync
,
},
});
});
};
};
...
@@ -104,4 +109,20 @@ describe('Package Search', () => {
...
@@ -104,4 +109,20 @@ describe('Package Search', () => {
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
});
});
it
(
'
has a UrlSync component
'
,
()
=>
{
mountComponent
();
expect
(
findUrlSync
().
exists
()).
toBe
(
true
);
});
it
(
'
on query:changed calls updateQuery from UrlSync
'
,
()
=>
{
jest
.
spyOn
(
UrlSync
.
methods
,
'
updateQuery
'
).
mockImplementation
(()
=>
{});
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
query:changed
'
);
expect
(
UrlSync
.
methods
.
updateQuery
).
toHaveBeenCalled
();
});
});
});
spec/frontend/packages_and_registries/shared/utils_spec.js
View file @
269fbf52
import
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
{
import
{
getQueryParams
,
getQueryParams
,
keyValueToFilterToken
,
keyValueToFilterToken
,
searchArrayToFilterTokens
,
searchArrayToFilterTokens
,
extractFilterAndSorting
,
}
from
'
~/packages_and_registries/shared/utils
'
;
}
from
'
~/packages_and_registries/shared/utils
'
;
describe
(
'
Packages And Registries shared utils
'
,
()
=>
{
describe
(
'
Packages And Registries shared utils
'
,
()
=>
{
...
@@ -27,9 +29,31 @@ describe('Packages And Registries shared utils', () => {
...
@@ -27,9 +29,31 @@ describe('Packages And Registries shared utils', () => {
const
search
=
[
'
one
'
,
'
two
'
];
const
search
=
[
'
one
'
,
'
two
'
];
expect
(
searchArrayToFilterTokens
(
search
)).
toStrictEqual
([
expect
(
searchArrayToFilterTokens
(
search
)).
toStrictEqual
([
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
one
'
}
},
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
'
one
'
}
},
{
type
:
'
filtered-search-term
'
,
value
:
{
data
:
'
two
'
}
},
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
'
two
'
}
},
]);
]);
});
});
});
});
describe
(
'
extractFilterAndSorting
'
,
()
=>
{
it
.
each
`
search | type | sort | orderBy | result
${[
'
one
'
]}
|
${
'
myType
'
}
|
${
'
asc
'
}
|
${
'
foo
'
}
|
${{
sorting
:
{
sort
:
'
asc
'
,
orderBy
:
'
foo
'
}
, filters: [{ type: 'type', value: { data: 'myType' } }, { type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
${[
'
one
'
]}
|
${
null
}
|
${
'
asc
'
}
|
${
'
foo
'
}
|
${{
sorting
:
{
sort
:
'
asc
'
,
orderBy
:
'
foo
'
}
, filters: [{ type: FILTERED_SEARCH_TERM, value: { data: 'one' } }] }}
${[]}
|
${
null
}
|
${
'
asc
'
}
|
${
'
foo
'
}
|
${{
sorting
:
{
sort
:
'
asc
'
,
orderBy
:
'
foo
'
}
, filters: [] }}
${
null
}
|
${
null
}
|
${
'
asc
'
}
|
${
'
foo
'
}
|
${{
sorting
:
{
sort
:
'
asc
'
,
orderBy
:
'
foo
'
}
, filters: [] }}
${
null
}
|
${
null
}
|
${
null
}
|
${
'
foo
'
}
|
${{
sorting
:
{
orderBy
:
'
foo
'
}
, filters: [] }}
${
null
}
|
${
null
}
|
${
null
}
|
${
null
}
|
${{
sorting
:
{},
filters
:
[]
}
}
`
(
'
returns sorting and filters objects in the correct form
'
,
({
search
,
type
,
sort
,
orderBy
,
result
})
=>
{
const
queryObject
=
{
search
,
type
,
sort
,
orderBy
,
};
expect
(
extractFilterAndSorting
(
queryObject
)).
toStrictEqual
(
result
);
},
);
});
});
});
spec/frontend/registry/explorer/pages/list_spec.js
View file @
269fbf52
import
{
GlSkeletonLoader
,
GlSprintf
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
GlSkeletonLoader
,
GlSprintf
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
...
@@ -61,7 +62,7 @@ describe('List Page', () => {
...
@@ -61,7 +62,7 @@ describe('List Page', () => {
const
waitForApolloRequestRender
=
async
()
=>
{
const
waitForApolloRequestRender
=
async
()
=>
{
jest
.
runOnlyPendingTimers
();
jest
.
runOnlyPendingTimers
();
await
waitForPromises
();
await
waitForPromises
();
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
};
};
const
mountComponent
=
({
const
mountComponent
=
({
...
@@ -70,6 +71,7 @@ describe('List Page', () => {
...
@@ -70,6 +71,7 @@ describe('List Page', () => {
detailsResolver
=
jest
.
fn
().
mockResolvedValue
(
graphQLProjectImageRepositoriesDetailsMock
),
detailsResolver
=
jest
.
fn
().
mockResolvedValue
(
graphQLProjectImageRepositoriesDetailsMock
),
mutationResolver
=
jest
.
fn
().
mockResolvedValue
(
graphQLImageDeleteMock
),
mutationResolver
=
jest
.
fn
().
mockResolvedValue
(
graphQLImageDeleteMock
),
config
=
{
isGroupPage
:
false
},
config
=
{
isGroupPage
:
false
},
query
=
{},
}
=
{})
=>
{
}
=
{})
=>
{
localVue
.
use
(
VueApollo
);
localVue
.
use
(
VueApollo
);
...
@@ -96,6 +98,7 @@ describe('List Page', () => {
...
@@ -96,6 +98,7 @@ describe('List Page', () => {
$toast
,
$toast
,
$route
:
{
$route
:
{
name
:
'
foo
'
,
name
:
'
foo
'
,
query
,
},
},
...
mocks
,
...
mocks
,
},
},
...
@@ -159,9 +162,11 @@ describe('List Page', () => {
...
@@ -159,9 +162,11 @@ describe('List Page', () => {
});
});
describe
(
'
isLoading is true
'
,
()
=>
{
describe
(
'
isLoading is true
'
,
()
=>
{
it
(
'
shows the skeleton loader
'
,
()
=>
{
it
(
'
shows the skeleton loader
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
await
nextTick
();
expect
(
findSkeletonLoader
().
exists
()).
toBe
(
true
);
expect
(
findSkeletonLoader
().
exists
()).
toBe
(
true
);
});
});
...
@@ -177,9 +182,11 @@ describe('List Page', () => {
...
@@ -177,9 +182,11 @@ describe('List Page', () => {
expect
(
findCliCommands
().
exists
()).
toBe
(
false
);
expect
(
findCliCommands
().
exists
()).
toBe
(
false
);
});
});
it
(
'
title has the metadataLoading props set to true
'
,
()
=>
{
it
(
'
title has the metadataLoading props set to true
'
,
async
()
=>
{
mountComponent
();
mountComponent
();
await
nextTick
();
expect
(
findRegistryHeader
().
props
(
'
metadataLoading
'
)).
toBe
(
true
);
expect
(
findRegistryHeader
().
props
(
'
metadataLoading
'
)).
toBe
(
true
);
});
});
});
});
...
@@ -312,7 +319,7 @@ describe('List Page', () => {
...
@@ -312,7 +319,7 @@ describe('List Page', () => {
await
selectImageForDeletion
();
await
selectImageForDeletion
();
findDeleteImage
().
vm
.
$emit
(
'
success
'
);
findDeleteImage
().
vm
.
$emit
(
'
success
'
);
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
const
alert
=
findDeleteAlert
();
const
alert
=
findDeleteAlert
();
expect
(
alert
.
exists
()).
toBe
(
true
);
expect
(
alert
.
exists
()).
toBe
(
true
);
...
@@ -328,7 +335,7 @@ describe('List Page', () => {
...
@@ -328,7 +335,7 @@ describe('List Page', () => {
await
selectImageForDeletion
();
await
selectImageForDeletion
();
findDeleteImage
().
vm
.
$emit
(
'
error
'
);
findDeleteImage
().
vm
.
$emit
(
'
error
'
);
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
const
alert
=
findDeleteAlert
();
const
alert
=
findDeleteAlert
();
expect
(
alert
.
exists
()).
toBe
(
true
);
expect
(
alert
.
exists
()).
toBe
(
true
);
...
@@ -349,7 +356,7 @@ describe('List Page', () => {
...
@@ -349,7 +356,7 @@ describe('List Page', () => {
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
};
};
it
(
'
has a search box element
'
,
async
()
=>
{
it
(
'
has a search box element
'
,
async
()
=>
{
...
@@ -374,7 +381,7 @@ describe('List Page', () => {
...
@@ -374,7 +381,7 @@ describe('List Page', () => {
await
waitForApolloRequestRender
();
await
waitForApolloRequestRender
();
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
{
sort
:
'
asc
'
});
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
{
sort
:
'
asc
'
});
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
sort
:
'
UPDATED_DESC
'
}));
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
sort
:
'
UPDATED_DESC
'
}));
});
});
...
@@ -417,7 +424,7 @@ describe('List Page', () => {
...
@@ -417,7 +424,7 @@ describe('List Page', () => {
await
waitForApolloRequestRender
();
await
waitForApolloRequestRender
();
findImageList
().
vm
.
$emit
(
'
prev-page
'
);
findImageList
().
vm
.
$emit
(
'
prev-page
'
);
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
before
:
pageInfo
.
startCursor
}),
expect
.
objectContaining
({
before
:
pageInfo
.
startCursor
}),
...
@@ -437,7 +444,7 @@ describe('List Page', () => {
...
@@ -437,7 +444,7 @@ describe('List Page', () => {
await
waitForApolloRequestRender
();
await
waitForApolloRequestRender
();
findImageList
().
vm
.
$emit
(
'
next-page
'
);
findImageList
().
vm
.
$emit
(
'
next-page
'
);
await
wrapper
.
vm
.
$
nextTick
();
await
nextTick
();
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
after
:
pageInfo
.
endCursor
}),
expect
.
objectContaining
({
after
:
pageInfo
.
endCursor
}),
...
@@ -458,13 +465,12 @@ describe('List Page', () => {
...
@@ -458,13 +465,12 @@ describe('List Page', () => {
expect
(
findDeleteModal
().
exists
()).
toBe
(
true
);
expect
(
findDeleteModal
().
exists
()).
toBe
(
true
);
});
});
it
(
'
contains a description with the path of the item to delete
'
,
()
=>
{
it
(
'
contains a description with the path of the item to delete
'
,
async
()
=>
{
findImageList
().
vm
.
$emit
(
'
delete
'
,
{
path
:
'
foo
'
});
findImageList
().
vm
.
$emit
(
'
delete
'
,
{
path
:
'
foo
'
});
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
await
nextTick
();
expect
(
findDeleteModal
().
html
()).
toContain
(
'
foo
'
);
expect
(
findDeleteModal
().
html
()).
toContain
(
'
foo
'
);
});
});
});
});
});
describe
(
'
tracking
'
,
()
=>
{
describe
(
'
tracking
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
@@ -498,4 +504,60 @@ describe('List Page', () => {
...
@@ -498,4 +504,60 @@ describe('List Page', () => {
testTrackingCall
(
'
confirm_delete
'
);
testTrackingCall
(
'
confirm_delete
'
);
});
});
});
});
describe
(
'
url query string handling
'
,
()
=>
{
const
defaultQueryParams
=
{
search
:
[
1
,
2
],
sort
:
'
asc
'
,
orderBy
:
'
CREATED
'
,
};
const
queryChangePayload
=
'
foo
'
;
it
(
'
query:updated event pushes the new query to the router
'
,
async
()
=>
{
const
push
=
jest
.
fn
();
mountComponent
({
mocks
:
{
$router
:
{
push
}
}
});
await
nextTick
();
findRegistrySearch
().
vm
.
$emit
(
'
query:changed
'
,
queryChangePayload
);
expect
(
push
).
toHaveBeenCalledWith
({
query
:
queryChangePayload
});
});
it
(
'
graphql API call has the variables set from the URL
'
,
async
()
=>
{
const
resolver
=
jest
.
fn
().
mockResolvedValue
(
graphQLImageListMock
);
mountComponent
({
query
:
defaultQueryParams
,
resolver
});
await
nextTick
();
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
name
:
1
,
sort
:
'
CREATED_ASC
'
,
}),
);
});
it
.
each
`
sort | orderBy | search | payload
${
'
ASC
'
}
|
${
undefined
}
|
${
undefined
}
|
${{
sort
:
'
UPDATED_ASC
'
}
}
${
undefined
}
|
${
'
bar
'
}
|
${
undefined
}
|
${{
sort
:
'
BAR_DESC
'
}
}
${
'
ASC
'
}
|
${
'
bar
'
}
|
${
undefined
}
|
${{
sort
:
'
BAR_ASC
'
}
}
${
undefined
}
|
${
undefined
}
|
${
undefined
}
|
${{}}
$
{
undefined
}
|
${
undefined
}
|
${[
'
one
'
]}
|
${{
name
:
'
one
'
}
}
${
undefined
}
|
${
undefined
}
|
${[
'
one
'
,
'
two
'
]}
|
${{
name
:
'
one
'
}
}
${
undefined
}
|
${
'
UPDATED
'
}
|
${[
'
one
'
,
'
two
'
]}
|
${{
name
:
'
one
'
,
sort
:
'
UPDATED_DESC
'
}
}
${
'
ASC
'
}
|
${
'
UPDATED
'
}
|
${[
'
one
'
,
'
two
'
]}
|
${{
name
:
'
one
'
,
sort
:
'
UPDATED_ASC
'
}
}
`
(
'
with sort equal to $sort, orderBy equal to $orderBy, search set to $search API call has the variables set as $payload
'
,
async
({
sort
,
orderBy
,
search
,
payload
})
=>
{
const
resolver
=
jest
.
fn
().
mockResolvedValue
({
sort
,
orderBy
});
mountComponent
({
query
:
{
sort
,
orderBy
,
search
},
resolver
});
await
nextTick
();
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
(
payload
));
},
);
});
});
});
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