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
a48a03d9
Commit
a48a03d9
authored
Jul 21, 2021
by
Nicolò Maria Mezzopera
Committed by
Olena Horal-Koretska
Jul 21, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add apollo and title component to package details refactor
parent
cf6e08a6
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
854 additions
and
24 deletions
+854
-24
app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue
...nd_registries/package_registry/components/details/app.vue
+58
-14
app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue
...ies/package_registry/components/details/package_title.vue
+133
-0
app/assets/javascripts/packages_and_registries/package_registry/constants.js
...pts/packages_and_registries/package_registry/constants.js
+46
-0
app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js
...packages_and_registries/package_registry/graphql/index.js
+14
-0
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
...egistry/graphql/queries/get_package_details.query.graphql
+35
-0
app/assets/javascripts/packages_and_registries/package_registry/pages/details.js
...packages_and_registries/package_registry/pages/details.js
+3
-1
app/assets/javascripts/packages_and_registries/package_registry/utils.js
...scripts/packages_and_registries/package_registry/utils.js
+40
-0
app/helpers/packages_helper.rb
app/helpers/packages_helper.rb
+3
-2
app/views/projects/packages/packages/show.html.haml
app/views/projects/packages/packages/show.html.haml
+2
-2
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap
...mponents/details/__snapshots__/package_title_spec.js.snap
+177
-0
spec/frontend/packages_and_registries/package_registry/components/details/app_spec.js
...egistries/package_registry/components/details/app_spec.js
+50
-5
spec/frontend/packages_and_registries/package_registry/components/details/package_title_spec.js
...package_registry/components/details/package_title_spec.js
+176
-0
spec/frontend/packages_and_registries/package_registry/mock_data.js
...end/packages_and_registries/package_registry/mock_data.js
+70
-0
spec/frontend/packages_and_registries/package_registry/utils_spec.js
...nd/packages_and_registries/package_registry/utils_spec.js
+23
-0
spec/helpers/packages_helper_spec.rb
spec/helpers/packages_helper_spec.rb
+21
-0
No files found.
app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue
View file @
a48a03d9
...
...
@@ -14,6 +14,8 @@ import {
GlTabs
,
GlSprintf
,
}
from
'
@gitlab/ui
'
;
import
createFlash
from
'
~/flash
'
;
import
{
convertToGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
numberToHumanSize
}
from
'
~/lib/utils/number_utils
'
;
import
{
objectToQuery
}
from
'
~/lib/utils/url_utility
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
...
...
@@ -24,12 +26,21 @@ import { s__, __ } from '~/locale';
// import PackageHistory from '~/packages/details/components/package_history.vue';
// import PackageListRow from '~/packages/shared/components/package_list_row.vue';
import
PackagesListLoader
from
'
~/packages/shared/components/packages_list_loader.vue
'
;
import
{
packageTypeToTrackCategory
}
from
'
~/packages/shared/utils
'
;
import
{
PackageType
,
TrackingActions
,
PACKAGE_TYPE_NUGET
,
PACKAGE_TYPE_COMPOSER
,
DELETE_PACKAGE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
,
PULL_PACKAGE_TRACKING_ACTION
,
DELETE_PACKAGE_FILE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION
,
SHOW_DELETE_SUCCESS_ALERT
,
}
from
'
~/packages/shared/constants
'
;
import
{
packageTypeToTrackCategory
}
from
'
~/packages/shared/utils
'
;
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
getPackageDetails
from
'
~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
'
;
import
Tracking
from
'
~/tracking
'
;
export
default
{
...
...
@@ -42,7 +53,8 @@ export default {
GlTab
,
GlTabs
,
GlSprintf
,
PackageTitle
:
()
=>
import
(
'
~/packages/details/components/package_title.vue
'
),
PackageTitle
:
()
=>
import
(
'
~/packages_and_registries/package_registry/components/details/package_title.vue
'
),
TerraformTitle
:
()
=>
import
(
'
~/packages_and_registries/infrastructure_registry/components/details_title.vue
'
),
PackagesListLoader
,
...
...
@@ -59,6 +71,7 @@ export default {
},
mixins
:
[
Tracking
.
mixin
()],
inject
:
[
'
packageId
'
,
'
titleComponent
'
,
'
projectName
'
,
'
canDelete
'
,
...
...
@@ -68,22 +81,53 @@ export default {
'
projectListUrl
'
,
'
groupListUrl
'
,
],
trackingActions
:
{
...
TrackingActions
},
trackingActions
:
{
DELETE_PACKAGE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
,
PULL_PACKAGE_TRACKING_ACTION
,
DELETE_PACKAGE_FILE_TRACKING_ACTION
,
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION
,
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION
,
},
data
()
{
return
{
fileToDelete
:
null
,
packageEntity
:
{},
};
},
apollo
:
{
packageEntity
:
{
query
:
getPackageDetails
,
variables
()
{
return
this
.
queryVariables
;
},
update
(
data
)
{
return
data
.
package
;
},
error
(
error
)
{
createFlash
({
message
:
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE
,
captureError
:
true
,
error
,
});
},
},
},
computed
:
{
queryVariables
()
{
return
{
id
:
convertToGraphQLId
(
'
Packages::Package
'
,
this
.
packageId
),
};
},
packageFiles
()
{
return
this
.
packageEntity
.
packageFiles
;
},
isLoading
()
{
return
fals
e
;
return
this
.
$apollo
.
queries
.
packag
e
;
},
isValidPackage
()
{
return
Boolean
(
this
.
packageEntity
.
name
);
return
Boolean
(
this
.
packageEntity
?
.
name
);
},
tracking
()
{
return
{
...
...
@@ -97,10 +141,10 @@ export default {
return
this
.
packageEntity
.
dependency_links
||
[];
},
showDependencies
()
{
return
this
.
packageEntity
.
package_type
===
P
ackageType
.
NUGET
;
return
this
.
packageEntity
.
package_type
===
P
ACKAGE_TYPE_
NUGET
;
},
showFiles
()
{
return
this
.
packageEntity
?.
package_type
!==
P
ackageType
.
COMPOSER
;
return
this
.
packageEntity
?.
package_type
!==
P
ACKAGE_TYPE_
COMPOSER
;
},
},
methods
:
{
...
...
@@ -113,7 +157,7 @@ export default {
}
},
async
confirmPackageDeletion
()
{
this
.
track
(
TrackingActions
.
DELETE_PACKAGE
);
this
.
track
(
DELETE_PACKAGE_TRACKING_ACTION
);
await
this
.
deletePackage
();
...
...
@@ -127,12 +171,12 @@ export default {
window
.
location
.
replace
(
`
${
returnTo
}
?
${
modalQuery
}
`
);
},
handleFileDelete
(
file
)
{
this
.
track
(
TrackingActions
.
REQUEST_DELETE_PACKAGE_FILE
);
this
.
track
(
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION
);
this
.
fileToDelete
=
{
...
file
};
this
.
$refs
.
deleteFileModal
.
show
();
},
confirmFileDelete
()
{
this
.
track
(
TrackingActions
.
DELETE_PACKAGE_FILE
);
this
.
track
(
DELETE_PACKAGE_FILE_TRACKING_ACTION
);
// this.deletePackageFile(this.fileToDelete.id);
this
.
fileToDelete
=
null
;
},
...
...
@@ -176,7 +220,7 @@ export default {
/>
<div
v-else
class=
"packages-app"
>
<component
:is=
"titleComponent"
>
<component
:is=
"titleComponent"
:package-entity=
"packageEntity"
>
<template
#delete-button
>
<gl-button
v-if=
"canDelete"
...
...
app/assets/javascripts/packages_and_registries/package_registry/components/details/package_title.vue
0 → 100644
View file @
a48a03d9
<
script
>
import
{
GlIcon
,
GlSprintf
,
GlTooltipDirective
,
GlBadge
}
from
'
@gitlab/ui
'
;
import
{
GlBreakpointInstance
}
from
'
@gitlab/ui/dist/utils
'
;
import
{
numberToHumanSize
}
from
'
~/lib/utils/number_utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
PackageTags
from
'
~/packages/shared/components/package_tags.vue
'
;
import
{
PACKAGE_TYPE_NUGET
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
{
getPackageTypeLabel
}
from
'
~/packages_and_registries/package_registry/utils
'
;
import
MetadataItem
from
'
~/vue_shared/components/registry/metadata_item.vue
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
export
default
{
name
:
'
PackageTitle
'
,
components
:
{
TitleArea
,
GlIcon
,
GlSprintf
,
PackageTags
,
MetadataItem
,
GlBadge
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
timeagoMixin
],
i18n
:
{
packageInfo
:
__
(
'
v%{version} published %{timeAgo}
'
),
},
props
:
{
packageEntity
:
{
type
:
Object
,
required
:
true
,
},
},
data
()
{
return
{
isDesktop
:
true
,
};
},
computed
:
{
packageTypeDisplay
()
{
return
getPackageTypeLabel
(
this
.
packageEntity
.
packageType
);
},
packagePipeline
()
{
return
this
.
packageEntity
.
pipelines
?.
nodes
[
0
];
},
packageIcon
()
{
if
(
this
.
packageEntity
.
packageType
===
PACKAGE_TYPE_NUGET
)
{
return
this
.
packageEntity
.
metadata
?.
iconUrl
||
null
;
}
return
null
;
},
hasTagsToDisplay
()
{
return
Boolean
(
this
.
packageEntity
.
tags
?.
nodes
&&
this
.
packageEntity
.
tags
?.
nodes
.
length
);
},
totalSize
()
{
return
this
.
packageEntity
.
packageFiles
?
numberToHumanSize
(
this
.
packageEntity
.
packageFiles
.
nodes
.
reduce
((
acc
,
p
)
=>
acc
+
Number
(
p
.
size
),
0
),
)
:
'
0
'
;
},
},
mounted
()
{
this
.
isDesktop
=
GlBreakpointInstance
.
isDesktop
();
},
methods
:
{
dynamicSlotName
(
index
)
{
return
`metadata-tag
${
index
}
`
;
},
},
};
</
script
>
<
template
>
<title-area
:title=
"packageEntity.name"
:avatar=
"packageIcon"
data-qa-selector=
"package_title"
>
<template
#sub-header
>
<gl-icon
name=
"eye"
class=
"gl-mr-3"
/>
<gl-sprintf
:message=
"$options.i18n.packageInfo"
>
<template
#version
>
{{
packageEntity
.
version
}}
</
template
>
<
template
#timeAgo
>
<span
v-gl-tooltip
:title=
"tooltipTitle(packageEntity.created_at)"
>
{{
timeFormatted
(
packageEntity
.
created_at
)
}}
</span>
</
template
>
</gl-sprintf>
</template>
<
template
v-if=
"packageTypeDisplay"
#metadata-type
>
<metadata-item
data-testid=
"package-type"
icon=
"package"
:text=
"packageTypeDisplay"
/>
</
template
>
<
template
#metadata-size
>
<metadata-item
data-testid=
"package-size"
icon=
"disk"
:text=
"totalSize"
/>
</
template
>
<
template
v-if=
"packagePipeline"
#metadata-pipeline
>
<metadata-item
data-testid=
"pipeline-project"
icon=
"review-list"
:text=
"packagePipeline.project.name"
:link=
"packagePipeline.project.webUrl"
/>
</
template
>
<
template
v-if=
"packagePipeline && packagePipeline.ref"
#metadata-ref
>
<metadata-item
data-testid=
"package-ref"
icon=
"branch"
:text=
"packagePipeline.ref"
/>
</
template
>
<
template
v-if=
"isDesktop && hasTagsToDisplay"
#metadata-tags
>
<package-tags
:tag-display-limit=
"2"
:tags=
"packageEntity.tags.nodes"
hide-label
/>
</
template
>
<!-- we need to duplicate the package tags on mobile to ensure proper styling inside the flex wrap -->
<
template
v-for=
"(tag, index) in packageEntity.tags.nodes"
v-else-if=
"hasTagsToDisplay"
#
[
dynamicSlotName
(
index
)]
>
<gl-badge
:key=
"index"
class=
"gl-my-1"
data-testid=
"tag-badge"
variant=
"info"
size=
"sm"
>
{{
tag
.
name
}}
</gl-badge>
</
template
>
<
template
#right-actions
>
<slot
name=
"delete-button"
></slot>
</
template
>
</title-area>
</template>
app/assets/javascripts/packages_and_registries/package_registry/constants.js
0 → 100644
View file @
a48a03d9
import
{
__
,
s__
}
from
'
~/locale
'
;
export
const
PACKAGE_TYPE_CONAN
=
'
CONAN
'
;
export
const
PACKAGE_TYPE_MAVEN
=
'
MAVEN
'
;
export
const
PACKAGE_TYPE_NPM
=
'
NPM
'
;
export
const
PACKAGE_TYPE_NUGET
=
'
NUGET
'
;
export
const
PACKAGE_TYPE_PYPI
=
'
PYPI
'
;
export
const
PACKAGE_TYPE_COMPOSER
=
'
COMPOSER
'
;
export
const
PACKAGE_TYPE_RUBYGEMS
=
'
RUBYGEMS
'
;
export
const
PACKAGE_TYPE_GENERIC
=
'
GENERIC
'
;
export
const
PACKAGE_TYPE_DEBIAN
=
'
DEBIAN
'
;
export
const
PACKAGE_TYPE_HELM
=
'
HELM
'
;
export
const
PACKAGE_TYPE_TERRAFORM
=
'
terraform_module
'
;
export
const
DELETE_PACKAGE_TRACKING_ACTION
=
'
delete_package
'
;
export
const
REQUEST_DELETE_PACKAGE_TRACKING_ACTION
=
'
request_delete_package
'
;
export
const
CANCEL_DELETE_PACKAGE_TRACKING_ACTION
=
'
cancel_delete_package
'
;
export
const
PULL_PACKAGE_TRACKING_ACTION
=
'
pull_package
'
;
export
const
DELETE_PACKAGE_FILE_TRACKING_ACTION
=
'
delete_package_file
'
;
export
const
REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION
=
'
request_delete_package_file
'
;
export
const
CANCEL_DELETE_PACKAGE_FILE_TRACKING_ACTION
=
'
cancel_delete_package_file
'
;
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.
'
,
);
export
const
DELETE_PACKAGE_FILE_ERROR_MESSAGE
=
s__
(
__
(
'
PackageRegistry|Something went wrong while deleting the package file.
'
),
);
export
const
DELETE_PACKAGE_FILE_SUCCESS_MESSAGE
=
s__
(
'
PackageRegistry|Package file deleted successfully
'
,
);
export
const
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE
=
s__
(
'
PackageRegistry|Failed to load the package data
'
,
);
export
const
PACKAGE_ERROR_STATUS
=
'
error
'
;
export
const
PACKAGE_DEFAULT_STATUS
=
'
default
'
;
export
const
PACKAGE_HIDDEN_STATUS
=
'
hidden
'
;
export
const
PACKAGE_PROCESSING_STATUS
=
'
processing
'
;
app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js
0 → 100644
View file @
a48a03d9
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
Vue
.
use
(
VueApollo
);
export
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
createDefaultClient
(
{},
{
assumeImmutableResults
:
true
,
},
),
});
app/assets/javascripts/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
0 → 100644
View file @
a48a03d9
query
getPackageDetails
(
$id
:
ID
!)
{
package
(
id
:
$id
)
{
id
name
packageType
version
createdAt
updatedAt
status
tags
{
nodes
{
id
name
}
}
pipelines
(
first
:
3
)
{
nodes
{
project
{
name
webUrl
}
}
}
packageFiles
(
first
:
1000
)
{
nodes
{
id
fileMd5
fileName
fileSha1
fileSha256
size
}
}
}
}
app/assets/javascripts/packages_and_registries/package_registry/pages/details.js
View file @
a48a03d9
import
Vue
from
'
vue
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
PackagesApp
from
'
~/packages_and_registries/package_registry/components/details/app.vue
'
;
import
{
apolloProvider
}
from
'
~/packages_and_registries/package_registry/graphql/index
'
;
import
Translate
from
'
~/vue_shared/translate
'
;
import
PackagesApp
from
'
../components/details/app.vue
'
;
Vue
.
use
(
Translate
);
...
...
@@ -14,6 +15,7 @@ export default () => {
const
{
canDelete
,
...
datasetOptions
}
=
el
.
dataset
;
return
new
Vue
({
el
,
apolloProvider
,
provide
:
{
canDelete
:
parseBoolean
(
canDelete
),
titleComponent
:
'
PackageTitle
'
,
...
...
app/assets/javascripts/packages_and_registries/package_registry/utils.js
0 → 100644
View file @
a48a03d9
import
{
s__
}
from
'
~/locale
'
;
import
{
PACKAGE_TYPE_CONAN
,
PACKAGE_TYPE_MAVEN
,
PACKAGE_TYPE_NPM
,
PACKAGE_TYPE_NUGET
,
PACKAGE_TYPE_PYPI
,
PACKAGE_TYPE_COMPOSER
,
PACKAGE_TYPE_RUBYGEMS
,
PACKAGE_TYPE_GENERIC
,
PACKAGE_TYPE_DEBIAN
,
PACKAGE_TYPE_HELM
,
}
from
'
./constants
'
;
export
const
getPackageTypeLabel
=
(
packageType
)
=>
{
switch
(
packageType
)
{
case
PACKAGE_TYPE_CONAN
:
return
s__
(
'
PackageRegistry|Conan
'
);
case
PACKAGE_TYPE_MAVEN
:
return
s__
(
'
PackageRegistry|Maven
'
);
case
PACKAGE_TYPE_NPM
:
return
s__
(
'
PackageRegistry|npm
'
);
case
PACKAGE_TYPE_NUGET
:
return
s__
(
'
PackageRegistry|NuGet
'
);
case
PACKAGE_TYPE_PYPI
:
return
s__
(
'
PackageRegistry|PyPI
'
);
case
PACKAGE_TYPE_RUBYGEMS
:
return
s__
(
'
PackageRegistry|RubyGems
'
);
case
PACKAGE_TYPE_COMPOSER
:
return
s__
(
'
PackageRegistry|Composer
'
);
case
PACKAGE_TYPE_GENERIC
:
return
s__
(
'
PackageRegistry|Generic
'
);
case
PACKAGE_TYPE_DEBIAN
:
return
s__
(
'
PackageRegistry|Debian
'
);
case
PACKAGE_TYPE_HELM
:
return
s__
(
'
PackageRegistry|Helm
'
);
default
:
return
null
;
}
};
app/helpers/packages_helper.rb
View file @
a48a03d9
...
...
@@ -64,9 +64,10 @@ module PackagesHelper
project
.
container_repositories
.
exists?
end
def
package_details_data
(
project
,
package
=
nil
)
def
package_details_data
(
project
,
package
,
use_presenter
=
false
)
{
package:
package
?
package_from_presenter
(
package
)
:
nil
,
package:
use_presenter
?
package_from_presenter
(
package
)
:
nil
,
package_id:
package
.
id
,
can_delete:
can?
(
current_user
,
:destroy_package
,
project
).
to_s
,
svg_path:
image_path
(
'illustrations/no-packages.svg'
),
npm_path:
package_registry_instance_url
(
:npm
),
...
...
app/views/projects/packages/packages/show.html.haml
View file @
a48a03d9
...
...
@@ -7,6 +7,6 @@
.row
.col-12
-
if
Feature
.
enabled?
(
:package_details_apollo
)
#js-vue-packages-detail-new
{
data:
package_details_data
(
@project
)
}
#js-vue-packages-detail-new
{
data:
package_details_data
(
@project
,
@package
)
}
-
else
#js-vue-packages-detail
{
data:
package_details_data
(
@project
,
@package
)
}
#js-vue-packages-detail
{
data:
package_details_data
(
@project
,
@package
,
true
)
}
locale/gitlab.pot
View file @
a48a03d9
...
...
@@ -23381,6 +23381,9 @@ msgstr ""
msgid "PackageRegistry|Delete package"
msgstr ""
msgid "PackageRegistry|Failed to load the package data"
msgstr ""
msgid "PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}"
msgstr ""
...
...
spec/frontend/packages_and_registries/package_registry/components/details/__snapshots__/package_title_spec.js.snap
0 → 100644
View file @
a48a03d9
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PackageTitle renders with tags 1`] = `
<div
class="gl-display-flex gl-flex-direction-column"
data-qa-selector="package_title"
>
<div
class="gl-display-flex gl-justify-content-space-between gl-py-3"
>
<div
class="gl-flex-direction-column gl-flex-grow-1"
>
<div
class="gl-display-flex"
>
<!---->
<div
class="gl-display-flex gl-flex-direction-column"
>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
data-testid="title"
>
@gitlab-org/package-15
</h1>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
/>
</div>
</div>
</div>
<div
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"
>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<metadata-item-stub
data-testid="package-type"
icon="package"
link=""
size="s"
text="npm"
texttooltip=""
/>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<metadata-item-stub
data-testid="package-size"
icon="disk"
link=""
size="s"
text="800.00 KiB"
texttooltip=""
/>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<package-tags-stub
hidelabel="true"
tagdisplaylimit="2"
tags="[object Object],[object Object],[object Object]"
/>
</div>
</div>
</div>
<!---->
</div>
<p />
</div>
`;
exports[`PackageTitle renders without tags 1`] = `
<div
class="gl-display-flex gl-flex-direction-column"
data-qa-selector="package_title"
>
<div
class="gl-display-flex gl-justify-content-space-between gl-py-3"
>
<div
class="gl-flex-direction-column gl-flex-grow-1"
>
<div
class="gl-display-flex"
>
<!---->
<div
class="gl-display-flex gl-flex-direction-column"
>
<h1
class="gl-font-size-h1 gl-mt-3 gl-mb-2"
data-testid="title"
>
@gitlab-org/package-15
</h1>
<div
class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1"
>
<gl-icon-stub
class="gl-mr-3"
name="eye"
size="16"
/>
<gl-sprintf-stub
message="v%{version} published %{timeAgo}"
/>
</div>
</div>
</div>
<div
class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"
>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<metadata-item-stub
data-testid="package-type"
icon="package"
link=""
size="s"
text="npm"
texttooltip=""
/>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<metadata-item-stub
data-testid="package-size"
icon="disk"
link=""
size="s"
text="800.00 KiB"
texttooltip=""
/>
</div>
<div
class="gl-display-flex gl-align-items-center gl-mr-5"
>
<package-tags-stub
hidelabel="true"
tagdisplaylimit="2"
tags="[object Object],[object Object],[object Object]"
/>
</div>
</div>
</div>
<!---->
</div>
<p />
</div>
`;
spec/frontend/packages_and_registries/package_registry/components/details/app_spec.js
View file @
a48a03d9
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
createFlash
from
'
~/flash
'
;
import
PackagesApp
from
'
~/packages_and_registries/package_registry/components/details/app.vue
'
;
import
PackageTitle
from
'
~/packages_and_registries/package_registry/components/details/package_title.vue
'
;
import
{
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
getPackageDetails
from
'
~/packages_and_registries/package_registry/graphql/queries/get_package_details.query.graphql
'
;
import
{
packageDetailsQuery
,
packageData
}
from
'
../../mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
const
localVue
=
createLocalVue
();
describe
(
'
PackagesApp
'
,
()
=>
{
let
wrapper
;
let
apolloProvider
;
function
createComponent
({
resolver
=
jest
.
fn
().
mockResolvedValue
(
packageDetailsQuery
())
}
=
{})
{
localVue
.
use
(
VueApollo
);
const
requestHandlers
=
[[
getPackageDetails
,
resolver
]];
apolloProvider
=
createMockApollo
(
requestHandlers
);
function
createComponent
()
{
wrapper
=
shallowMount
(
PackagesApp
,
{
localVue
,
apolloProvider
,
provide
:
{
titleComponent
:
'
titleComponent
'
,
packageId
:
'
111
'
,
titleComponent
:
'
PackageTitle
'
,
projectName
:
'
projectName
'
,
canDelete
:
'
canDelete
'
,
svgPath
:
'
svgPath
'
,
...
...
@@ -21,7 +42,8 @@ describe('PackagesApp', () => {
});
}
const
emptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
const
findEmptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
const
findPackageTitle
=
()
=>
wrapper
.
findComponent
(
PackageTitle
);
afterEach
(()
=>
{
wrapper
.
destroy
();
...
...
@@ -30,6 +52,29 @@ describe('PackagesApp', () => {
it
(
'
renders an empty state component
'
,
()
=>
{
createComponent
();
expect
(
emptyState
().
exists
()).
toBe
(
true
);
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
});
it
(
'
renders the app and displays the package title
'
,
async
()
=>
{
createComponent
();
await
waitForPromises
();
expect
(
findPackageTitle
().
exists
()).
toBe
(
true
);
expect
(
findPackageTitle
().
props
()).
toMatchObject
({
packageEntity
:
expect
.
objectContaining
(
packageData
()),
});
});
it
(
'
emits an error message if the load fails
'
,
async
()
=>
{
createComponent
({
resolver
:
jest
.
fn
().
mockRejectedValue
()
});
await
waitForPromises
();
expect
(
createFlash
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
message
:
FETCH_PACKAGE_DETAILS_ERROR_MESSAGE
,
}),
);
});
});
spec/frontend/packages_and_registries/package_registry/components/details/package_title_spec.js
0 → 100644
View file @
a48a03d9
import
{
GlBreakpointInstance
}
from
'
@gitlab/ui/dist/utils
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
PackageTags
from
'
~/packages/shared/components/package_tags.vue
'
;
import
PackageTitle
from
'
~/packages_and_registries/package_registry/components/details/package_title.vue
'
;
import
{
PACKAGE_TYPE_CONAN
,
PACKAGE_TYPE_MAVEN
,
PACKAGE_TYPE_NPM
,
PACKAGE_TYPE_NUGET
,
}
from
'
~/packages_and_registries/package_registry/constants
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
import
{
packageData
,
packageFiles
,
packageTags
,
packagePipelines
}
from
'
../../mock_data
'
;
const
packageWithTags
=
{
...
packageData
(),
tags
:
{
nodes
:
packageTags
()
},
packageFiles
:
{
nodes
:
packageFiles
()
},
};
describe
(
'
PackageTitle
'
,
()
=>
{
let
wrapper
;
function
createComponent
(
packageEntity
=
packageWithTags
)
{
wrapper
=
shallowMountExtended
(
PackageTitle
,
{
propsData
:
{
packageEntity
},
stubs
:
{
TitleArea
,
},
});
return
wrapper
.
vm
.
$nextTick
();
}
const
findTitleArea
=
()
=>
wrapper
.
findComponent
(
TitleArea
);
const
findPackageType
=
()
=>
wrapper
.
findByTestId
(
'
package-type
'
);
const
findPackageSize
=
()
=>
wrapper
.
findByTestId
(
'
package-size
'
);
const
findPipelineProject
=
()
=>
wrapper
.
findByTestId
(
'
pipeline-project
'
);
const
findPackageRef
=
()
=>
wrapper
.
findByTestId
(
'
package-ref
'
);
const
findPackageTags
=
()
=>
wrapper
.
findComponent
(
PackageTags
);
const
findPackageBadges
=
()
=>
wrapper
.
findAllByTestId
(
'
tag-badge
'
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
renders
'
,
()
=>
{
it
(
'
without tags
'
,
async
()
=>
{
await
createComponent
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
it
(
'
with tags
'
,
async
()
=>
{
await
createComponent
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
it
(
'
with tags on mobile
'
,
async
()
=>
{
jest
.
spyOn
(
GlBreakpointInstance
,
'
isDesktop
'
).
mockReturnValue
(
false
);
await
createComponent
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
findPackageBadges
()).
toHaveLength
(
packageTags
().
length
);
});
});
describe
(
'
package title
'
,
()
=>
{
it
(
'
is correctly bound
'
,
async
()
=>
{
await
createComponent
();
expect
(
findTitleArea
().
props
(
'
title
'
)).
toBe
(
packageData
().
name
);
});
});
describe
(
'
package icon
'
,
()
=>
{
const
iconUrl
=
'
a-fake-src
'
;
it
(
'
shows an icon when present and package type is NUGET
'
,
async
()
=>
{
await
createComponent
({
...
packageData
(),
packageType
:
PACKAGE_TYPE_NUGET
,
metadata
:
{
iconUrl
},
});
expect
(
findTitleArea
().
props
(
'
avatar
'
)).
toBe
(
iconUrl
);
});
it
(
'
hides the icon when not present
'
,
async
()
=>
{
await
createComponent
();
expect
(
findTitleArea
().
props
(
'
avatar
'
)).
toBe
(
null
);
});
});
describe
.
each
`
packageType | text
${
PACKAGE_TYPE_CONAN
}
|
${
'
Conan
'
}
${
PACKAGE_TYPE_MAVEN
}
|
${
'
Maven
'
}
${
PACKAGE_TYPE_NPM
}
|
${
'
npm
'
}
${
PACKAGE_TYPE_NUGET
}
|
${
'
NuGet
'
}
`
(
`package type`
,
({
packageType
,
text
})
=>
{
beforeEach
(()
=>
createComponent
({
...
packageData
,
packageType
}));
it
(
`
${
packageType
}
should render
${
text
}
`
,
()
=>
{
expect
(
findPackageType
().
props
()).
toEqual
(
expect
.
objectContaining
({
text
,
icon
:
'
package
'
}));
});
});
describe
(
'
calculates the package size
'
,
()
=>
{
it
(
'
correctly calculates when there is only 1 file
'
,
async
()
=>
{
await
createComponent
({
...
packageData
(),
packageFiles
:
{
nodes
:
[
packageFiles
()[
0
]]
}
});
expect
(
findPackageSize
().
props
()).
toMatchObject
({
text
:
'
400.00 KiB
'
,
icon
:
'
disk
'
});
});
it
(
'
correctly calculates when there are multiple files
'
,
async
()
=>
{
await
createComponent
();
expect
(
findPackageSize
().
props
(
'
text
'
)).
toBe
(
'
800.00 KiB
'
);
});
});
describe
(
'
package tags
'
,
()
=>
{
it
(
'
displays the package-tags component when the package has tags
'
,
async
()
=>
{
await
createComponent
();
expect
(
findPackageTags
().
exists
()).
toBe
(
true
);
});
it
(
'
does not display the package-tags component when there are no tags
'
,
async
()
=>
{
await
createComponent
({
...
packageData
(),
tags
:
{
nodes
:
[]
}
});
expect
(
findPackageTags
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
package ref
'
,
()
=>
{
it
(
'
does not display the ref if missing
'
,
async
()
=>
{
await
createComponent
();
expect
(
findPackageRef
().
exists
()).
toBe
(
false
);
});
it
(
'
correctly shows the package ref if there is one
'
,
async
()
=>
{
await
createComponent
({
...
packageData
(),
pipelines
:
{
nodes
:
packagePipelines
({
ref
:
'
test
'
})
},
});
expect
(
findPackageRef
().
props
()).
toMatchObject
({
text
:
'
test
'
,
icon
:
'
branch
'
,
});
});
});
describe
(
'
pipeline project
'
,
()
=>
{
it
(
'
does not display the project if missing
'
,
async
()
=>
{
await
createComponent
();
expect
(
findPipelineProject
().
exists
()).
toBe
(
false
);
});
it
(
'
correctly shows the pipeline project if there is one
'
,
async
()
=>
{
await
createComponent
({
...
packageData
(),
pipelines
:
{
nodes
:
packagePipelines
()
},
});
expect
(
findPipelineProject
().
props
()).
toMatchObject
({
text
:
packagePipelines
()[
0
].
project
.
name
,
icon
:
'
review-list
'
,
link
:
packagePipelines
()[
0
].
project
.
webUrl
,
});
});
});
});
spec/frontend/packages_and_registries/package_registry/mock_data.js
0 → 100644
View file @
a48a03d9
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
'
},
{
id
:
'
gid://gitlab/Packages::Tag/85
'
,
name
:
'
bananas_7
'
,
__typename
:
'
PackageTag
'
},
];
export
const
packagePipelines
=
(
extend
)
=>
[
{
project
:
{
name
:
'
project14
'
,
webUrl
:
'
http://gdk.test:3000/namespace14/project14
'
,
__typename
:
'
Project
'
,
},
...
extend
,
__typename
:
'
Pipeline
'
,
},
];
export
const
packageFiles
=
()
=>
[
{
id
:
'
gid://gitlab/Packages::PackageFile/118
'
,
fileMd5
:
null
,
fileName
:
'
foo-1.0.1.tgz
'
,
fileSha1
:
'
be93151dc23ac34a82752444556fe79b32c7a1ad
'
,
fileSha256
:
null
,
size
:
'
409600
'
,
__typename
:
'
PackageFile
'
,
},
{
id
:
'
gid://gitlab/Packages::PackageFile/119
'
,
fileMd5
:
null
,
fileName
:
'
foo-1.0.2.tgz
'
,
fileSha1
:
'
be93151dc23ac34a82752444556fe79b32c7a1ss
'
,
fileSha256
:
null
,
size
:
'
409600
'
,
__typename
:
'
PackageFile
'
,
},
];
export
const
packageData
=
(
extend
)
=>
({
id
:
'
gid://gitlab/Packages::Package/111
'
,
name
:
'
@gitlab-org/package-15
'
,
packageType
:
'
NPM
'
,
version
:
'
1.0.0
'
,
createdAt
:
'
2020-08-17T14:23:32Z
'
,
updatedAt
:
'
2020-08-17T14:23:32Z
'
,
status
:
'
DEFAULT
'
,
...
extend
,
});
export
const
packageDetailsQuery
=
()
=>
({
data
:
{
package
:
{
...
packageData
(),
tags
:
{
nodes
:
packageTags
(),
__typename
:
'
PackageTagConnection
'
,
},
pipelines
:
{
nodes
:
packagePipelines
(),
__typename
:
'
PipelineConnection
'
,
},
packageFiles
:
{
nodes
:
packageFiles
(),
__typename
:
'
PackageFileConnection
'
,
},
__typename
:
'
PackageDetailsType
'
,
},
},
});
spec/frontend/packages_and_registries/package_registry/utils_spec.js
0 → 100644
View file @
a48a03d9
import
{
getPackageTypeLabel
}
from
'
~/packages_and_registries/package_registry/utils
'
;
describe
(
'
Packages shared utils
'
,
()
=>
{
describe
(
'
getPackageTypeLabel
'
,
()
=>
{
describe
.
each
`
packageType | expectedResult
${
'
CONAN
'
}
|
${
'
Conan
'
}
${
'
MAVEN
'
}
|
${
'
Maven
'
}
${
'
NPM
'
}
|
${
'
npm
'
}
${
'
NUGET
'
}
|
${
'
NuGet
'
}
${
'
PYPI
'
}
|
${
'
PyPI
'
}
${
'
RUBYGEMS
'
}
|
${
'
RubyGems
'
}
${
'
COMPOSER
'
}
|
${
'
Composer
'
}
${
'
DEBIAN
'
}
|
${
'
Debian
'
}
${
'
HELM
'
}
|
${
'
Helm
'
}
${
'
FOO
'
}
|
${
null
}
`
(
`package type`
,
({
packageType
,
expectedResult
})
=>
{
it
(
`
${
packageType
}
should show as
${
expectedResult
}
`
,
()
=>
{
expect
(
getPackageTypeLabel
(
packageType
)).
toBe
(
expectedResult
);
});
});
});
});
spec/helpers/packages_helper_spec.rb
View file @
a48a03d9
...
...
@@ -219,4 +219,25 @@ RSpec.describe PackagesHelper do
it
{
is_expected
.
to
eq
(
expected_result
)
}
end
end
describe
'#package_details_data'
do
let_it_be
(
:package
)
{
create
(
:package
)
}
before
do
allow
(
helper
).
to
receive
(
:current_user
)
{
project
.
owner
}
allow
(
helper
).
to
receive
(
:can?
)
{
true
}
end
it
'when use_presenter is true populate the package key'
do
result
=
helper
.
package_details_data
(
project
,
package
,
true
)
expect
(
result
[
:package
]).
not_to
be_nil
end
it
'when use_presenter is false the package key is nil'
do
result
=
helper
.
package_details_data
(
project
,
package
,
false
)
expect
(
result
[
:package
]).
to
be_nil
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment