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
a797d196
Commit
a797d196
authored
Oct 25, 2021
by
Nicolò Maria Mezzopera
Committed by
Natalia Tepluhina
Oct 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor and connect package list for GraphQl implementation
parent
8cd2e168
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
413 additions
and
192 deletions
+413
-192
app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue
...s_and_registries/package_registry/components/list/app.vue
+51
-8
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
...stries/package_registry/components/list/packages_list.vue
+45
-40
app/assets/javascripts/packages_and_registries/package_registry/constants.js
...pts/packages_and_registries/package_registry/constants.js
+1
-6
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
...ckage_registry/graphql/queries/get_packages.query.graphql
+29
-2
app/assets/javascripts/packages_and_registries/package_registry/utils.js
...scripts/packages_and_registries/package_registry/utils.js
+3
-0
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/app_spec.js.snap
...e_registry/components/list/__snapshots__/app_spec.js.snap
+57
-0
spec/frontend/packages_and_registries/package_registry/components/list/app_spec.js
...d_registries/package_registry/components/list/app_spec.js
+87
-10
spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
...es/package_registry/components/list/packages_list_spec.js
+125
-124
spec/frontend/packages_and_registries/package_registry/mock_data.js
...end/packages_and_registries/package_registry/mock_data.js
+15
-2
No files found.
app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue
View file @
a797d196
...
...
@@ -4,7 +4,7 @@
* For a complete overview of the plan please check: https://gitlab.com/gitlab-org/gitlab/-/issues/330846
* This work is behind feature flag: https://gitlab.com/gitlab-org/gitlab/-/issues/341136
*/
//
import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import
{
GlEmptyState
,
GlLink
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
createFlash
from
'
~/flash
'
;
import
{
historyReplaceState
}
from
'
~/lib/utils/common_utils
'
;
import
{
s__
}
from
'
~/locale
'
;
...
...
@@ -15,17 +15,18 @@ import {
PROJECT_RESOURCE_TYPE
,
GROUP_RESOURCE_TYPE
,
LIST_QUERY_DEBOUNCE_TIME
,
GRAPHQL_PAGE_SIZE
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
PackageTitle
from
'
./package_title.vue
'
;
import
PackageSearch
from
'
./package_search.vue
'
;
//
import PackageList from './packages_list.vue';
import
PackageList
from
'
./packages_list.vue
'
;
export
default
{
components
:
{
//
GlEmptyState,
//
GlLink,
//
GlSprintf,
//
PackageList,
GlEmptyState
,
GlLink
,
GlSprintf
,
PackageList
,
PackageTitle
,
PackageSearch
,
},
...
...
@@ -64,17 +65,24 @@ export default {
groupSort
:
this
.
isGroupPage
?
this
.
sort
:
undefined
,
packageName
:
this
.
filters
?.
packageName
,
packageType
:
this
.
filters
?.
packageType
,
first
:
GRAPHQL_PAGE_SIZE
,
};
},
graphqlResource
()
{
return
this
.
isGroupPage
?
GROUP_RESOURCE_TYPE
:
PROJECT_RESOURCE_TYPE
;
},
pageInfo
()
{
return
this
.
packages
?.
pageInfo
??
{};
},
packagesCount
()
{
return
this
.
packages
?.
count
;
},
hasFilters
()
{
return
this
.
filters
.
packageName
&&
this
.
filters
.
packageType
;
},
emptySearch
()
{
return
!
this
.
filters
.
packageName
&&
!
this
.
filters
.
packageType
;
},
emptyStateTitle
()
{
return
this
.
emptySearch
?
this
.
$options
.
i18n
.
emptyPageTitle
...
...
@@ -99,6 +107,35 @@ export default {
this
.
sort
=
sort
;
this
.
filters
=
{
...
filters
};
},
updateQuery
(
_
,
{
fetchMoreResult
})
{
return
fetchMoreResult
;
},
fetchNextPage
()
{
const
variables
=
{
...
this
.
queryVariables
,
first
:
GRAPHQL_PAGE_SIZE
,
last
:
null
,
after
:
this
.
pageInfo
?.
endCursor
,
};
this
.
$apollo
.
queries
.
packages
.
fetchMore
({
variables
,
updateQuery
:
this
.
updateQuery
,
});
},
fetchPreviousPage
()
{
const
variables
=
{
...
this
.
queryVariables
,
first
:
null
,
last
:
GRAPHQL_PAGE_SIZE
,
before
:
this
.
pageInfo
?.
startCursor
,
};
this
.
$apollo
.
queries
.
packages
.
fetchMore
({
variables
,
updateQuery
:
this
.
updateQuery
,
});
},
},
i18n
:
{
widenFilters
:
s__
(
'
PackageRegistry|To widen your search, change or remove the filters above.
'
),
...
...
@@ -116,7 +153,13 @@ export default {
<package-title
:help-url=
"packageHelpUrl"
:count=
"packagesCount"
/>
<package-search
@
update=
"handleSearchUpdate"
/>
<!--
<package-list
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<package-list
:list=
"packages.nodes"
:is-loading=
"$apollo.queries.packages.loading"
:page-info=
"pageInfo"
@
prev-page=
"fetchPreviousPage"
@
next-page=
"fetchNextPage"
>
<template
#empty-state
>
<gl-empty-state
:title=
"emptyStateTitle"
:svg-path=
"emptyListIllustration"
>
<template
#description
>
...
...
@@ -129,6 +172,6 @@ export default {
</template>
</gl-empty-state>
</template>
</package-list>
-->
</package-list>
</div>
</template>
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
View file @
a797d196
<
script
>
import
{
GlPagination
,
GlModal
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapGetters
}
from
'
vuex
'
;
import
{
GlModal
,
GlSprintf
,
GlKeysetPagination
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
PackagesListRow
from
'
~/packages
/shared/components
/package_list_row.vue
'
;
import
PackagesListRow
from
'
~/packages
_and_registries/package_registry/components/list
/package_list_row.vue
'
;
import
PackagesListLoader
from
'
~/packages/shared/components/packages_list_loader.vue
'
;
import
{
TrackingActions
}
from
'
~/packages/shared/constants
'
;
import
{
packageTypeToTrackCategory
}
from
'
~/packages/shared/utils
'
;
import
{
DELETE_PACKAGE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
{
packageTypeToTrackCategory
}
from
'
~/packages_and_registries/package_registry/utils
'
;
import
Tracking
from
'
~/tracking
'
;
export
default
{
components
:
{
GlPagination
,
Gl
Keyset
Pagination
,
GlModal
,
GlSprintf
,
PackagesListLoader
,
PackagesListRow
,
},
mixins
:
[
Tracking
.
mixin
()],
props
:
{
list
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
isLoading
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
pageInfo
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
itemToBeDeleted
:
null
,
};
},
computed
:
{
...
mapState
({
perPage
:
(
state
)
=>
state
.
pagination
.
perPage
,
totalItems
:
(
state
)
=>
state
.
pagination
.
total
,
page
:
(
state
)
=>
state
.
pagination
.
page
,
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
isLoading
:
'
isLoading
'
,
}),
...
mapGetters
({
list
:
'
getList
'
}),
currentPage
:
{
get
()
{
return
this
.
page
;
},
set
(
value
)
{
this
.
$emit
(
'
page:changed
'
,
value
);
},
},
isListEmpty
()
{
return
!
this
.
list
||
this
.
list
.
length
===
0
;
},
modalAction
()
{
return
s__
(
'
PackageRegistry|Delete package
'
);
},
deletePackageName
()
{
return
this
.
itemToBeDeleted
?.
name
??
''
;
},
tracking
()
{
const
category
=
this
.
itemToBeDeleted
?
packageTypeToTrackCategory
(
this
.
itemToBeDeleted
.
package
_t
ype
)
?
packageTypeToTrackCategory
(
this
.
itemToBeDeleted
.
package
T
ype
)
:
undefined
;
return
{
category
,
};
},
showPagination
()
{
return
this
.
pageInfo
.
hasPreviousPage
||
this
.
pageInfo
.
hasNextPage
;
},
},
methods
:
{
setItemToBeDeleted
(
item
)
{
this
.
itemToBeDeleted
=
{
...
item
};
this
.
track
(
TrackingActions
.
REQUEST_DELETE_PACKAGE
);
this
.
track
(
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
);
this
.
$refs
.
packageListDeleteModal
.
show
();
},
deleteItemConfirmation
()
{
this
.
$emit
(
'
package:delete
'
,
this
.
itemToBeDeleted
);
this
.
track
(
TrackingActions
.
DELETE_PACKAGE
);
this
.
track
(
DELETE_PACKAGE_TRACKING_ACTION
);
this
.
itemToBeDeleted
=
null
;
},
deleteItemCanceled
()
{
this
.
track
(
TrackingActions
.
CANCEL_DELETE_PACKAGE
);
this
.
track
(
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
);
this
.
itemToBeDeleted
=
null
;
},
},
...
...
@@ -77,6 +81,7 @@ export default {
deleteModalContent
:
s__
(
'
PackageRegistry|You are about to delete %{name}, this operation is irreversible, are you sure?
'
,
),
modalAction
:
s__
(
'
PackageRegistry|Delete package
'
),
},
};
</
script
>
...
...
@@ -95,19 +100,19 @@ export default {
v-for=
"packageEntity in list"
:key=
"packageEntity.id"
:package-entity=
"packageEntity"
:package-link=
"packageEntity._links.web_path"
:is-group=
"isGroupPage"
@
packageToDelete=
"setItemToBeDeleted"
/>
</div>
<gl-pagination
v-model=
"currentPage"
:per-page=
"perPage"
:total-items=
"totalItems"
align=
"center"
class=
"gl-w-full gl-mt-3"
/>
<div
class=
"gl-display-flex gl-justify-content-center"
>
<gl-keyset-pagination
v-if=
"showPagination"
v-bind=
"pageInfo"
class=
"gl-mt-3"
@
prev=
"$emit('prev-page')"
@
next=
"$emit('next-page')"
/>
</div>
<gl-modal
ref=
"packageListDeleteModal"
...
...
@@ -116,8 +121,8 @@ export default {
@
ok=
"deleteItemConfirmation"
@
cancel=
"deleteItemCanceled"
>
<template
#modal-title
>
{{
modalAction
}}
</
template
>
<
template
#modal-ok
>
{{
modalAction
}}
</
template
>
<template
#modal-title
>
{{
$options
.
i18n
.
modalAction
}}
</
template
>
<
template
#modal-ok
>
{{
$options
.
i18n
.
modalAction
}}
</
template
>
<gl-sprintf
:message=
"$options.i18n.deleteModalContent"
>
<
template
#name
>
<strong>
{{
deletePackageName
}}
</strong>
...
...
app/assets/javascripts/packages_and_registries/package_registry/constants.js
View file @
a797d196
...
...
@@ -59,12 +59,6 @@ export const TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND =
export
const
TRACKING_ACTION_COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND
=
'
copy_composer_package_include_command
'
;
export
const
TrackingCategories
=
{
[
PACKAGE_TYPE_MAVEN
]:
'
MavenPackages
'
,
[
PACKAGE_TYPE_NPM
]:
'
NpmPackages
'
,
[
PACKAGE_TYPE_CONAN
]:
'
ConanPackages
'
,
};
export
const
SHOW_DELETE_SUCCESS_ALERT
=
'
showSuccessDeleteAlert
'
;
export
const
DELETE_PACKAGE_ERROR_MESSAGE
=
s__
(
'
PackageRegistry|Something went wrong while deleting the package.
'
,
...
...
@@ -93,3 +87,4 @@ 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
;
export
const
GRAPHQL_PAGE_SIZE
=
20
;
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
View file @
a797d196
#import "~/packages_and_registries/package_registry/graphql/fragments/package_data.fragment.graphql"
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query
getPackages
(
$fullPath
:
ID
!
...
...
@@ -7,21 +8,47 @@ query getPackages(
$groupSort
:
PackageGroupSort
$packageName
:
String
$packageType
:
PackageTypeEnum
$first
:
Int
$last
:
Int
$after
:
String
$before
:
String
)
{
project
(
fullPath
:
$fullPath
)
@skip
(
if
:
$isGroupPage
)
{
packages
(
sort
:
$sort
,
packageName
:
$packageName
,
packageType
:
$packageType
)
{
packages
(
sort
:
$sort
packageName
:
$packageName
packageType
:
$packageType
after
:
$after
before
:
$before
first
:
$first
last
:
$last
)
{
count
nodes
{
...
PackageData
}
pageInfo
{
...
PageInfo
}
}
}
group
(
fullPath
:
$fullPath
)
@include
(
if
:
$isGroupPage
)
{
packages
(
sort
:
$groupSort
,
packageName
:
$packageName
,
packageType
:
$packageType
)
{
packages
(
sort
:
$groupSort
packageName
:
$packageName
packageType
:
$packageType
after
:
$after
before
:
$before
first
:
$first
last
:
$last
)
{
count
nodes
{
...
PackageData
}
pageInfo
{
...
PageInfo
}
}
}
}
app/assets/javascripts/packages_and_registries/package_registry/utils.js
View file @
a797d196
import
{
capitalize
}
from
'
lodash
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
PACKAGE_TYPE_CONAN
,
...
...
@@ -38,3 +39,5 @@ export const getPackageTypeLabel = (packageType) => {
return
null
;
}
};
export
const
packageTypeToTrackCategory
=
(
type
)
=>
`UI::
${
capitalize
(
type
)}
Packages`
;
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/app_spec.js.snap
View file @
a797d196
...
...
@@ -8,5 +8,62 @@ exports[`PackagesListApp renders 1`] = `
/>
<package-search-stub />
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
role="img"
src="emptyListIllustration"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="emptyListHelpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div
class="gl-display-flex gl-flex-wrap gl-justify-content-center"
>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</div>
`;
spec/frontend/packages_and_registries/package_registry/components/list/app_spec.js
View file @
a797d196
...
...
@@ -2,22 +2,25 @@ import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
import
{
createLocalVue
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
{
nextTick
}
from
'
vue
'
;
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
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
OriginalPackageList
from
'
~/packages_and_registries/package_registry/components/list/packages_list.vue
'
;
import
{
PROJECT_RESOURCE_TYPE
,
GROUP_RESOURCE_TYPE
,
LIST_QUERY_DEBOUNCE_TIME
,
GRAPHQL_PAGE_SIZE
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
getPackagesQuery
from
'
~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql
'
;
import
{
packagesListQuery
}
from
'
../../mock_data
'
;
import
{
packagesListQuery
,
packageData
,
pagination
}
from
'
../../mock_data
'
;
jest
.
mock
(
'
~/lib/utils/common_utils
'
);
jest
.
mock
(
'
~/flash
'
);
...
...
@@ -39,11 +42,19 @@ describe('PackagesListApp', () => {
const
PackageList
=
{
name
:
'
package-list
'
,
template
:
'
<div><slot name="empty-state"></slot></div>
'
,
props
:
OriginalPackageList
.
props
,
};
const
GlLoadingIcon
=
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
};
const
searchPayload
=
{
sort
:
'
VERSION_DESC
'
,
filters
:
{
packageName
:
'
foo
'
,
packageType
:
'
CONAN
'
},
};
const
findPackageTitle
=
()
=>
wrapper
.
findComponent
(
PackageTitle
);
const
findSearch
=
()
=>
wrapper
.
findComponent
(
PackageSearch
);
const
findListComponent
=
()
=>
wrapper
.
findComponent
(
PackageList
);
const
findEmptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
const
mountComponent
=
({
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
()),
...
...
@@ -105,25 +116,55 @@ describe('PackagesListApp', () => {
const
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
());
mountComponent
({
resolver
});
const
payload
=
{
sort
:
'
VERSION_DESC
'
,
filters
:
{
packageName
:
'
foo
'
,
packageType
:
'
CONAN
'
},
};
findSearch
().
vm
.
$emit
(
'
update
'
,
payload
);
findSearch
().
vm
.
$emit
(
'
update
'
,
searchPayload
);
await
waitForDebouncedApollo
();
jest
.
advanceTimersByTime
(
LIST_QUERY_DEBOUNCE_TIME
);
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
groupSort
:
p
ayload
.
sort
,
...
p
ayload
.
filters
,
groupSort
:
searchP
ayload
.
sort
,
...
searchP
ayload
.
filters
,
}),
);
});
});
describe
(
'
list component
'
,
()
=>
{
let
resolver
;
beforeEach
(()
=>
{
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
());
mountComponent
({
resolver
});
return
waitForDebouncedApollo
();
});
it
(
'
exists and has the right props
'
,
()
=>
{
expect
(
findListComponent
().
props
()).
toMatchObject
({
list
:
expect
.
arrayContaining
([
expect
.
objectContaining
({
id
:
packageData
().
id
})]),
isLoading
:
false
,
pageInfo
:
expect
.
objectContaining
({
endCursor
:
pagination
().
endCursor
}),
});
});
it
(
'
when list emits next-page fetches the next set of records
'
,
()
=>
{
findListComponent
().
vm
.
$emit
(
'
next-page
'
);
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
after
:
pagination
().
endCursor
,
first
:
GRAPHQL_PAGE_SIZE
}),
);
});
it
(
'
when list emits prev-page fetches the prev set of records
'
,
()
=>
{
findListComponent
().
vm
.
$emit
(
'
prev-page
'
);
expect
(
resolver
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
before
:
pagination
().
startCursor
,
last
:
GRAPHQL_PAGE_SIZE
}),
);
});
});
describe
.
each
`
type | sortType
${
PROJECT_RESOURCE_TYPE
}
|
${
'
sort
'
}
...
...
@@ -136,7 +177,7 @@ describe('PackagesListApp', () => {
beforeEach
(()
=>
{
provide
=
{
...
defaultProvide
,
isGroupPage
};
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
(
type
));
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
(
{
type
}
));
mountComponent
({
provide
,
resolver
});
return
waitForDebouncedApollo
();
});
...
...
@@ -151,4 +192,40 @@ describe('PackagesListApp', () => {
);
});
});
describe
(
'
empty state
'
,
()
=>
{
beforeEach
(()
=>
{
const
resolver
=
jest
.
fn
().
mockResolvedValue
(
packagesListQuery
({
extend
:
{
nodes
:
[]
}
}));
mountComponent
({
resolver
});
return
waitForDebouncedApollo
();
});
it
(
'
generate the correct empty list link
'
,
()
=>
{
const
link
=
findListComponent
().
findComponent
(
GlLink
);
expect
(
link
.
attributes
(
'
href
'
)).
toBe
(
defaultProvide
.
emptyListHelpUrl
);
expect
(
link
.
text
()).
toBe
(
'
publish and share your packages
'
);
});
it
(
'
includes the right content on the default tab
'
,
()
=>
{
expect
(
findEmptyState
().
text
()).
toContain
(
PackageListApp
.
i18n
.
emptyPageTitle
);
});
});
describe
(
'
filter without results
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mountComponent
();
await
waitForDebouncedApollo
();
findSearch
().
vm
.
$emit
(
'
update
'
,
searchPayload
);
return
nextTick
();
});
it
(
'
should show specific empty message
'
,
()
=>
{
expect
(
findEmptyState
().
text
()).
toContain
(
PackageListApp
.
i18n
.
noResultsTitle
);
expect
(
findEmptyState
().
text
()).
toContain
(
PackageListApp
.
i18n
.
widenFilters
);
});
});
});
spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
View file @
a797d196
import
{
GlTable
,
GlPagination
,
GlModal
}
from
'
@gitlab/ui
'
;
import
{
mount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
last
}
from
'
lodash
'
;
import
Vuex
from
'
vuex
'
;
import
stubChildren
from
'
helpers/stub_children
'
;
import
{
packageList
}
from
'
jest/packages/mock_data
'
;
import
{
GlKeysetPagination
,
GlModal
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
nextTick
}
from
'
vue
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
PackagesListRow
from
'
~/packages/shared/components/package_list_row.vue
'
;
import
PackagesListLoader
from
'
~/packages/shared/components/packages_list_loader.vue
'
;
import
{
TrackingActions
}
from
'
~/packages/shared/constants
'
;
import
*
as
SharedUtils
from
'
~/packages/shared/utils
'
;
import
{
DELETE_PACKAGE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
PackagesList
from
'
~/packages_and_registries/package_registry/components/list/packages_list.vue
'
;
import
Tracking
from
'
~/tracking
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
import
{
packageData
}
from
'
../../mock_data
'
;
describe
(
'
packages_list
'
,
()
=>
{
let
wrapper
;
let
store
;
const
firstPackage
=
packageData
();
const
secondPackage
=
{
...
packageData
(),
id
:
'
gid://gitlab/Packages::Package/112
'
,
name
:
'
second-package
'
,
};
const
defaultProps
=
{
list
:
[
firstPackage
,
secondPackage
],
isLoading
:
false
,
pageInfo
:
{},
};
const
EmptySlotStub
=
{
name
:
'
empty-slot-stub
'
,
template
:
'
<div>bar</div>
'
};
const
GlModalStub
=
{
name
:
GlModal
.
name
,
template
:
'
<div><slot></slot></div>
'
,
methods
:
{
show
:
jest
.
fn
()
},
};
const
findPackagesListLoader
=
()
=>
wrapper
.
find
(
PackagesListLoader
);
const
findPackageListPagination
=
()
=>
wrapper
.
find
(
GlPagination
);
const
findPackageListDeleteModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
findEmptySlot
=
()
=>
wrapper
.
find
(
EmptySlotStub
);
const
findPackagesListRow
=
()
=>
wrapper
.
find
(
PackagesListRow
);
const
createStore
=
(
isGroupPage
,
packages
,
isLoading
)
=>
{
const
state
=
{
isLoading
,
packages
,
pagination
:
{
perPage
:
1
,
total
:
1
,
page
:
1
,
},
config
:
{
isGroupPage
,
const
findPackagesListLoader
=
()
=>
wrapper
.
findComponent
(
PackagesListLoader
);
const
findPackageListPagination
=
()
=>
wrapper
.
findComponent
(
GlKeysetPagination
);
const
findPackageListDeleteModal
=
()
=>
wrapper
.
findComponent
(
GlModalStub
);
const
findEmptySlot
=
()
=>
wrapper
.
findComponent
(
EmptySlotStub
);
const
findPackagesListRow
=
()
=>
wrapper
.
findComponent
(
PackagesListRow
);
const
mountComponent
=
(
props
)
=>
{
wrapper
=
shallowMountExtended
(
PackagesList
,
{
propsData
:
{
...
defaultProps
,
...
props
,
},
s
orting
:
{
orderBy
:
'
version
'
,
sort
:
'
desc
'
,
s
tubs
:
{
GlModal
:
GlModalStub
,
GlSprintf
,
},
};
store
=
new
Vuex
.
Store
({
state
,
getters
:
{
getList
:
()
=>
packages
,
slots
:
{
'
empty-state
'
:
EmptySlotStub
,
},
});
store
.
dispatch
=
jest
.
fn
();
};
const
mountComponent
=
({
isGroupPage
=
false
,
packages
=
packageList
,
isLoading
=
false
,
...
options
}
=
{})
=>
{
createStore
(
isGroupPage
,
packages
,
isLoading
);
wrapper
=
mount
(
PackagesList
,
{
localVue
,
store
,
stubs
:
{
...
stubChildren
(
PackagesList
),
GlTable
,
GlModal
,
},
...
options
,
});
};
beforeEach
(()
=>
{
GlModalStub
.
methods
.
show
.
mockReset
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
when is loading
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
packages
:
[],
isLoading
:
true
,
});
mountComponent
({
isLoading
:
true
});
});
it
(
'
shows skeleton loader
when loading
'
,
()
=>
{
it
(
'
shows skeleton loader
'
,
()
=>
{
expect
(
findPackagesListLoader
().
exists
()).
toBe
(
true
);
});
it
(
'
does not show the rows
'
,
()
=>
{
expect
(
findPackagesListRow
().
exists
()).
toBe
(
false
);
});
it
(
'
does not show the pagination
'
,
()
=>
{
expect
(
findPackageListPagination
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when is not loading
'
,
()
=>
{
...
...
@@ -95,74 +88,68 @@ describe('packages_list', () => {
mountComponent
();
});
it
(
'
does not show skeleton loader
when not loading
'
,
()
=>
{
it
(
'
does not show skeleton loader
'
,
()
=>
{
expect
(
findPackagesListLoader
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
layout
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
it
(
'
shows the rows
'
,
()
=>
{
expect
(
findPackagesListRow
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
layout
'
,
()
=>
{
it
(
'
contains a pagination component
'
,
()
=>
{
const
sorting
=
findPackageListPagination
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
mountComponent
({
pageInfo
:
{
hasPreviousPage
:
true
}
});
expect
(
findPackageListPagination
().
exists
()).
toBe
(
true
);
});
it
(
'
contains a modal component
'
,
()
=>
{
const
sorting
=
findPackageListDeleteModal
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
mountComponent
();
expect
(
findPackageListDeleteModal
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
when the user can destroy the package
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
findPackagesListRow
().
vm
.
$emit
(
'
packageToDelete
'
,
firstPackage
);
return
nextTick
();
});
it
(
'
setItemToBeDeleted sets itemToBeDeleted and open
the modal
'
,
()
=>
{
const
mockModalShow
=
jest
.
spyOn
(
wrapper
.
vm
.
$refs
.
packageListDeleteModal
,
'
show
'
);
const
item
=
last
(
wrapper
.
vm
.
list
);
it
(
'
deleting a package opens
the modal
'
,
()
=>
{
expect
(
findPackageListDeleteModal
().
text
()).
toContain
(
firstPackage
.
name
);
}
);
findPackagesListRow
().
vm
.
$emit
(
'
packageToDelete
'
,
item
);
it
(
'
confirming delete empties itemsToBeDeleted
'
,
async
()
=>
{
findPackageListDeleteModal
().
vm
.
$emit
(
'
ok
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
item
);
expect
(
mockModalShow
).
toHaveBeenCalled
();
});
});
await
nextTick
();
it
(
'
deleteItemConfirmation resets itemToBeDeleted
'
,
()
=>
{
wrapper
.
setData
({
itemToBeDeleted
:
1
});
wrapper
.
vm
.
deleteItemConfirmation
();
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
null
);
expect
(
findPackageListDeleteModal
().
text
()).
not
.
toContain
(
firstPackage
.
name
);
});
it
(
'
deleteItemConfirmation emit package:delete
'
,
()
=>
{
const
itemToBeDeleted
=
{
id
:
2
};
wrapper
.
setData
({
itemToBeDeleted
});
wrapper
.
vm
.
deleteItemConfirmation
();
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
emitted
(
'
package:delete
'
)[
0
]).
toEqual
([
itemToBeDeleted
]);
});
it
(
'
confirming on the modal emits package:delete
'
,
async
()
=>
{
findPackageListDeleteModal
().
vm
.
$emit
(
'
ok
'
);
await
nextTick
();
expect
(
wrapper
.
emitted
(
'
package:delete
'
)[
0
]).
toEqual
([
firstPackage
]);
});
it
(
'
deleteItemCanceled resets itemToBeDeleted
'
,
()
=>
{
wrapper
.
setData
({
itemToBeDeleted
:
1
});
wrapper
.
vm
.
deleteItemCanceled
();
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
null
);
it
(
'
cancel event resets itemToBeDeleted
'
,
async
()
=>
{
findPackageListDeleteModal
().
vm
.
$emit
(
'
cancel
'
);
await
nextTick
();
expect
(
findPackageListDeleteModal
().
text
()).
not
.
toContain
(
firstPackage
.
name
);
});
});
describe
(
'
when the list is empty
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
packages
:
[],
slots
:
{
'
empty-state
'
:
EmptySlotStub
,
},
});
mountComponent
({
list
:
[]
});
});
it
(
'
show the empty slot
'
,
()
=>
{
...
...
@@ -171,45 +158,59 @@ describe('packages_list', () => {
});
});
describe
(
'
pagination component
'
,
()
=>
{
let
pagination
;
let
modelEvent
;
describe
(
'
pagination
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
pagination
=
findPackageListPagination
();
// retrieve the event used by v-model, a more sturdy approach than hardcoding it
modelEvent
=
pagination
.
vm
.
$options
.
model
.
event
;
mountComponent
({
pageInfo
:
{
hasPreviousPage
:
true
}
});
});
it
(
'
emits prev-page events when the prev event is fired
'
,
()
=>
{
findPackageListPagination
().
vm
.
$emit
(
'
prev
'
);
expect
(
wrapper
.
emitted
(
'
prev-page
'
)).
toEqual
([[]]);
});
it
(
'
emits page:changed events when the page changes
'
,
()
=>
{
pagination
.
vm
.
$emit
(
modelEvent
,
2
);
expect
(
wrapper
.
emitted
(
'
page:changed
'
)).
toEqual
([[
2
]]);
it
(
'
emits next-page events when the next event is fired
'
,
()
=>
{
findPackageListPagination
().
vm
.
$emit
(
'
next
'
);
expect
(
wrapper
.
emitted
(
'
next-page
'
)).
toEqual
([[]]);
});
});
describe
(
'
tracking
'
,
()
=>
{
let
eventSpy
;
let
utilSpy
;
const
category
=
'
foo
'
;
const
category
=
'
UI::NpmPackages
'
;
beforeEach
(()
=>
{
mountComponent
();
eventSpy
=
jest
.
spyOn
(
Tracking
,
'
event
'
);
utilSpy
=
jest
.
spyOn
(
SharedUtils
,
'
packageTypeToTrackCategory
'
).
mockReturnValue
(
category
);
wrapper
.
setData
({
itemToBeDeleted
:
{
package_type
:
'
conan
'
}
});
mountComponent
();
findPackagesListRow
().
vm
.
$emit
(
'
packageToDelete
'
,
firstPackage
);
return
nextTick
();
});
it
(
'
tracking category calls packageTypeToTrackCategory
'
,
()
=>
{
expect
(
wrapper
.
vm
.
tracking
.
category
).
toBe
(
category
);
expect
(
utilSpy
).
toHaveBeenCalledWith
(
'
conan
'
);
it
(
'
requesting the delete tracks the right action
'
,
()
=>
{
expect
(
eventSpy
).
toHaveBeenCalledWith
(
category
,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
,
expect
.
any
(
Object
),
);
});
it
(
'
deleteItemConfirmation calls event
'
,
()
=>
{
wrapper
.
vm
.
deleteItemConfirmation
();
it
(
'
confirming delete tracks the right action
'
,
()
=>
{
findPackageListDeleteModal
().
vm
.
$emit
(
'
ok
'
);
expect
(
eventSpy
).
toHaveBeenCalledWith
(
category
,
DELETE_PACKAGE_TRACKING_ACTION
,
expect
.
any
(
Object
),
);
});
it
(
'
canceling delete tracks the right action
'
,
()
=>
{
findPackageListDeleteModal
().
vm
.
$emit
(
'
cancel
'
);
expect
(
eventSpy
).
toHaveBeenCalledWith
(
category
,
TrackingActions
.
DELETE_PACKAGE
,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
,
expect
.
any
(
Object
),
);
});
...
...
spec/frontend/packages_and_registries/package_registry/mock_data.js
View file @
a797d196
import
capitalize
from
'
lodash/capitalize
'
;
export
const
packageTags
=
()
=>
[
{
id
:
'
gid://gitlab/Packages::Tag/87
'
,
name
:
'
bananas_9
'
,
__typename
:
'
PackageTag
'
},
{
id
:
'
gid://gitlab/Packages::Tag/86
'
,
name
:
'
bananas_8
'
,
__typename
:
'
PackageTag
'
},
...
...
@@ -156,6 +158,15 @@ export const nugetMetadata = () => ({
projectUrl
:
'
projectUrl
'
,
});
export
const
pagination
=
(
extend
)
=>
({
endCursor
:
'
eyJpZCI6IjIwNSIsIm5hbWUiOiJteS9jb21wYW55L2FwcC9teS1hcHAifQ
'
,
hasNextPage
:
true
,
hasPreviousPage
:
true
,
startCursor
:
'
eyJpZCI6IjI0NyIsIm5hbWUiOiJ2ZXJzaW9uX3Rlc3QxIn0
'
,
__typename
:
'
PageInfo
'
,
...
extend
,
});
export
const
packageDetailsQuery
=
(
extendPackage
)
=>
({
data
:
{
package
:
{
...
...
@@ -256,7 +267,7 @@ export const packageDestroyFileMutationError = () => ({
],
});
export
const
packagesListQuery
=
(
type
=
'
group
'
)
=>
({
export
const
packagesListQuery
=
(
{
type
=
'
group
'
,
extend
=
{},
extendPagination
=
{}
}
=
{}
)
=>
({
data
:
{
[
type
]:
{
packages
:
{
...
...
@@ -277,9 +288,11 @@ export const packagesListQuery = (type = 'group') => ({
pipelines
:
{
nodes
:
[]
},
},
],
pageInfo
:
pagination
(
extendPagination
),
__typename
:
'
PackageConnection
'
,
},
__typename
:
'
Group
'
,
...
extend
,
__typename
:
capitalize
(
type
),
},
},
});
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