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
6dd10023
Commit
6dd10023
authored
Sep 15, 2021
by
Nicolò Maria Mezzopera
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Copy package list components to new folder
- source - test - snapshots
parent
a75471dd
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1212 additions
and
0 deletions
+1212
-0
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
...tries/package_registry/components/list/package_search.vue
+57
-0
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_title.vue
...stries/package_registry/components/list/package_title.vue
+47
-0
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
...stries/package_registry/components/list/packages_list.vue
+129
-0
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list_app.vue
...es/package_registry/components/list/packages_list_app.vue
+132
-0
app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue
...ge_registry/components/list/tokens/package_type_token.vue
+26
-0
app/assets/javascripts/packages_and_registries/package_registry/pages/list.js
...ts/packages_and_registries/package_registry/pages/list.js
+16
-0
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/packages_list_app_spec.js.snap
...ponents/list/__snapshots__/packages_list_app_spec.js.snap
+68
-0
spec/frontend/packages_and_registries/package_registry/components/list/packages_list_app_spec.js
...ackage_registry/components/list/packages_list_app_spec.js
+273
-0
spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
...es/package_registry/components/list/packages_list_spec.js
+217
-0
spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
.../package_registry/components/list/packages_search_spec.js
+128
-0
spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js
...s/package_registry/components/list/packages_title_spec.js
+71
-0
spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
...egistry/components/list/tokens/package_type_token_spec.js
+48
-0
No files found.
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_search.vue
0 → 100644
View file @
6dd10023
<
script
>
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
import
{
OPERATOR_IS_ONLY
}
from
'
~/vue_shared/components/filtered_search_bar/constants
'
;
import
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
import
PackageTypeToken
from
'
./tokens/package_type_token.vue
'
;
export
default
{
tokens
:
[
{
type
:
'
type
'
,
icon
:
'
package
'
,
title
:
s__
(
'
PackageRegistry|Type
'
),
unique
:
true
,
token
:
PackageTypeToken
,
operators
:
OPERATOR_IS_ONLY
,
},
],
components
:
{
RegistrySearch
,
UrlSync
},
computed
:
{
...
mapState
({
isGroupPage
:
(
state
)
=>
state
.
config
.
isGroupPage
,
sorting
:
(
state
)
=>
state
.
sorting
,
filter
:
(
state
)
=>
state
.
filter
,
}),
sortableFields
()
{
return
sortableFields
(
this
.
isGroupPage
);
},
},
methods
:
{
...
mapActions
([
'
setSorting
'
,
'
setFilter
'
]),
updateSorting
(
newValue
)
{
this
.
setSorting
(
newValue
);
this
.
$emit
(
'
update
'
);
},
},
};
</
script
>
<
template
>
<url-sync>
<template
#default
="
{ updateQuery }">
<registry-search
:filter=
"filter"
:sorting=
"sorting"
:tokens=
"$options.tokens"
:sortable-fields=
"sortableFields"
@
sorting:changed=
"updateSorting"
@
filter:changed=
"setFilter"
@
filter:submit=
"$emit('update')"
@
query:changed=
"updateQuery"
/>
</
template
>
</url-sync>
</template>
app/assets/javascripts/packages_and_registries/package_registry/components/list/package_title.vue
0 → 100644
View file @
6dd10023
<
script
>
import
{
n__
}
from
'
~/locale
'
;
import
{
LIST_INTRO_TEXT
,
LIST_TITLE_TEXT
}
from
'
~/packages/list/constants
'
;
import
MetadataItem
from
'
~/vue_shared/components/registry/metadata_item.vue
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
export
default
{
name
:
'
PackageTitle
'
,
components
:
{
TitleArea
,
MetadataItem
,
},
props
:
{
count
:
{
type
:
Number
,
required
:
false
,
default
:
null
,
},
helpUrl
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
showPackageCount
()
{
return
Number
.
isInteger
(
this
.
count
);
},
packageAmountText
()
{
return
n__
(
`%d Package`
,
`%d Packages`
,
this
.
count
);
},
infoMessages
()
{
return
[{
text
:
LIST_INTRO_TEXT
,
link
:
this
.
helpUrl
}];
},
},
i18n
:
{
LIST_TITLE_TEXT
,
},
};
</
script
>
<
template
>
<title-area
:title=
"$options.i18n.LIST_TITLE_TEXT"
:info-messages=
"infoMessages"
>
<template
#metadata-amount
>
<metadata-item
v-if=
"showPackageCount"
icon=
"package"
:text=
"packageAmountText"
/>
</
template
>
</title-area>
</template>
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
0 → 100644
View file @
6dd10023
<
script
>
import
{
GlPagination
,
GlModal
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapGetters
}
from
'
vuex
'
;
import
{
s__
}
from
'
~/locale
'
;
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
{
packageTypeToTrackCategory
}
from
'
~/packages/shared/utils
'
;
import
Tracking
from
'
~/tracking
'
;
export
default
{
components
:
{
GlPagination
,
GlModal
,
GlSprintf
,
PackagesListLoader
,
PackagesListRow
,
},
mixins
:
[
Tracking
.
mixin
()],
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_type
)
:
undefined
;
return
{
category
,
};
},
},
methods
:
{
setItemToBeDeleted
(
item
)
{
this
.
itemToBeDeleted
=
{
...
item
};
this
.
track
(
TrackingActions
.
REQUEST_DELETE_PACKAGE
);
this
.
$refs
.
packageListDeleteModal
.
show
();
},
deleteItemConfirmation
()
{
this
.
$emit
(
'
package:delete
'
,
this
.
itemToBeDeleted
);
this
.
track
(
TrackingActions
.
DELETE_PACKAGE
);
this
.
itemToBeDeleted
=
null
;
},
deleteItemCanceled
()
{
this
.
track
(
TrackingActions
.
CANCEL_DELETE_PACKAGE
);
this
.
itemToBeDeleted
=
null
;
},
},
i18n
:
{
deleteModalContent
:
s__
(
'
PackageRegistry|You are about to delete %{name}, this operation is irreversible, are you sure?
'
,
),
},
};
</
script
>
<
template
>
<div
class=
"gl-display-flex gl-flex-direction-column"
>
<slot
v-if=
"isListEmpty && !isLoading"
name=
"empty-state"
></slot>
<div
v-else-if=
"isLoading"
>
<packages-list-loader
/>
</div>
<template
v-else
>
<div
data-qa-selector=
"packages-table"
>
<packages-list-row
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"
/>
<gl-modal
ref=
"packageListDeleteModal"
modal-id=
"confirm-delete-pacakge"
ok-variant=
"danger"
@
ok=
"deleteItemConfirmation"
@
cancel=
"deleteItemCanceled"
>
<template
#modal-title
>
{{
modalAction
}}
</
template
>
<
template
#modal-ok
>
{{
modalAction
}}
</
template
>
<gl-sprintf
:message=
"$options.i18n.deleteModalContent"
>
<
template
#name
>
<strong>
{{
deletePackageName
}}
</strong>
</
template
>
</gl-sprintf>
</gl-modal>
</template>
</div>
</template>
app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list_app.vue
0 → 100644
View file @
6dd10023
<
script
>
import
{
GlEmptyState
,
GlLink
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
mapActions
,
mapState
}
from
'
vuex
'
;
import
createFlash
from
'
~/flash
'
;
import
{
historyReplaceState
}
from
'
~/lib/utils/common_utils
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
~/packages/list/constants
'
;
import
{
SHOW_DELETE_SUCCESS_ALERT
}
from
'
~/packages/shared/constants
'
;
import
{
FILTERED_SEARCH_TERM
}
from
'
~/packages_and_registries/shared/constants
'
;
import
{
getQueryParams
,
extractFilterAndSorting
}
from
'
~/packages_and_registries/shared/utils
'
;
import
PackageList
from
'
./packages_list.vue
'
;
export
default
{
components
:
{
GlEmptyState
,
GlLink
,
GlSprintf
,
PackageList
,
PackageTitle
:
()
=>
import
(
/* webpackChunkName: 'package_registry_components' */
'
./package_title.vue
'
),
PackageSearch
:
()
=>
import
(
/* webpackChunkName: 'package_registry_components' */
'
./package_search.vue
'
),
InfrastructureTitle
:
()
=>
import
(
/* webpackChunkName: 'infrastructure_registry_components' */
'
~/packages_and_registries/infrastructure_registry/components/infrastructure_title.vue
'
),
InfrastructureSearch
:
()
=>
import
(
/* webpackChunkName: 'infrastructure_registry_components' */
'
~/packages_and_registries/infrastructure_registry/components/infrastructure_search.vue
'
),
},
inject
:
{
titleComponent
:
{
from
:
'
titleComponent
'
,
default
:
'
PackageTitle
'
,
},
searchComponent
:
{
from
:
'
searchComponent
'
,
default
:
'
PackageSearch
'
,
},
emptyPageTitle
:
{
from
:
'
emptyPageTitle
'
,
default
:
s__
(
'
PackageRegistry|There are no packages yet
'
),
},
noResultsText
:
{
from
:
'
noResultsText
'
,
default
:
s__
(
'
PackageRegistry|Learn how to %{noPackagesLinkStart}publish and share your packages%{noPackagesLinkEnd} with GitLab.
'
,
),
},
},
computed
:
{
...
mapState
({
emptyListIllustration
:
(
state
)
=>
state
.
config
.
emptyListIllustration
,
emptyListHelpUrl
:
(
state
)
=>
state
.
config
.
emptyListHelpUrl
,
filter
:
(
state
)
=>
state
.
filter
,
selectedType
:
(
state
)
=>
state
.
selectedType
,
packageHelpUrl
:
(
state
)
=>
state
.
config
.
packageHelpUrl
,
packagesCount
:
(
state
)
=>
state
.
pagination
?.
total
,
}),
emptySearch
()
{
return
(
this
.
filter
.
filter
((
f
)
=>
f
.
type
!==
FILTERED_SEARCH_TERM
||
f
.
value
?.
data
).
length
===
0
);
},
emptyStateTitle
()
{
return
this
.
emptySearch
?
this
.
emptyPageTitle
:
s__
(
'
PackageRegistry|Sorry, your filter produced no results
'
);
},
},
mounted
()
{
const
queryParams
=
getQueryParams
(
window
.
document
.
location
.
search
);
const
{
sorting
,
filters
}
=
extractFilterAndSorting
(
queryParams
);
this
.
setSorting
(
sorting
);
this
.
setFilter
(
filters
);
this
.
requestPackagesList
();
this
.
checkDeleteAlert
();
},
methods
:
{
...
mapActions
([
'
requestPackagesList
'
,
'
requestDeletePackage
'
,
'
setSelectedType
'
,
'
setSorting
'
,
'
setFilter
'
,
]),
onPageChanged
(
page
)
{
return
this
.
requestPackagesList
({
page
});
},
onPackageDeleteRequest
(
item
)
{
return
this
.
requestDeletePackage
(
item
);
},
checkDeleteAlert
()
{
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
const
showAlert
=
urlParams
.
get
(
SHOW_DELETE_SUCCESS_ALERT
);
if
(
showAlert
)
{
// to be refactored to use gl-alert
createFlash
({
message
:
DELETE_PACKAGE_SUCCESS_MESSAGE
,
type
:
'
notice
'
});
const
cleanUrl
=
window
.
location
.
href
.
split
(
'
?
'
)[
0
];
historyReplaceState
(
cleanUrl
);
}
},
},
i18n
:
{
widenFilters
:
s__
(
'
PackageRegistry|To widen your search, change or remove the filters above.
'
),
},
};
</
script
>
<
template
>
<div>
<component
:is=
"titleComponent"
:help-url=
"packageHelpUrl"
:count=
"packagesCount"
/>
<component
:is=
"searchComponent"
@
update=
"requestPackagesList"
/>
<package-list
@
page:changed=
"onPageChanged"
@
package:delete=
"onPackageDeleteRequest"
>
<template
#empty-state
>
<gl-empty-state
:title=
"emptyStateTitle"
:svg-path=
"emptyListIllustration"
>
<template
#description
>
<gl-sprintf
v-if=
"!emptySearch"
:message=
"$options.i18n.widenFilters"
/>
<gl-sprintf
v-else
:message=
"noResultsText"
>
<template
#noPackagesLink
="
{ content }">
<gl-link
:href=
"emptyListHelpUrl"
target=
"_blank"
>
{{
content
}}
</gl-link>
</
template
>
</gl-sprintf>
</template>
</gl-empty-state>
</template>
</package-list>
</div>
</template>
app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue
0 → 100644
View file @
6dd10023
<
script
>
import
{
GlFilteredSearchToken
,
GlFilteredSearchSuggestion
}
from
'
@gitlab/ui
'
;
import
{
PACKAGE_TYPES
}
from
'
~/packages/list/constants
'
;
export
default
{
components
:
{
GlFilteredSearchToken
,
GlFilteredSearchSuggestion
,
},
PACKAGE_TYPES
,
};
</
script
>
<
template
>
<gl-filtered-search-token
v-bind=
"
{ ...$attrs }" v-on="$listeners">
<template
#suggestions
>
<gl-filtered-search-suggestion
v-for=
"(type, index) in $options.PACKAGE_TYPES"
:key=
"index"
:value=
"type.type"
>
{{
type
.
title
}}
</gl-filtered-search-suggestion>
</
template
>
</gl-filtered-search-token>
</template>
app/assets/javascripts/packages_and_registries/package_registry/pages/list.js
0 → 100644
View file @
6dd10023
import
Vue
from
'
vue
'
;
import
Translate
from
'
~/vue_shared/translate
'
;
import
PackagesListApp
from
'
../components/list/packages_list_app.vue
'
;
Vue
.
use
(
Translate
);
export
default
()
=>
{
const
el
=
document
.
getElementById
(
'
js-vue-packages-list
'
);
return
new
Vue
({
el
,
render
(
createElement
)
{
return
createElement
(
PackagesListApp
);
},
});
};
spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/packages_list_app_spec.js.snap
0 → 100644
View file @
6dd10023
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`packages_list_app renders 1`] = `
<div>
<div
help-url="foo"
/>
<div />
<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="helpSvg"
/>
</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="helpUrl"
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/packages_list_app_spec.js
0 → 100644
View file @
6dd10023
import
{
GlEmptyState
,
GlSprintf
,
GlLink
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
setWindowLocation
from
'
helpers/set_window_location_helper
'
;
import
createFlash
from
'
~/flash
'
;
import
*
as
commonUtils
from
'
~/lib/utils/common_utils
'
;
import
{
DELETE_PACKAGE_SUCCESS_MESSAGE
}
from
'
~/packages/list/constants
'
;
import
{
SHOW_DELETE_SUCCESS_ALERT
}
from
'
~/packages/shared/constants
'
;
import
PackageListApp
from
'
~/packages_and_registries/package_registry/components/list/packages_list_app.vue
'
;
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
(
'
~/flash
'
);
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
packages_list_app
'
,
()
=>
{
let
wrapper
;
let
store
;
const
PackageList
=
{
name
:
'
package-list
'
,
template
:
'
<div><slot name="empty-state"></slot></div>
'
,
};
const
GlLoadingIcon
=
{
name
:
'
gl-loading-icon
'
,
template
:
'
<div>loading</div>
'
};
// we need to manually stub dynamic imported components because shallowMount is not able to stub them automatically. See: https://github.com/vuejs/vue-test-utils/issues/1279
const
PackageSearch
=
{
name
:
'
PackageSearch
'
,
template
:
'
<div></div>
'
};
const
PackageTitle
=
{
name
:
'
PackageTitle
'
,
template
:
'
<div></div>
'
};
const
InfrastructureTitle
=
{
name
:
'
InfrastructureTitle
'
,
template
:
'
<div></div>
'
};
const
InfrastructureSearch
=
{
name
:
'
InfrastructureSearch
'
,
template
:
'
<div></div>
'
};
const
emptyListHelpUrl
=
'
helpUrl
'
;
const
findEmptyState
=
()
=>
wrapper
.
find
(
GlEmptyState
);
const
findListComponent
=
()
=>
wrapper
.
find
(
PackageList
);
const
findPackageSearch
=
()
=>
wrapper
.
find
(
PackageSearch
);
const
findPackageTitle
=
()
=>
wrapper
.
find
(
PackageTitle
);
const
findInfrastructureTitle
=
()
=>
wrapper
.
find
(
InfrastructureTitle
);
const
findInfrastructureSearch
=
()
=>
wrapper
.
find
(
InfrastructureSearch
);
const
createStore
=
(
filter
=
[])
=>
{
store
=
new
Vuex
.
Store
({
state
:
{
isLoading
:
false
,
config
:
{
resourceId
:
'
project_id
'
,
emptyListIllustration
:
'
helpSvg
'
,
emptyListHelpUrl
,
packageHelpUrl
:
'
foo
'
,
},
filter
,
},
});
store
.
dispatch
=
jest
.
fn
();
};
const
mountComponent
=
(
provide
)
=>
{
wrapper
=
shallowMount
(
PackageListApp
,
{
localVue
,
store
,
stubs
:
{
GlEmptyState
,
GlLoadingIcon
,
PackageList
,
GlSprintf
,
GlLink
,
PackageSearch
,
PackageTitle
,
InfrastructureTitle
,
InfrastructureSearch
,
},
provide
,
});
};
beforeEach
(()
=>
{
createStore
();
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
({});
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
renders
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
it
(
'
call requestPackagesList on page:changed
'
,
()
=>
{
mountComponent
();
store
.
dispatch
.
mockClear
();
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
page:changed
'
,
1
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
,
{
page
:
1
});
});
it
(
'
call requestDeletePackage on package:delete
'
,
()
=>
{
mountComponent
();
const
list
=
findListComponent
();
list
.
vm
.
$emit
(
'
package:delete
'
,
'
foo
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestDeletePackage
'
,
'
foo
'
);
});
it
(
'
does call requestPackagesList only one time on render
'
,
()
=>
{
mountComponent
();
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
'
);
});
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
(
'
calls setFilter with the query string based filters
'
,
()
=>
{
jest
.
spyOn
(
packageUtils
,
'
getQueryParams
'
).
mockReturnValue
(
defaultQueryParamsMock
);
mountComponent
();
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
2
,
'
setFilter
'
,
[
{
type
:
'
type
'
,
value
:
{
data
:
defaultQueryParamsMock
.
type
}
},
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
defaultQueryParamsMock
.
search
[
0
]
}
},
{
type
:
FILTERED_SEARCH_TERM
,
value
:
{
data
:
defaultQueryParamsMock
.
search
[
1
]
}
},
]);
});
it
(
'
calls setSorting and setFilters with the results of extractFilterAndSorting
'
,
()
=>
{
jest
.
spyOn
(
packageUtils
,
'
extractFilterAndSorting
'
)
.
mockReturnValue
({
filters
:
[
'
foo
'
],
sorting
:
{
sort
:
'
desc
'
}
});
mountComponent
();
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
1
,
'
setSorting
'
,
{
sort
:
'
desc
'
});
expect
(
store
.
dispatch
).
toHaveBeenNthCalledWith
(
2
,
'
setFilter
'
,
[
'
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
(
'
includes the right content on the default tab
'
,
()
=>
{
mountComponent
();
const
heading
=
findEmptyState
().
find
(
'
h1
'
);
expect
(
heading
.
text
()).
toBe
(
'
There are no packages yet
'
);
});
});
describe
(
'
filter without results
'
,
()
=>
{
beforeEach
(()
=>
{
createStore
([{
type
:
'
something
'
}]);
mountComponent
();
});
it
(
'
should show specific empty message
'
,
()
=>
{
expect
(
findEmptyState
().
text
()).
toContain
(
'
Sorry, your filter produced no results
'
);
expect
(
findEmptyState
().
text
()).
toContain
(
'
To widen your search, change or remove the filters above
'
,
);
});
});
describe
(
'
Package Search
'
,
()
=>
{
it
(
'
exists
'
,
()
=>
{
mountComponent
();
expect
(
findPackageSearch
().
exists
()).
toBe
(
true
);
});
it
(
'
on update fetches data from the store
'
,
()
=>
{
mountComponent
();
store
.
dispatch
.
mockClear
();
findPackageSearch
().
vm
.
$emit
(
'
update
'
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
requestPackagesList
'
);
});
});
describe
(
'
Infrastructure config
'
,
()
=>
{
it
(
'
defaults to package registry components
'
,
()
=>
{
mountComponent
();
expect
(
findPackageSearch
().
exists
()).
toBe
(
true
);
expect
(
findPackageTitle
().
exists
()).
toBe
(
true
);
expect
(
findInfrastructureTitle
().
exists
()).
toBe
(
false
);
expect
(
findInfrastructureSearch
().
exists
()).
toBe
(
false
);
});
it
(
'
mount different component based on the provided values
'
,
()
=>
{
mountComponent
({
titleComponent
:
'
InfrastructureTitle
'
,
searchComponent
:
'
InfrastructureSearch
'
,
});
expect
(
findPackageSearch
().
exists
()).
toBe
(
false
);
expect
(
findPackageTitle
().
exists
()).
toBe
(
false
);
expect
(
findInfrastructureTitle
().
exists
()).
toBe
(
true
);
expect
(
findInfrastructureSearch
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
delete alert handling
'
,
()
=>
{
const
originalLocation
=
window
.
location
.
href
;
const
search
=
`?
${
SHOW_DELETE_SUCCESS_ALERT
}
=true`
;
beforeEach
(()
=>
{
createStore
();
jest
.
spyOn
(
commonUtils
,
'
historyReplaceState
'
).
mockImplementation
(()
=>
{});
setWindowLocation
(
search
);
});
afterEach
(()
=>
{
setWindowLocation
(
originalLocation
);
});
it
(
`creates a flash if the query string contains
${
SHOW_DELETE_SUCCESS_ALERT
}
`
,
()
=>
{
mountComponent
();
expect
(
createFlash
).
toHaveBeenCalledWith
({
message
:
DELETE_PACKAGE_SUCCESS_MESSAGE
,
type
:
'
notice
'
,
});
});
it
(
'
calls historyReplaceState with a clean url
'
,
()
=>
{
mountComponent
();
expect
(
commonUtils
.
historyReplaceState
).
toHaveBeenCalledWith
(
originalLocation
);
});
it
(
`does nothing if the query string does not contain
${
SHOW_DELETE_SUCCESS_ALERT
}
`
,
()
=>
{
setWindowLocation
(
'
?
'
);
mountComponent
();
expect
(
createFlash
).
not
.
toHaveBeenCalled
();
expect
(
commonUtils
.
historyReplaceState
).
not
.
toHaveBeenCalled
();
});
});
});
spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
0 → 100644
View file @
6dd10023
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
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
PackagesList
from
'
~/packages_and_registries/package_registry/components/list/packages_list.vue
'
;
import
Tracking
from
'
~/tracking
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
packages_list
'
,
()
=>
{
let
wrapper
;
let
store
;
const
EmptySlotStub
=
{
name
:
'
empty-slot-stub
'
,
template
:
'
<div>bar</div>
'
};
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
,
},
sorting
:
{
orderBy
:
'
version
'
,
sort
:
'
desc
'
,
},
};
store
=
new
Vuex
.
Store
({
state
,
getters
:
{
getList
:
()
=>
packages
,
},
});
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
,
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
when is loading
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
packages
:
[],
isLoading
:
true
,
});
});
it
(
'
shows skeleton loader when loading
'
,
()
=>
{
expect
(
findPackagesListLoader
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
when is not loading
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
does not show skeleton loader when not loading
'
,
()
=>
{
expect
(
findPackagesListLoader
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
layout
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
contains a pagination component
'
,
()
=>
{
const
sorting
=
findPackageListPagination
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
it
(
'
contains a modal component
'
,
()
=>
{
const
sorting
=
findPackageListDeleteModal
();
expect
(
sorting
.
exists
()).
toBe
(
true
);
});
});
describe
(
'
when the user can destroy the package
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
setItemToBeDeleted sets itemToBeDeleted and open the modal
'
,
()
=>
{
const
mockModalShow
=
jest
.
spyOn
(
wrapper
.
vm
.
$refs
.
packageListDeleteModal
,
'
show
'
);
const
item
=
last
(
wrapper
.
vm
.
list
);
findPackagesListRow
().
vm
.
$emit
(
'
packageToDelete
'
,
item
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
item
);
expect
(
mockModalShow
).
toHaveBeenCalled
();
});
});
it
(
'
deleteItemConfirmation resets itemToBeDeleted
'
,
()
=>
{
wrapper
.
setData
({
itemToBeDeleted
:
1
});
wrapper
.
vm
.
deleteItemConfirmation
();
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
null
);
});
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
(
'
deleteItemCanceled resets itemToBeDeleted
'
,
()
=>
{
wrapper
.
setData
({
itemToBeDeleted
:
1
});
wrapper
.
vm
.
deleteItemCanceled
();
expect
(
wrapper
.
vm
.
itemToBeDeleted
).
toEqual
(
null
);
});
});
describe
(
'
when the list is empty
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
packages
:
[],
slots
:
{
'
empty-state
'
:
EmptySlotStub
,
},
});
});
it
(
'
show the empty slot
'
,
()
=>
{
const
emptySlot
=
findEmptySlot
();
expect
(
emptySlot
.
exists
()).
toBe
(
true
);
});
});
describe
(
'
pagination component
'
,
()
=>
{
let
pagination
;
let
modelEvent
;
beforeEach
(()
=>
{
mountComponent
();
pagination
=
findPackageListPagination
();
// retrieve the event used by v-model, a more sturdy approach than hardcoding it
modelEvent
=
pagination
.
vm
.
$options
.
model
.
event
;
});
it
(
'
emits page:changed events when the page changes
'
,
()
=>
{
pagination
.
vm
.
$emit
(
modelEvent
,
2
);
expect
(
wrapper
.
emitted
(
'
page:changed
'
)).
toEqual
([[
2
]]);
});
});
describe
(
'
tracking
'
,
()
=>
{
let
eventSpy
;
let
utilSpy
;
const
category
=
'
foo
'
;
beforeEach
(()
=>
{
mountComponent
();
eventSpy
=
jest
.
spyOn
(
Tracking
,
'
event
'
);
utilSpy
=
jest
.
spyOn
(
SharedUtils
,
'
packageTypeToTrackCategory
'
).
mockReturnValue
(
category
);
wrapper
.
setData
({
itemToBeDeleted
:
{
package_type
:
'
conan
'
}
});
});
it
(
'
tracking category calls packageTypeToTrackCategory
'
,
()
=>
{
expect
(
wrapper
.
vm
.
tracking
.
category
).
toBe
(
category
);
expect
(
utilSpy
).
toHaveBeenCalledWith
(
'
conan
'
);
});
it
(
'
deleteItemConfirmation calls event
'
,
()
=>
{
wrapper
.
vm
.
deleteItemConfirmation
();
expect
(
eventSpy
).
toHaveBeenCalledWith
(
category
,
TrackingActions
.
DELETE_PACKAGE
,
expect
.
any
(
Object
),
);
});
});
});
spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js
0 → 100644
View file @
6dd10023
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
Vuex
from
'
vuex
'
;
import
{
sortableFields
}
from
'
~/packages/list/utils
'
;
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
RegistrySearch
from
'
~/vue_shared/components/registry/registry_search.vue
'
;
import
UrlSync
from
'
~/vue_shared/components/url_sync.vue
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
describe
(
'
Package Search
'
,
()
=>
{
let
wrapper
;
let
store
;
const
findRegistrySearch
=
()
=>
wrapper
.
findComponent
(
RegistrySearch
);
const
findUrlSync
=
()
=>
wrapper
.
findComponent
(
UrlSync
);
const
createStore
=
(
isGroupPage
)
=>
{
const
state
=
{
config
:
{
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
:
{
UrlSync
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
has a registry search component
'
,
()
=>
{
mountComponent
();
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
.
each
`
isGroupPage | page
${
false
}
|
${
'
project
'
}
${
true
}
|
${
'
group
'
}
`
(
'
in a $page page binds the right props
'
,
({
isGroupPage
})
=>
{
mountComponent
(
isGroupPage
);
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
(
isGroupPage
),
});
});
it
(
'
on sorting:changed emits update event and calls vuex setSorting
'
,
()
=>
{
const
payload
=
{
sort
:
'
foo
'
};
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
sorting:changed
'
,
payload
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setSorting
'
,
payload
);
expect
(
wrapper
.
emitted
(
'
update
'
)).
toEqual
([[]]);
});
it
(
'
on filter:changed calls vuex setFilter
'
,
()
=>
{
const
payload
=
[
'
foo
'
];
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
filter:changed
'
,
payload
);
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
setFilter
'
,
payload
);
});
it
(
'
on filter:submit emits update event
'
,
()
=>
{
mountComponent
();
findRegistrySearch
().
vm
.
$emit
(
'
filter:submit
'
);
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/package_registry/components/list/packages_title_spec.js
0 → 100644
View file @
6dd10023
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
LIST_INTRO_TEXT
,
LIST_TITLE_TEXT
}
from
'
~/packages/list/constants
'
;
import
PackageTitle
from
'
~/packages_and_registries/package_registry/components/list/package_title.vue
'
;
import
MetadataItem
from
'
~/vue_shared/components/registry/metadata_item.vue
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
describe
(
'
PackageTitle
'
,
()
=>
{
let
wrapper
;
let
store
;
const
findTitleArea
=
()
=>
wrapper
.
find
(
TitleArea
);
const
findMetadataItem
=
()
=>
wrapper
.
find
(
MetadataItem
);
const
mountComponent
=
(
propsData
=
{
helpUrl
:
'
foo
'
})
=>
{
wrapper
=
shallowMount
(
PackageTitle
,
{
store
,
propsData
,
stubs
:
{
TitleArea
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
title area
'
,
()
=>
{
it
(
'
exists
'
,
()
=>
{
mountComponent
();
expect
(
findTitleArea
().
exists
()).
toBe
(
true
);
});
it
(
'
has the correct props
'
,
()
=>
{
mountComponent
();
expect
(
findTitleArea
().
props
()).
toMatchObject
({
title
:
LIST_TITLE_TEXT
,
infoMessages
:
[{
text
:
LIST_INTRO_TEXT
,
link
:
'
foo
'
}],
});
});
});
describe
.
each
`
count | exist | text
${
null
}
|
${
false
}
|
${
''
}
${
undefined
}
|
${
false
}
|
${
''
}
${
0
}
|
${
true
}
|
${
'
0 Packages
'
}
${
1
}
|
${
true
}
|
${
'
1 Package
'
}
${
2
}
|
${
true
}
|
${
'
2 Packages
'
}
`
(
'
when count is $count metadata item
'
,
({
count
,
exist
,
text
})
=>
{
beforeEach
(()
=>
{
mountComponent
({
count
,
helpUrl
:
'
foo
'
});
});
it
(
`is
${
exist
}
that it exists`
,
()
=>
{
expect
(
findMetadataItem
().
exists
()).
toBe
(
exist
);
});
if
(
exist
)
{
it
(
'
has the correct props
'
,
()
=>
{
expect
(
findMetadataItem
().
props
()).
toMatchObject
({
icon
:
'
package
'
,
text
,
});
});
}
});
});
spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
0 → 100644
View file @
6dd10023
import
{
GlFilteredSearchToken
,
GlFilteredSearchSuggestion
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
component
from
'
~/packages/list/components/tokens/package_type_token.vue
'
;
import
{
PACKAGE_TYPES
}
from
'
~/packages/list/constants
'
;
describe
(
'
packages_filter
'
,
()
=>
{
let
wrapper
;
const
findFilteredSearchToken
=
()
=>
wrapper
.
find
(
GlFilteredSearchToken
);
const
findFilteredSearchSuggestions
=
()
=>
wrapper
.
findAll
(
GlFilteredSearchSuggestion
);
const
mountComponent
=
({
attrs
,
listeners
}
=
{})
=>
{
wrapper
=
shallowMount
(
component
,
{
attrs
,
listeners
,
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
it binds all of his attrs to filtered search token
'
,
()
=>
{
mountComponent
({
attrs
:
{
foo
:
'
bar
'
}
});
expect
(
findFilteredSearchToken
().
attributes
(
'
foo
'
)).
toBe
(
'
bar
'
);
});
it
(
'
it binds all of his events to filtered search token
'
,
()
=>
{
const
clickListener
=
jest
.
fn
();
mountComponent
({
listeners
:
{
click
:
clickListener
}
});
findFilteredSearchToken
().
vm
.
$emit
(
'
click
'
);
expect
(
clickListener
).
toHaveBeenCalled
();
});
it
.
each
(
PACKAGE_TYPES
.
map
((
p
,
index
)
=>
[
p
,
index
]))(
'
displays a suggestion for %p
'
,
(
packageType
,
index
)
=>
{
mountComponent
();
const
item
=
findFilteredSearchSuggestions
().
at
(
index
);
expect
(
item
.
text
()).
toBe
(
packageType
.
title
);
expect
(
item
.
props
(
'
value
'
)).
toBe
(
packageType
.
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