Commit f4f200a0 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch '330847-convert-package-details-page-to-use-graphql-3' into 'master'

Copy package components to new folder

See merge request gitlab-org/gitlab!66313
parents ef4d5c50 7bd4496b
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { PackageType } from '~/packages/shared/constants';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
export default {
i18n: {
sourceText: s__('PackageRegistry|Source project located at %{link}'),
licenseText: s__('PackageRegistry|License information located at %{link}'),
recipeText: s__('PackageRegistry|Recipe: %{recipe}'),
appGroup: s__('PackageRegistry|App group: %{group}'),
appName: s__('PackageRegistry|App name: %{name}'),
},
components: {
DetailsRow,
GlLink,
GlSprintf,
},
props: {
packageEntity: {
type: Object,
required: true,
},
},
computed: {
showMetadata() {
const visibilityConditions = {
[PackageType.NUGET]: this.packageEntity.nuget_metadatum,
[PackageType.CONAN]: this.packageEntity.conan_metadatum,
[PackageType.MAVEN]: this.packageEntity.maven_metadatum,
};
return visibilityConditions[this.packageEntity.package_type];
},
},
};
</script>
<template>
<div v-if="showMetadata">
<h3 class="gl-font-lg" data-testid="title">{{ __('Additional Metadata') }}</h3>
<div class="gl-bg-gray-50 gl-inset-border-1-gray-100 gl-rounded-base" data-testid="main">
<template v-if="packageEntity.nuget_metadatum">
<details-row icon="project" padding="gl-p-4" dashed data-testid="nuget-source">
<gl-sprintf :message="$options.i18n.sourceText">
<template #link>
<gl-link :href="packageEntity.nuget_metadatum.project_url" target="_blank">{{
packageEntity.nuget_metadatum.project_url
}}</gl-link>
</template>
</gl-sprintf>
</details-row>
<details-row icon="license" padding="gl-p-4" data-testid="nuget-license">
<gl-sprintf :message="$options.i18n.licenseText">
<template #link>
<gl-link :href="packageEntity.nuget_metadatum.license_url" target="_blank">{{
packageEntity.nuget_metadatum.license_url
}}</gl-link>
</template>
</gl-sprintf>
</details-row>
</template>
<details-row
v-else-if="packageEntity.conan_metadatum"
icon="information-o"
padding="gl-p-4"
data-testid="conan-recipe"
>
<gl-sprintf :message="$options.i18n.recipeText">
<template #recipe>{{ packageEntity.name }}</template>
</gl-sprintf>
</details-row>
<template v-else-if="packageEntity.maven_metadatum">
<details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app">
<gl-sprintf :message="$options.i18n.appName">
<template #name>
<strong>{{ packageEntity.maven_metadatum.app_name }}</strong>
</template>
</gl-sprintf>
</details-row>
<details-row icon="information-o" padding="gl-p-4" data-testid="maven-group">
<gl-sprintf :message="$options.i18n.appGroup">
<template #group>
<strong>{{ packageEntity.maven_metadatum.app_group }}</strong>
</template>
</gl-sprintf>
</details-row>
</template>
</div>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'ComposerInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
computed: {
...mapState(['composerHelpPath']),
...mapGetters(['composerRegistryInclude', 'composerPackageInclude', 'groupExists']),
},
i18n: {
registryInclude: s__('PackageRegistry|Add composer registry'),
copyRegistryInclude: s__('PackageRegistry|Copy registry include'),
packageInclude: s__('PackageRegistry|Install package version'),
copyPackageInclude: s__('PackageRegistry|Copy require package include'),
infoLine: s__(
'PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [{ value: 'composer', label: s__('PackageRegistry|Show Composer commands') }],
};
</script>
<template>
<div v-if="groupExists" data-testid="root-node">
<installation-title package-type="composer" :options="$options.installOptions" />
<code-instruction
:label="$options.i18n.registryInclude"
:instruction="composerRegistryInclude"
:copy-text="$options.i18n.copyRegistryInclude"
:tracking-action="$options.trackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
data-testid="registry-include"
/>
<code-instruction
:label="$options.i18n.packageInclude"
:instruction="composerPackageInclude"
:copy-text="$options.i18n.copyPackageInclude"
:tracking-action="$options.trackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
data-testid="package-include"
/>
<span data-testid="help-text">
<gl-sprintf :message="$options.i18n.infoLine">
<template #link="{ content }">
<gl-link :href="composerHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</span>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'ConanInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
computed: {
...mapState(['conanHelpPath']),
...mapGetters(['conanInstallationCommand', 'conanSetupCommand']),
},
i18n: {
helpText: s__(
'PackageRegistry|For more information on the Conan registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [{ value: 'conan', label: s__('PackageRegistry|Show Conan commands') }],
};
</script>
<template>
<div>
<installation-title package-type="conan" :options="$options.installOptions" />
<code-instruction
:label="s__('PackageRegistry|Conan Command')"
:instruction="conanInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Conan Command')"
:tracking-action="$options.trackingActions.COPY_CONAN_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<code-instruction
:label="s__('PackageRegistry|Add Conan Remote')"
:instruction="conanSetupCommand"
:copy-text="s__('PackageRegistry|Copy Conan Setup Command')"
:tracking-action="$options.trackingActions.COPY_CONAN_SETUP_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="conanHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
<script>
export default {
name: 'DependencyRow',
props: {
dependency: {
type: Object,
required: true,
},
},
computed: {
showVersion() {
return Boolean(this.dependency.version_pattern);
},
},
};
</script>
<template>
<div class="gl-responsive-table-row">
<div class="table-section section-50">
<strong class="gl-text-body">{{ dependency.name }}</strong>
<span v-if="dependency.target_framework" data-testid="target-framework"
>({{ dependency.target_framework }})</span
>
</div>
<div
v-if="showVersion"
class="table-section section-50 gl-display-flex gl-md-justify-content-end"
data-testid="version-pattern"
>
<span class="gl-text-body">{{ dependency.version_pattern }}</span>
</div>
</div>
</template>
<script>
import { s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
export default {
name: 'FileSha',
components: {
DetailsRow,
ClipboardButton,
},
props: {
sha: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
},
i18n: {
copyButtonTitle: s__('PackageRegistry|Copy SHA'),
},
};
</script>
<template>
<details-row dashed>
<div class="gl-px-4">
{{ title }}:
{{ sha }}
<clipboard-button
:text="sha"
:title="$options.i18n.copyButtonTitle"
category="tertiary"
size="small"
/>
</div>
</details-row>
</template>
<script>
import { PackageType, TERRAFORM_PACKAGE_TYPE } from '~/packages/shared/constants';
import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
import ComposerInstallation from './composer_installation.vue';
import ConanInstallation from './conan_installation.vue';
import MavenInstallation from './maven_installation.vue';
import NpmInstallation from './npm_installation.vue';
import NugetInstallation from './nuget_installation.vue';
import PypiInstallation from './pypi_installation.vue';
export default {
name: 'InstallationCommands',
components: {
[PackageType.CONAN]: ConanInstallation,
[PackageType.MAVEN]: MavenInstallation,
[PackageType.NPM]: NpmInstallation,
[PackageType.NUGET]: NugetInstallation,
[PackageType.PYPI]: PypiInstallation,
[PackageType.COMPOSER]: ComposerInstallation,
[TERRAFORM_PACKAGE_TYPE]: TerraformInstallation,
},
props: {
packageEntity: {
type: Object,
required: true,
},
npmPath: {
type: String,
required: false,
default: '',
},
npmHelpPath: {
type: String,
required: false,
default: '',
},
},
computed: {
installationComponent() {
return this.$options.components[this.packageEntity.package_type];
},
},
};
</script>
<template>
<div v-if="installationComponent">
<component
:is="installationComponent"
:name="packageEntity.name"
:registry-url="npmPath"
:help-url="npmHelpPath"
/>
</div>
</template>
<script>
import PersistedDropdownSelection from '~/vue_shared/components/registry/persisted_dropdown_selection.vue';
export default {
name: 'InstallationTitle',
components: {
PersistedDropdownSelection,
},
props: {
packageType: {
type: String,
required: true,
},
options: {
type: Array,
required: true,
},
},
computed: {
storageKey() {
return `package_${this.packageType}_installation_instructions`;
},
},
};
</script>
<template>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
<h3 class="gl-font-lg">{{ __('Installation') }}</h3>
<div>
<persisted-dropdown-selection
:storage-key="storageKey"
:options="options"
@change="$emit('change', $event)"
/>
</div>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'MavenInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
data() {
return {
instructionType: 'maven',
};
},
computed: {
...mapState(['mavenHelpPath']),
...mapGetters([
'mavenInstallationXml',
'mavenInstallationCommand',
'mavenSetupXml',
'gradleGroovyInstalCommand',
'gradleGroovyAddSourceCommand',
'gradleKotlinInstalCommand',
'gradleKotlinAddSourceCommand',
]),
showMaven() {
return this.instructionType === 'maven';
},
showGroovy() {
return this.instructionType === 'groovy';
},
},
i18n: {
xmlText: s__(
`PackageRegistry|Copy and paste this inside your %{codeStart}pom.xml%{codeEnd} %{codeStart}dependencies%{codeEnd} block.`,
),
setupText: s__(
`PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}pom.xml%{codeEnd} file.`,
),
helpText: s__(
'PackageRegistry|For more information on the Maven registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [
{ value: 'maven', label: s__('PackageRegistry|Maven XML') },
{ value: 'groovy', label: s__('PackageRegistry|Gradle Groovy DSL') },
{ value: 'kotlin', label: s__('PackageRegistry|Gradle Kotlin DSL') },
],
};
</script>
<template>
<div>
<installation-title
package-type="maven"
:options="$options.installOptions"
@change="instructionType = $event"
/>
<template v-if="showMaven">
<p>
<gl-sprintf :message="$options.i18n.xmlText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<code-instruction
:instruction="mavenInstallationXml"
:copy-text="s__('PackageRegistry|Copy Maven XML')"
:tracking-action="$options.trackingActions.COPY_MAVEN_XML"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
<code-instruction
:label="s__('PackageRegistry|Maven Command')"
:instruction="mavenInstallationCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
:tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ s__('PackageRegistry|Registry setup') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.setupText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<code-instruction
:instruction="mavenSetupXml"
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
:tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="mavenHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
<template v-else-if="showGroovy">
<code-instruction
class="gl-mb-5"
:label="s__('PackageRegistry|Gradle Groovy DSL install command')"
:instruction="gradleGroovyInstalCommand"
:copy-text="s__('PackageRegistry|Copy Gradle Groovy DSL install command')"
:tracking-action="$options.trackingActions.COPY_GRADLE_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
:label="s__('PackageRegistry|Add Gradle Groovy DSL repository command')"
:instruction="gradleGroovyAddSourceCommand"
:copy-text="s__('PackageRegistry|Copy add Gradle Groovy DSL repository command')"
:tracking-action="$options.trackingActions.COPY_GRADLE_ADD_TO_SOURCE_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
</template>
<template v-else>
<code-instruction
class="gl-mb-5"
:label="s__('PackageRegistry|Gradle Kotlin DSL install command')"
:instruction="gradleKotlinInstalCommand"
:copy-text="s__('PackageRegistry|Copy Gradle Kotlin DSL install command')"
:tracking-action="$options.trackingActions.COPY_KOTLIN_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
:label="s__('PackageRegistry|Add Gradle Kotlin DSL repository command')"
:instruction="gradleKotlinAddSourceCommand"
:copy-text="s__('PackageRegistry|Copy add Gradle Kotlin DSL repository command')"
:tracking-action="$options.trackingActions.COPY_KOTLIN_ADD_TO_SOURCE_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
multiline
/>
</template>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { NpmManager, TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'NpmInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
data() {
return {
instructionType: 'npm',
};
},
computed: {
...mapState(['npmHelpPath']),
...mapGetters(['npmInstallationCommand', 'npmSetupCommand']),
npmCommand() {
return this.npmInstallationCommand(NpmManager.NPM);
},
npmSetup() {
return this.npmSetupCommand(NpmManager.NPM);
},
yarnCommand() {
return this.npmInstallationCommand(NpmManager.YARN);
},
yarnSetupCommand() {
return this.npmSetupCommand(NpmManager.YARN);
},
showNpm() {
return this.instructionType === 'npm';
},
},
i18n: {
helpText: s__(
'PackageRegistry|You may also need to setup authentication using an auth token. %{linkStart}See the documentation%{linkEnd} to find out more.',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [
{ value: 'npm', label: s__('PackageRegistry|Show NPM commands') },
{ value: 'yarn', label: s__('PackageRegistry|Show Yarn commands') },
],
};
</script>
<template>
<div>
<installation-title
package-type="npm"
:options="$options.installOptions"
@change="instructionType = $event"
/>
<code-instruction
v-if="showNpm"
:instruction="npmCommand"
:copy-text="s__('PackageRegistry|Copy npm command')"
:tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
v-else
:instruction="yarnCommand"
:copy-text="s__('PackageRegistry|Copy yarn command')"
:tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<code-instruction
v-if="showNpm"
:instruction="npmSetup"
:copy-text="s__('PackageRegistry|Copy npm setup command')"
:tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<code-instruction
v-else
:instruction="yarnSetupCommand"
:copy-text="s__('PackageRegistry|Copy yarn setup command')"
:tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="npmHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'NugetInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
computed: {
...mapState(['nugetHelpPath']),
...mapGetters(['nugetInstallationCommand', 'nugetSetupCommand']),
},
i18n: {
helpText: s__(
'PackageRegistry|For more information on the NuGet registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [{ value: 'nuget', label: s__('PackageRegistry|Show Nuget commands') }],
};
</script>
<template>
<div>
<installation-title package-type="nuget" :options="$options.installOptions" />
<code-instruction
:label="s__('PackageRegistry|NuGet Command')"
:instruction="nugetInstallationCommand"
:copy-text="s__('PackageRegistry|Copy NuGet Command')"
:tracking-action="$options.trackingActions.COPY_NUGET_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<code-instruction
:label="s__('PackageRegistry|Add NuGet Source')"
:instruction="nugetSetupCommand"
:copy-text="s__('PackageRegistry|Copy NuGet Setup Command')"
:tracking-action="$options.trackingActions.COPY_NUGET_SETUP_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="nugetHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
<script>
import { GlLink, GlTable, GlDropdownItem, GlDropdown, GlIcon, GlButton } from '@gitlab/ui';
import { last } from 'lodash';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue';
import Tracking from '~/tracking';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
name: 'PackageFiles',
components: {
GlLink,
GlTable,
GlIcon,
GlDropdown,
GlDropdownItem,
GlButton,
FileIcon,
TimeAgoTooltip,
FileSha,
},
mixins: [Tracking.mixin()],
props: {
packageFiles: {
type: Array,
required: false,
default: () => [],
},
canDelete: {
type: Boolean,
default: false,
required: false,
},
},
computed: {
filesTableRows() {
return this.packageFiles.map((pf) => ({
...pf,
size: this.formatSize(pf.size),
pipeline: last(pf.pipelines),
}));
},
showCommitColumn() {
return this.filesTableRows.some((row) => Boolean(row.pipeline?.id));
},
filesTableHeaderFields() {
return [
{
key: 'name',
label: __('Name'),
},
{
key: 'commit',
label: __('Commit'),
hide: !this.showCommitColumn,
},
{
key: 'size',
label: __('Size'),
},
{
key: 'created',
label: __('Created'),
class: 'gl-text-right',
},
{
key: 'actions',
label: '',
hide: !this.canDelete,
class: 'gl-text-right',
tdClass: 'gl-w-4',
},
].filter((c) => !c.hide);
},
},
methods: {
formatSize(size) {
return numberToHumanSize(size);
},
hasDetails(item) {
return item.file_sha256 || item.file_md5 || item.file_sha1;
},
},
i18n: {
deleteFile: __('Delete file'),
},
};
</script>
<template>
<div>
<h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
<gl-table
:fields="filesTableHeaderFields"
:items="filesTableRows"
:tbody-tr-attr="{ 'data-testid': 'file-row' }"
>
<template #cell(name)="{ item, toggleDetails, detailsShowing }">
<gl-button
v-if="hasDetails(item)"
:icon="detailsShowing ? 'angle-up' : 'angle-down'"
:aria-label="detailsShowing ? __('Collapse') : __('Expand')"
category="tertiary"
size="small"
@click="toggleDetails"
/>
<gl-link
:href="item.download_path"
class="gl-text-gray-500"
data-testid="download-link"
@click="$emit('download-file')"
>
<file-icon
:file-name="item.file_name"
css-classes="gl-relative file-icon"
class="gl-mr-1 gl-relative"
/>
<span>{{ item.file_name }}</span>
</gl-link>
</template>
<template #cell(commit)="{ item }">
<gl-link
v-if="item.pipeline && item.pipeline.project"
:href="item.pipeline.project.commit_url"
class="gl-text-gray-500"
data-testid="commit-link"
>{{ item.pipeline.git_commit_message }}</gl-link
>
</template>
<template #cell(created)="{ item }">
<time-ago-tooltip :time="item.created_at" />
</template>
<template #cell(actions)="{ item }">
<gl-dropdown category="tertiary" right>
<template #button-content>
<gl-icon name="ellipsis_v" />
</template>
<gl-dropdown-item data-testid="delete-file" @click="$emit('delete-file', item)">
{{ $options.i18n.deleteFile }}
</gl-dropdown-item>
</gl-dropdown>
</template>
<template #row-details="{ item }">
<div
class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100"
>
<file-sha
v-if="item.file_sha256"
data-testid="sha-256"
title="SHA-256"
:sha="item.file_sha256"
/>
<file-sha v-if="item.file_md5" data-testid="md5" title="MD5" :sha="item.file_md5" />
<file-sha v-if="item.file_sha1" data-testid="sha-1" title="SHA-1" :sha="item.file_sha1" />
</div>
</template>
</gl-table>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { first } from 'lodash';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, n__ } from '~/locale';
import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
name: 'PackageHistory',
i18n: {
createdOn: s__('PackageRegistry|%{name} version %{version} was first created %{datetime}'),
createdByCommitText: s__('PackageRegistry|Created by commit %{link} on branch %{branch}'),
createdByPipelineText: s__(
'PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}',
),
publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'),
combinedUpdateText: s__(
'PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}',
),
archivedPipelineMessageSingular: s__('PackageRegistry|Package has %{number} archived update'),
archivedPipelineMessagePlural: s__('PackageRegistry|Package has %{number} archived updates'),
},
components: {
GlLink,
GlSprintf,
HistoryItem,
TimeAgoTooltip,
},
props: {
packageEntity: {
type: Object,
required: true,
},
projectName: {
type: String,
required: true,
},
},
data() {
return {
showDescription: false,
};
},
computed: {
pipelines() {
return this.packageEntity.pipelines || [];
},
firstPipeline() {
return first(this.pipelines);
},
lastPipelines() {
return this.pipelines.slice(1).slice(-HISTORY_PIPELINES_LIMIT);
},
showPipelinesInfo() {
return Boolean(this.firstPipeline?.id);
},
archiviedLines() {
return Math.max(this.pipelines.length - HISTORY_PIPELINES_LIMIT - 1, 0);
},
archivedPipelineMessage() {
return n__(
this.$options.i18n.archivedPipelineMessageSingular,
this.$options.i18n.archivedPipelineMessagePlural,
this.archiviedLines,
);
},
},
methods: {
truncate(value) {
return truncateSha(value);
},
},
};
</script>
<template>
<div class="issuable-discussion">
<h3 class="gl-font-lg" data-testid="title">{{ __('History') }}</h3>
<ul class="timeline main-notes-list notes gl-mb-4" data-testid="timeline">
<history-item icon="clock" data-testid="created-on">
<gl-sprintf :message="$options.i18n.createdOn">
<template #name>
<strong>{{ packageEntity.name }}</strong>
</template>
<template #version>
<strong>{{ packageEntity.version }}</strong>
</template>
<template #datetime>
<time-ago-tooltip :time="packageEntity.created_at" />
</template>
</gl-sprintf>
</history-item>
<template v-if="showPipelinesInfo">
<!-- FIRST PIPELINE BLOCK -->
<history-item icon="commit" data-testid="first-pipeline-commit">
<gl-sprintf :message="$options.i18n.createdByCommitText">
<template #link>
<gl-link :href="firstPipeline.project.commit_url"
>#{{ truncate(firstPipeline.sha) }}</gl-link
>
</template>
<template #branch>
<strong>{{ firstPipeline.ref }}</strong>
</template>
</gl-sprintf>
</history-item>
<history-item icon="pipeline" data-testid="first-pipeline-pipeline">
<gl-sprintf :message="$options.i18n.createdByPipelineText">
<template #link>
<gl-link :href="firstPipeline.project.pipeline_url">#{{ firstPipeline.id }}</gl-link>
</template>
<template #datetime>
<time-ago-tooltip :time="firstPipeline.created_at" />
</template>
<template #author>{{ firstPipeline.user.name }}</template>
</gl-sprintf>
</history-item>
</template>
<!-- PUBLISHED LINE -->
<history-item icon="package" data-testid="published">
<gl-sprintf :message="$options.i18n.publishText">
<template #project>
<strong>{{ projectName }}</strong>
</template>
<template #datetime>
<time-ago-tooltip :time="packageEntity.created_at" />
</template>
</gl-sprintf>
</history-item>
<history-item v-if="archiviedLines" icon="history" data-testid="archived">
<gl-sprintf :message="archivedPipelineMessage">
<template #number>
<strong>{{ archiviedLines }}</strong>
</template>
</gl-sprintf>
</history-item>
<!-- PIPELINES LIST ENTRIES -->
<history-item
v-for="pipeline in lastPipelines"
:key="pipeline.id"
icon="pencil"
data-testid="pipeline-entry"
>
<gl-sprintf :message="$options.i18n.combinedUpdateText">
<template #link>
<gl-link :href="pipeline.project.commit_url">#{{ truncate(pipeline.sha) }}</gl-link>
</template>
<template #branch>
<strong>{{ pipeline.ref }}</strong>
</template>
<template #pipeline>
<gl-link :href="pipeline.project.pipeline_url">#{{ pipeline.id }}</gl-link>
</template>
<template #datetime>
<time-ago-tooltip :time="pipeline.created_at" />
</template>
</gl-sprintf>
</history-item>
</ul>
</div>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import { TrackingActions, TrackingLabels } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
export default {
name: 'PyPiInstallation',
components: {
InstallationTitle,
CodeInstruction,
GlLink,
GlSprintf,
},
computed: {
...mapState(['pypiHelpPath']),
...mapGetters(['pypiPipCommand', 'pypiSetupCommand']),
},
i18n: {
setupText: s__(
`PackageRegistry|If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file.`,
),
helpText: s__(
'PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}.',
),
},
trackingActions: { ...TrackingActions },
TrackingLabels,
installOptions: [{ value: 'pypi', label: s__('PackageRegistry|Show PyPi commands') }],
};
</script>
<template>
<div>
<installation-title package-type="pypi" :options="$options.installOptions" />
<code-instruction
:label="s__('PackageRegistry|Pip Command')"
:instruction="pypiPipCommand"
:copy-text="s__('PackageRegistry|Copy Pip command')"
data-testid="pip-command"
:tracking-action="$options.trackingActions.COPY_PIP_INSTALL_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<h3 class="gl-font-lg">{{ __('Registry setup') }}</h3>
<p>
<gl-sprintf :message="$options.i18n.setupText">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<code-instruction
:instruction="pypiSetupCommand"
:copy-text="s__('PackageRegistry|Copy .pypirc content')"
data-testid="pypi-setup-content"
multiline
:tracking-action="$options.trackingActions.COPY_PYPI_SETUP_COMMAND"
:tracking-label="$options.TrackingLabels.CODE_INSTRUCTION"
/>
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="pypiHelpPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ConanInstallation renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object]"
packagetype="conan"
/>
<code-instruction-stub
copytext="Copy Conan Command"
instruction="foo/command"
label="Conan Command"
trackingaction="copy_conan_command"
trackinglabel="code_instruction"
/>
<h3
class="gl-font-lg"
>
Registry setup
</h3>
<code-instruction-stub
copytext="Copy Conan Setup Command"
instruction="foo/setup"
label="Add Conan Remote"
trackingaction="copy_conan_setup_command"
trackinglabel="code_instruction"
/>
<gl-sprintf-stub
message="For more information on the Conan registry, %{linkStart}see the documentation%{linkEnd}."
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DependencyRow renders full dependency 1`] = `
<div
class="gl-responsive-table-row"
>
<div
class="table-section section-50"
>
<strong
class="gl-text-body"
>
Test.Dependency
</strong>
<span
data-testid="target-framework"
>
(.NETStandard2.0)
</span>
</div>
<div
class="table-section section-50 gl-display-flex gl-md-justify-content-end"
data-testid="version-pattern"
>
<span
class="gl-text-body"
>
2.3.7
</span>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FileSha renders 1`] = `
<div
class="gl-display-flex gl-align-items-center gl-font-monospace gl-font-sm gl-word-break-all gl-py-2 gl-border-b-solid gl-border-gray-100 gl-border-b-1"
>
<!---->
<span>
<div
class="gl-px-4"
>
bar:
foo
<gl-button-stub
aria-label="Copy this value"
buttontextclasses=""
category="tertiary"
data-clipboard-text="foo"
icon="copy-to-clipboard"
size="small"
title="Copy SHA"
variant="default"
/>
</div>
</span>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MavenInstallation groovy renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object],[object Object],[object Object]"
packagetype="maven"
/>
<code-instruction-stub
class="gl-mb-5"
copytext="Copy Gradle Groovy DSL install command"
instruction="foo/gradle/groovy/install"
label="Gradle Groovy DSL install command"
trackingaction="copy_gradle_install_command"
trackinglabel="code_instruction"
/>
<code-instruction-stub
copytext="Copy add Gradle Groovy DSL repository command"
instruction="foo/gradle/groovy/add/source"
label="Add Gradle Groovy DSL repository command"
multiline="true"
trackingaction="copy_gradle_add_to_source_command"
trackinglabel="code_instruction"
/>
</div>
`;
exports[`MavenInstallation kotlin renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object],[object Object],[object Object]"
packagetype="maven"
/>
<code-instruction-stub
class="gl-mb-5"
copytext="Copy Gradle Kotlin DSL install command"
instruction="foo/gradle/kotlin/install"
label="Gradle Kotlin DSL install command"
trackingaction="copy_kotlin_install_command"
trackinglabel="code_instruction"
/>
<code-instruction-stub
copytext="Copy add Gradle Kotlin DSL repository command"
instruction="foo/gradle/kotlin/add/source"
label="Add Gradle Kotlin DSL repository command"
multiline="true"
trackingaction="copy_kotlin_add_to_source_command"
trackinglabel="code_instruction"
/>
</div>
`;
exports[`MavenInstallation maven renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object],[object Object],[object Object]"
packagetype="maven"
/>
<p>
<gl-sprintf-stub
message="Copy and paste this inside your %{codeStart}pom.xml%{codeEnd} %{codeStart}dependencies%{codeEnd} block."
/>
</p>
<code-instruction-stub
copytext="Copy Maven XML"
instruction="foo/xml"
label=""
multiline="true"
trackingaction="copy_maven_xml"
trackinglabel="code_instruction"
/>
<code-instruction-stub
copytext="Copy Maven command"
instruction="foo/command"
label="Maven Command"
trackingaction="copy_maven_command"
trackinglabel="code_instruction"
/>
<h3
class="gl-font-lg"
>
Registry setup
</h3>
<p>
<gl-sprintf-stub
message="If you haven't already done so, you will need to add the below to your %{codeStart}pom.xml%{codeEnd} file."
/>
</p>
<code-instruction-stub
copytext="Copy Maven registry XML"
instruction="foo/setup"
label=""
multiline="true"
trackingaction="copy_maven_setup_xml"
trackinglabel="code_instruction"
/>
<gl-sprintf-stub
message="For more information on the Maven registry, %{linkStart}see the documentation%{linkEnd}."
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NpmInstallation renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object],[object Object]"
packagetype="npm"
/>
<code-instruction-stub
copytext="Copy npm command"
instruction="npm i @Test/package"
label=""
trackingaction="copy_npm_install_command"
trackinglabel="code_instruction"
/>
<h3
class="gl-font-lg"
>
Registry setup
</h3>
<code-instruction-stub
copytext="Copy npm setup command"
instruction="echo @Test:registry=undefined/ >> .npmrc"
label=""
trackingaction="copy_npm_setup_command"
trackinglabel="code_instruction"
/>
<gl-sprintf-stub
message="You may also need to setup authentication using an auth token. %{linkStart}See the documentation%{linkEnd} to find out more."
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NugetInstallation renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object]"
packagetype="nuget"
/>
<code-instruction-stub
copytext="Copy NuGet Command"
instruction="foo/command"
label="NuGet Command"
trackingaction="copy_nuget_install_command"
trackinglabel="code_instruction"
/>
<h3
class="gl-font-lg"
>
Registry setup
</h3>
<code-instruction-stub
copytext="Copy NuGet Setup Command"
instruction="foo/setup"
label="Add NuGet Source"
trackingaction="copy_nuget_setup_command"
trackinglabel="code_instruction"
/>
<gl-sprintf-stub
message="For more information on the NuGet registry, %{linkStart}see the documentation%{linkEnd}."
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PypiInstallation renders all the messages 1`] = `
<div>
<installation-title-stub
options="[object Object]"
packagetype="pypi"
/>
<code-instruction-stub
copytext="Copy Pip command"
data-testid="pip-command"
instruction="pip install"
label="Pip Command"
trackingaction="copy_pip_install_command"
trackinglabel="code_instruction"
/>
<h3
class="gl-font-lg"
>
Registry setup
</h3>
<p>
<gl-sprintf-stub
message="If you haven't already done so, you will need to add the below to your %{codeStart}.pypirc%{codeEnd} file."
/>
</p>
<code-instruction-stub
copytext="Copy .pypirc content"
data-testid="pypi-setup-content"
instruction="python setup"
label=""
multiline="true"
trackingaction="copy_pypi_setup_command"
trackinglabel="code_instruction"
/>
<gl-sprintf-stub
message="For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
/>
</div>
`;
import { GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { mavenPackage, conanPackage, nugetPackage, npmPackage } from 'jest/packages/mock_data';
import component from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
describe('Package Additional Metadata', () => {
let wrapper;
const defaultProps = {
packageEntity: { ...mavenPackage },
};
const mountComponent = (props) => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
stubs: {
DetailsRow,
GlSprintf,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findTitle = () => wrapper.find('[data-testid="title"]');
const findMainArea = () => wrapper.find('[data-testid="main"]');
const findNugetSource = () => wrapper.find('[data-testid="nuget-source"]');
const findNugetLicense = () => wrapper.find('[data-testid="nuget-license"]');
const findConanRecipe = () => wrapper.find('[data-testid="conan-recipe"]');
const findMavenApp = () => wrapper.find('[data-testid="maven-app"]');
const findMavenGroup = () => wrapper.find('[data-testid="maven-group"]');
const findElementLink = (container) => container.find(GlLink);
it('has the correct title', () => {
mountComponent();
const title = findTitle();
expect(title.exists()).toBe(true);
expect(title.text()).toBe('Additional Metadata');
});
describe.each`
packageEntity | visible | metadata
${mavenPackage} | ${true} | ${'maven_metadatum'}
${conanPackage} | ${true} | ${'conan_metadatum'}
${nugetPackage} | ${true} | ${'nuget_metadatum'}
${npmPackage} | ${false} | ${null}
`('Component visibility', ({ packageEntity, visible, metadata }) => {
it(`Is ${visible} that the component markup is visible when the package is ${packageEntity.package_type}`, () => {
mountComponent({ packageEntity });
expect(findTitle().exists()).toBe(visible);
expect(findMainArea().exists()).toBe(visible);
});
it(`The component is hidden if ${metadata} is missing`, () => {
mountComponent({ packageEntity: { ...packageEntity, [metadata]: null } });
expect(findTitle().exists()).toBe(false);
expect(findMainArea().exists()).toBe(false);
});
});
describe('nuget metadata', () => {
beforeEach(() => {
mountComponent({ packageEntity: nugetPackage });
});
it.each`
name | finderFunction | text | link | icon
${'source'} | ${findNugetSource} | ${'Source project located at project-foo-url'} | ${'project_url'} | ${'project'}
${'license'} | ${findNugetLicense} | ${'License information located at license-foo-url'} | ${'license_url'} | ${'license'}
`('$name element', ({ finderFunction, text, link, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
expect(findElementLink(element).attributes('href')).toBe(nugetPackage.nuget_metadatum[link]);
});
});
describe('conan metadata', () => {
beforeEach(() => {
mountComponent({ packageEntity: conanPackage });
});
it.each`
name | finderFunction | text | icon
${'recipe'} | ${findConanRecipe} | ${'Recipe: conan-package/1.0.0@conan+conan-package/stable'} | ${'information-o'}
`('$name element', ({ finderFunction, text, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
});
});
describe('maven metadata', () => {
beforeEach(() => {
mountComponent();
});
it.each`
name | finderFunction | text | icon
${'app'} | ${findMavenApp} | ${'App name: test-app'} | ${'information-o'}
${'group'} | ${findMavenGroup} | ${'App group: com.test.app'} | ${'information-o'}
`('$name element', ({ finderFunction, text, icon }) => {
const element = finderFunction();
expect(element.exists()).toBe(true);
expect(element.text()).toBe(text);
expect(element.props('icon')).toBe(icon);
});
});
});
import { GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { registryUrl as composerHelpPath } from 'jest/packages/details/mock_data';
import { composerPackage as packageEntity } from 'jest/packages/mock_data';
import { TrackingActions } from '~/packages/details/constants';
import ComposerInstallation from '~/packages_and_registries/package_registry/components/details/composer_installation.vue';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('ComposerInstallation', () => {
let wrapper;
let store;
const composerRegistryIncludeStr = 'foo/registry';
const composerPackageIncludeStr = 'foo/package';
const createStore = (groupExists = true) => {
store = new Vuex.Store({
state: { packageEntity, composerHelpPath },
getters: {
composerRegistryInclude: () => composerRegistryIncludeStr,
composerPackageInclude: () => composerPackageIncludeStr,
groupExists: () => groupExists,
},
});
};
const findRootNode = () => wrapper.find('[data-testid="root-node"]');
const findRegistryInclude = () => wrapper.find('[data-testid="registry-include"]');
const findPackageInclude = () => wrapper.find('[data-testid="package-include"]');
const findHelpText = () => wrapper.find('[data-testid="help-text"]');
const findHelpLink = () => wrapper.find(GlLink);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent() {
wrapper = shallowMount(ComposerInstallation, {
localVue,
store,
stubs: {
GlSprintf,
},
});
}
afterEach(() => {
wrapper.destroy();
});
describe('install command switch', () => {
it('has the installation title component', () => {
createStore();
createComponent();
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'composer',
options: [{ value: 'composer', label: 'Show Composer commands' }],
});
});
});
describe('registry include command', () => {
beforeEach(() => {
createStore();
createComponent();
});
it('uses code_instructions', () => {
const registryIncludeCommand = findRegistryInclude();
expect(registryIncludeCommand.exists()).toBe(true);
expect(registryIncludeCommand.props()).toMatchObject({
instruction: composerRegistryIncludeStr,
copyText: 'Copy registry include',
trackingAction: TrackingActions.COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
});
});
it('has the correct title', () => {
expect(findRegistryInclude().props('label')).toBe('Add composer registry');
});
});
describe('package include command', () => {
beforeEach(() => {
createStore();
createComponent();
});
it('uses code_instructions', () => {
const registryIncludeCommand = findPackageInclude();
expect(registryIncludeCommand.exists()).toBe(true);
expect(registryIncludeCommand.props()).toMatchObject({
instruction: composerPackageIncludeStr,
copyText: 'Copy require package include',
trackingAction: TrackingActions.COPY_COMPOSER_PACKAGE_INCLUDE_COMMAND,
});
});
it('has the correct title', () => {
expect(findPackageInclude().props('label')).toBe('Install package version');
});
it('has the correct help text', () => {
expect(findHelpText().text()).toBe(
'For more information on Composer packages in GitLab, see the documentation.',
);
expect(findHelpLink().attributes()).toMatchObject({
href: composerHelpPath,
target: '_blank',
});
});
});
describe('root node', () => {
it('is normally rendered', () => {
createStore();
createComponent();
expect(findRootNode().exists()).toBe(true);
});
it('is not rendered when the group does not exist', () => {
createStore(false);
createComponent();
expect(findRootNode().exists()).toBe(false);
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { registryUrl as conanPath } from 'jest/packages/details/mock_data';
import { conanPackage as packageEntity } from 'jest/packages/mock_data';
import ConanInstallation from '~/packages_and_registries/package_registry/components/details/conan_installation.vue';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('ConanInstallation', () => {
let wrapper;
const conanInstallationCommandStr = 'foo/command';
const conanSetupCommandStr = 'foo/setup';
const store = new Vuex.Store({
state: {
packageEntity,
conanPath,
},
getters: {
conanInstallationCommand: () => conanInstallationCommandStr,
conanSetupCommand: () => conanSetupCommandStr,
},
});
const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent() {
wrapper = shallowMount(ConanInstallation, {
localVue,
store,
});
}
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('install command switch', () => {
it('has the installation title component', () => {
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'conan',
options: [{ value: 'conan', label: 'Show Conan commands' }],
});
});
});
describe('installation commands', () => {
it('renders the correct command', () => {
expect(findCodeInstructions().at(0).props('instruction')).toBe(conanInstallationCommandStr);
});
});
describe('setup commands', () => {
it('renders the correct command', () => {
expect(findCodeInstructions().at(1).props('instruction')).toBe(conanSetupCommandStr);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { dependencyLinks } from 'jest/packages/mock_data';
import DependencyRow from '~/packages_and_registries/package_registry/components/details/dependency_row.vue';
describe('DependencyRow', () => {
let wrapper;
const { withoutFramework, withoutVersion, fullLink } = dependencyLinks;
function createComponent({ dependencyLink = fullLink } = {}) {
wrapper = shallowMount(DependencyRow, {
propsData: {
dependency: dependencyLink,
},
});
}
const dependencyVersion = () => wrapper.find('[data-testid="version-pattern"]');
const dependencyFramework = () => wrapper.find('[data-testid="target-framework"]');
afterEach(() => {
wrapper.destroy();
});
describe('renders', () => {
it('full dependency', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
describe('version', () => {
it('does not render any version information when not supplied', () => {
createComponent({ dependencyLink: withoutVersion });
expect(dependencyVersion().exists()).toBe(false);
});
it('does render version info when it exists', () => {
createComponent();
expect(dependencyVersion().exists()).toBe(true);
expect(dependencyVersion().text()).toBe(fullLink.version_pattern);
});
});
describe('target framework', () => {
it('does not render any framework information when not supplied', () => {
createComponent({ dependencyLink: withoutFramework });
expect(dependencyFramework().exists()).toBe(false);
});
it('does render framework info when it exists', () => {
createComponent();
expect(dependencyFramework().exists()).toBe(true);
expect(dependencyFramework().text()).toBe(`(${fullLink.target_framework})`);
});
});
});
import { shallowMount } from '@vue/test-utils';
import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
describe('FileSha', () => {
let wrapper;
const defaultProps = { sha: 'foo', title: 'bar' };
function createComponent() {
wrapper = shallowMount(FileSha, {
propsData: {
...defaultProps,
},
stubs: {
ClipboardButton,
DetailsRow,
},
});
}
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount } from '@vue/test-utils';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import PersistedDropdownSelection from '~/vue_shared/components/registry/persisted_dropdown_selection.vue';
describe('InstallationTitle', () => {
let wrapper;
const defaultProps = { packageType: 'foo', options: [{ value: 'foo', label: 'bar' }] };
const findPersistedDropdownSelection = () => wrapper.findComponent(PersistedDropdownSelection);
const findTitle = () => wrapper.find('h3');
function createComponent({ props = {} } = {}) {
wrapper = shallowMount(InstallationTitle, {
propsData: {
...defaultProps,
...props,
},
});
}
afterEach(() => {
wrapper.destroy();
});
it('has a title', () => {
createComponent();
expect(findTitle().exists()).toBe(true);
expect(findTitle().text()).toBe('Installation');
});
describe('persisted dropdown selection', () => {
it('exists', () => {
createComponent();
expect(findPersistedDropdownSelection().exists()).toBe(true);
});
it('has the correct props', () => {
createComponent();
expect(findPersistedDropdownSelection().props()).toMatchObject({
storageKey: 'package_foo_installation_instructions',
options: defaultProps.options,
});
});
it('on change event emits a change event', () => {
createComponent();
findPersistedDropdownSelection().vm.$emit('change', 'baz');
expect(wrapper.emitted('change')).toEqual([['baz']]);
});
});
});
import { shallowMount } from '@vue/test-utils';
import {
conanPackage,
mavenPackage,
npmPackage,
nugetPackage,
pypiPackage,
composerPackage,
terraformModule,
} from 'jest/packages/mock_data';
import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue';
import ComposerInstallation from '~/packages_and_registries/package_registry/components/details/composer_installation.vue';
import ConanInstallation from '~/packages_and_registries/package_registry/components/details/conan_installation.vue';
import InstallationCommands from '~/packages_and_registries/package_registry/components/details/installation_commands.vue';
import MavenInstallation from '~/packages_and_registries/package_registry/components/details/maven_installation.vue';
import NpmInstallation from '~/packages_and_registries/package_registry/components/details/npm_installation.vue';
import NugetInstallation from '~/packages_and_registries/package_registry/components/details/nuget_installation.vue';
import PypiInstallation from '~/packages_and_registries/package_registry/components/details/pypi_installation.vue';
describe('InstallationCommands', () => {
let wrapper;
function createComponent(propsData) {
wrapper = shallowMount(InstallationCommands, {
propsData,
});
}
const npmInstallation = () => wrapper.find(NpmInstallation);
const mavenInstallation = () => wrapper.find(MavenInstallation);
const conanInstallation = () => wrapper.find(ConanInstallation);
const nugetInstallation = () => wrapper.find(NugetInstallation);
const pypiInstallation = () => wrapper.find(PypiInstallation);
const composerInstallation = () => wrapper.find(ComposerInstallation);
const terraformInstallation = () => wrapper.findComponent(TerraformInstallation);
afterEach(() => {
wrapper.destroy();
});
describe('installation instructions', () => {
describe.each`
packageEntity | selector
${conanPackage} | ${conanInstallation}
${mavenPackage} | ${mavenInstallation}
${npmPackage} | ${npmInstallation}
${nugetPackage} | ${nugetInstallation}
${pypiPackage} | ${pypiInstallation}
${composerPackage} | ${composerInstallation}
${terraformModule} | ${terraformInstallation}
`('renders', ({ packageEntity, selector }) => {
it(`${packageEntity.package_type} instructions exist`, () => {
createComponent({ packageEntity });
expect(selector()).toExist();
});
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
import { registryUrl as mavenPath } from 'jest/packages/details/mock_data';
import { mavenPackage as packageEntity } from 'jest/packages/mock_data';
import { TrackingActions } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import MavenInstallation from '~/packages_and_registries/package_registry/components/details/maven_installation.vue';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('MavenInstallation', () => {
let wrapper;
const xmlCodeBlock = 'foo/xml';
const mavenCommandStr = 'foo/command';
const mavenSetupXml = 'foo/setup';
const gradleGroovyInstallCommandText = 'foo/gradle/groovy/install';
const gradleGroovyAddSourceCommandText = 'foo/gradle/groovy/add/source';
const gradleKotlinInstallCommandText = 'foo/gradle/kotlin/install';
const gradleKotlinAddSourceCommandText = 'foo/gradle/kotlin/add/source';
const store = new Vuex.Store({
state: {
packageEntity,
mavenPath,
},
getters: {
mavenInstallationXml: () => xmlCodeBlock,
mavenInstallationCommand: () => mavenCommandStr,
mavenSetupXml: () => mavenSetupXml,
gradleGroovyInstalCommand: () => gradleGroovyInstallCommandText,
gradleGroovyAddSourceCommand: () => gradleGroovyAddSourceCommandText,
gradleKotlinInstalCommand: () => gradleKotlinInstallCommandText,
gradleKotlinAddSourceCommand: () => gradleKotlinAddSourceCommandText,
},
});
const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent({ data = {} } = {}) {
wrapper = shallowMount(MavenInstallation, {
localVue,
store,
data() {
return data;
},
});
}
afterEach(() => {
wrapper.destroy();
});
describe('install command switch', () => {
it('has the installation title component', () => {
createComponent();
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'maven',
options: [
{ value: 'maven', label: 'Maven XML' },
{ value: 'groovy', label: 'Gradle Groovy DSL' },
{ value: 'kotlin', label: 'Gradle Kotlin DSL' },
],
});
});
it('on change event updates the instructions to show', async () => {
createComponent();
expect(findCodeInstructions().at(0).props('instruction')).toBe(xmlCodeBlock);
findInstallationTitle().vm.$emit('change', 'groovy');
await nextTick();
expect(findCodeInstructions().at(0).props('instruction')).toBe(
gradleGroovyInstallCommandText,
);
});
});
describe('maven', () => {
beforeEach(() => {
createComponent();
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('installation commands', () => {
it('renders the correct xml block', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: xmlCodeBlock,
multiline: true,
trackingAction: TrackingActions.COPY_MAVEN_XML,
});
});
it('renders the correct maven command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: mavenCommandStr,
multiline: false,
trackingAction: TrackingActions.COPY_MAVEN_COMMAND,
});
});
});
describe('setup commands', () => {
it('renders the correct xml block', () => {
expect(findCodeInstructions().at(2).props()).toMatchObject({
instruction: mavenSetupXml,
multiline: true,
trackingAction: TrackingActions.COPY_MAVEN_SETUP,
});
});
});
});
describe('groovy', () => {
beforeEach(() => {
createComponent({ data: { instructionType: 'groovy' } });
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('installation commands', () => {
it('renders the gradle install command', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: gradleGroovyInstallCommandText,
multiline: false,
trackingAction: TrackingActions.COPY_GRADLE_INSTALL_COMMAND,
});
});
});
describe('setup commands', () => {
it('renders the correct gradle command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: gradleGroovyAddSourceCommandText,
multiline: true,
trackingAction: TrackingActions.COPY_GRADLE_ADD_TO_SOURCE_COMMAND,
});
});
});
});
describe('kotlin', () => {
beforeEach(() => {
createComponent({ data: { instructionType: 'kotlin' } });
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('installation commands', () => {
it('renders the gradle install command', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: gradleKotlinInstallCommandText,
multiline: false,
trackingAction: TrackingActions.COPY_KOTLIN_INSTALL_COMMAND,
});
});
});
describe('setup commands', () => {
it('renders the correct gradle command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: gradleKotlinAddSourceCommandText,
multiline: true,
trackingAction: TrackingActions.COPY_KOTLIN_ADD_TO_SOURCE_COMMAND,
});
});
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
import { registryUrl as nugetPath } from 'jest/packages/details/mock_data';
import { npmPackage as packageEntity } from 'jest/packages/mock_data';
import { TrackingActions } from '~/packages/details/constants';
import { npmInstallationCommand, npmSetupCommand } from '~/packages/details/store/getters';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import NpmInstallation from '~/packages_and_registries/package_registry/components/details/npm_installation.vue';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('NpmInstallation', () => {
let wrapper;
const npmInstallationCommandLabel = 'npm i @Test/package';
const yarnInstallationCommandLabel = 'yarn add @Test/package';
const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent({ data = {} } = {}) {
const store = new Vuex.Store({
state: {
packageEntity,
nugetPath,
},
getters: {
npmInstallationCommand,
npmSetupCommand,
},
});
wrapper = shallowMount(NpmInstallation, {
localVue,
store,
data() {
return data;
},
});
}
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('install command switch', () => {
it('has the installation title component', () => {
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'npm',
options: [
{ value: 'npm', label: 'Show NPM commands' },
{ value: 'yarn', label: 'Show Yarn commands' },
],
});
});
it('on change event updates the instructions to show', async () => {
createComponent();
expect(findCodeInstructions().at(0).props('instruction')).toBe(npmInstallationCommandLabel);
findInstallationTitle().vm.$emit('change', 'yarn');
await nextTick();
expect(findCodeInstructions().at(0).props('instruction')).toBe(yarnInstallationCommandLabel);
});
});
describe('npm', () => {
beforeEach(() => {
createComponent();
});
it('renders the correct installation command', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: npmInstallationCommandLabel,
multiline: false,
trackingAction: TrackingActions.COPY_NPM_INSTALL_COMMAND,
});
});
it('renders the correct setup command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo @Test:registry=undefined/ >> .npmrc',
multiline: false,
trackingAction: TrackingActions.COPY_NPM_SETUP_COMMAND,
});
});
});
describe('yarn', () => {
beforeEach(() => {
createComponent({ data: { instructionType: 'yarn' } });
});
it('renders the correct setup command', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: yarnInstallationCommandLabel,
multiline: false,
trackingAction: TrackingActions.COPY_YARN_INSTALL_COMMAND,
});
});
it('renders the correct registry command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: 'echo \\"@Test:registry\\" \\"undefined/\\" >> .yarnrc',
multiline: false,
trackingAction: TrackingActions.COPY_YARN_SETUP_COMMAND,
});
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { registryUrl as nugetPath } from 'jest/packages/details/mock_data';
import { nugetPackage as packageEntity } from 'jest/packages/mock_data';
import { TrackingActions } from '~/packages/details/constants';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import NugetInstallation from '~/packages_and_registries/package_registry/components/details/nuget_installation.vue';
import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('NugetInstallation', () => {
let wrapper;
const nugetInstallationCommandStr = 'foo/command';
const nugetSetupCommandStr = 'foo/setup';
const store = new Vuex.Store({
state: {
packageEntity,
nugetPath,
},
getters: {
nugetInstallationCommand: () => nugetInstallationCommandStr,
nugetSetupCommand: () => nugetSetupCommandStr,
},
});
const findCodeInstructions = () => wrapper.findAll(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent() {
wrapper = shallowMount(NugetInstallation, {
localVue,
store,
});
}
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('install command switch', () => {
it('has the installation title component', () => {
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'nuget',
options: [{ value: 'nuget', label: 'Show Nuget commands' }],
});
});
});
describe('installation commands', () => {
it('renders the correct command', () => {
expect(findCodeInstructions().at(0).props()).toMatchObject({
instruction: nugetInstallationCommandStr,
trackingAction: TrackingActions.COPY_NUGET_INSTALL_COMMAND,
});
});
});
describe('setup commands', () => {
it('renders the correct command', () => {
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: nugetSetupCommandStr,
trackingAction: TrackingActions.COPY_NUGET_SETUP_COMMAND,
});
});
});
});
import { GlDropdown, GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import stubChildren from 'helpers/stub_children';
import { npmFiles, mavenFiles } from 'jest/packages/mock_data';
import component from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
describe('Package Files', () => {
let wrapper;
const findAllRows = () => wrapper.findAll('[data-testid="file-row"');
const findFirstRow = () => findAllRows().at(0);
const findSecondRow = () => findAllRows().at(1);
const findFirstRowDownloadLink = () => findFirstRow().find('[data-testid="download-link"]');
const findFirstRowCommitLink = () => findFirstRow().find('[data-testid="commit-link"]');
const findSecondRowCommitLink = () => findSecondRow().find('[data-testid="commit-link"]');
const findFirstRowFileIcon = () => findFirstRow().find(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().find(TimeAgoTooltip);
const findFirstActionMenu = () => findFirstRow().findComponent(GlDropdown);
const findActionMenuDelete = () => findFirstActionMenu().find('[data-testid="delete-file"]');
const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
const findFirstRowShaComponent = (id) => wrapper.find(`[data-testid="${id}"]`);
const createComponent = ({ packageFiles = npmFiles, canDelete = true } = {}) => {
wrapper = mount(component, {
propsData: {
packageFiles,
canDelete,
},
stubs: {
...stubChildren(component),
GlTable: false,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('rows', () => {
it('renders a single file for an npm package', () => {
createComponent();
expect(findAllRows()).toHaveLength(1);
});
it('renders multiple files for a package that contains more than one file', () => {
createComponent({ packageFiles: mavenFiles });
expect(findAllRows()).toHaveLength(2);
});
});
describe('link', () => {
it('exists', () => {
createComponent();
expect(findFirstRowDownloadLink().exists()).toBe(true);
});
it('has the correct attrs bound', () => {
createComponent();
expect(findFirstRowDownloadLink().attributes('href')).toBe(npmFiles[0].download_path);
});
it('emits "download-file" event on click', () => {
createComponent();
findFirstRowDownloadLink().vm.$emit('click');
expect(wrapper.emitted('download-file')).toEqual([[]]);
});
});
describe('file-icon', () => {
it('exists', () => {
createComponent();
expect(findFirstRowFileIcon().exists()).toBe(true);
});
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowFileIcon().props('fileName')).toBe(npmFiles[0].file_name);
});
});
describe('time-ago tooltip', () => {
it('exists', () => {
createComponent();
expect(findFirstRowCreatedAt().exists()).toBe(true);
});
it('has the correct props bound', () => {
createComponent();
expect(findFirstRowCreatedAt().props('time')).toBe(npmFiles[0].created_at);
});
});
describe('commit', () => {
describe('when package file has a pipeline associated', () => {
it('exists', () => {
createComponent();
expect(findFirstRowCommitLink().exists()).toBe(true);
});
it('the link points to the commit url', () => {
createComponent();
expect(findFirstRowCommitLink().attributes('href')).toBe(
npmFiles[0].pipelines[0].project.commit_url,
);
});
it('the text is git_commit_message', () => {
createComponent();
expect(findFirstRowCommitLink().text()).toBe(npmFiles[0].pipelines[0].git_commit_message);
});
});
describe('when package file has no pipeline associated', () => {
it('does not exist', () => {
createComponent({ packageFiles: mavenFiles });
expect(findFirstRowCommitLink().exists()).toBe(false);
});
});
describe('when only one file lacks an associated pipeline', () => {
it('renders the commit when it exists and not otherwise', () => {
createComponent({ packageFiles: [npmFiles[0], mavenFiles[0]] });
expect(findFirstRowCommitLink().exists()).toBe(true);
expect(findSecondRowCommitLink().exists()).toBe(false);
});
});
describe('action menu', () => {
describe('when the user can delete', () => {
it('exists', () => {
createComponent();
expect(findFirstActionMenu().exists()).toBe(true);
});
describe('menu items', () => {
describe('delete file', () => {
it('exists', () => {
createComponent();
expect(findActionMenuDelete().exists()).toBe(true);
});
it('emits a delete event when clicked', () => {
createComponent();
findActionMenuDelete().vm.$emit('click');
const [[{ id }]] = wrapper.emitted('delete-file');
expect(id).toBe(npmFiles[0].id);
});
});
});
});
describe('when the user can not delete', () => {
const canDelete = false;
it('does not exist', () => {
createComponent({ canDelete });
expect(findFirstActionMenu().exists()).toBe(false);
});
});
});
});
describe('additional details', () => {
describe('details toggle button', () => {
it('exists', () => {
createComponent();
expect(findFirstToggleDetailsButton().exists()).toBe(true);
});
it('is hidden when no details is present', () => {
const [{ ...noShaFile }] = npmFiles;
noShaFile.file_sha256 = null;
noShaFile.file_md5 = null;
noShaFile.file_sha1 = null;
createComponent({ packageFiles: [noShaFile] });
expect(findFirstToggleDetailsButton().exists()).toBe(false);
});
it('toggles the details row', async () => {
createComponent();
expect(findFirstToggleDetailsButton().props('icon')).toBe('angle-down');
findFirstToggleDetailsButton().vm.$emit('click');
await nextTick();
expect(findFirstRowShaComponent('sha-256').exists()).toBe(true);
expect(findFirstToggleDetailsButton().props('icon')).toBe('angle-up');
findFirstToggleDetailsButton().vm.$emit('click');
await nextTick();
expect(findFirstRowShaComponent('sha-256').exists()).toBe(false);
expect(findFirstToggleDetailsButton().props('icon')).toBe('angle-down');
});
});
describe('file shas', () => {
const showShaFiles = () => {
findFirstToggleDetailsButton().vm.$emit('click');
return nextTick();
};
it.each`
selector | title | sha
${'sha-256'} | ${'SHA-256'} | ${'file_sha256'}
${'md5'} | ${'MD5'} | ${'file_md5'}
${'sha-1'} | ${'SHA-1'} | ${'file_sha1'}
`('has a $title row', async ({ selector, title, sha }) => {
createComponent();
await showShaFiles();
expect(findFirstRowShaComponent(selector).props()).toMatchObject({
title,
sha,
});
});
it('does not display a row when the data is missing', async () => {
const [{ ...missingMd5 }] = npmFiles;
missingMd5.file_md5 = null;
createComponent({ packageFiles: [missingMd5] });
await showShaFiles();
expect(findFirstRowShaComponent('md5').exists()).toBe(false);
});
});
});
});
import { GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import { mavenPackage, mockPipelineInfo } from 'jest/packages/mock_data';
import { HISTORY_PIPELINES_LIMIT } from '~/packages/details/constants';
import component from '~/packages_and_registries/package_registry/components/details/package_history.vue';
import HistoryItem from '~/vue_shared/components/registry/history_item.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
describe('Package History', () => {
let wrapper;
const defaultProps = {
projectName: 'baz project',
packageEntity: { ...mavenPackage },
};
const createPipelines = (amount) =>
[...Array(amount)].map((x, index) => ({ ...mockPipelineInfo, id: index + 1 }));
const mountComponent = (props) => {
wrapper = shallowMount(component, {
propsData: { ...defaultProps, ...props },
stubs: {
HistoryItem: stubComponent(HistoryItem, {
template: '<div data-testid="history-element"><slot></slot></div>',
}),
GlSprintf,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findHistoryElement = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findElementLink = (container) => container.find(GlLink);
const findElementTimeAgo = (container) => container.find(TimeAgoTooltip);
const findTitle = () => wrapper.find('[data-testid="title"]');
const findTimeline = () => wrapper.find('[data-testid="timeline"]');
it('has the correct title', () => {
mountComponent();
const title = findTitle();
expect(title.exists()).toBe(true);
expect(title.text()).toBe('History');
});
it('has a timeline container', () => {
mountComponent();
const title = findTimeline();
expect(title.exists()).toBe(true);
expect(title.classes()).toEqual(
expect.arrayContaining(['timeline', 'main-notes-list', 'notes']),
);
});
describe.each`
name | amount | icon | text | timeAgoTooltip | link
${'created-on'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'clock'} | ${'Test package version 1.0.0 was first created'} | ${mavenPackage.created_at} | ${null}
${'first-pipeline-commit'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'commit'} | ${'Created by commit #sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url}
${'first-pipeline-pipeline'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pipeline'} | ${'Built by pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url}
${'published'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null}
${'archived'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'history'} | ${'Package has 1 archived update'} | ${null} | ${null}
${'archived'} | ${HISTORY_PIPELINES_LIMIT + 3} | ${'history'} | ${'Package has 2 archived updates'} | ${null} | ${null}
${'pipeline-entry'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pencil'} | ${'Package updated by commit #sha-baz on branch branch-name, built by pipeline #3, and published to the registry'} | ${mavenPackage.created_at} | ${mockPipelineInfo.project.commit_url}
`(
'with $amount pipelines history element $name',
({ name, icon, text, timeAgoTooltip, link, amount }) => {
let element;
beforeEach(() => {
mountComponent({
packageEntity: { ...mavenPackage, pipelines: createPipelines(amount) },
});
element = findHistoryElement(name);
});
it('exists', () => {
expect(element.exists()).toBe(true);
});
it('has the correct icon', () => {
expect(element.props('icon')).toBe(icon);
});
it('has the correct text', () => {
expect(element.text()).toBe(text);
});
it('time-ago tooltip', () => {
const timeAgo = findElementTimeAgo(element);
const exist = Boolean(timeAgoTooltip);
expect(timeAgo.exists()).toBe(exist);
if (exist) {
expect(timeAgo.props('time')).toBe(timeAgoTooltip);
}
});
it('link', () => {
const linkElement = findElementLink(element);
const exist = Boolean(link);
expect(linkElement.exists()).toBe(exist);
if (exist) {
expect(linkElement.attributes('href')).toBe(link);
}
});
},
);
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { pypiPackage as packageEntity } from 'jest/packages/mock_data';
import InstallationTitle from '~/packages_and_registries/package_registry/components/details/installation_title.vue';
import PypiInstallation from '~/packages_and_registries/package_registry/components/details/pypi_installation.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('PypiInstallation', () => {
let wrapper;
const pipCommandStr = 'pip install';
const pypiSetupStr = 'python setup';
const store = new Vuex.Store({
state: {
packageEntity,
pypiHelpPath: 'foo',
},
getters: {
pypiPipCommand: () => pipCommandStr,
pypiSetupCommand: () => pypiSetupStr,
},
});
const pipCommand = () => wrapper.find('[data-testid="pip-command"]');
const setupInstruction = () => wrapper.find('[data-testid="pypi-setup-content"]');
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
function createComponent() {
wrapper = shallowMount(PypiInstallation, {
localVue,
store,
});
}
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('install command switch', () => {
it('has the installation title component', () => {
expect(findInstallationTitle().exists()).toBe(true);
expect(findInstallationTitle().props()).toMatchObject({
packageType: 'pypi',
options: [{ value: 'pypi', label: 'Show PyPi commands' }],
});
});
});
it('renders all the messages', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('installation commands', () => {
it('renders the correct pip command', () => {
expect(pipCommand().props('instruction')).toBe(pipCommandStr);
});
});
describe('setup commands', () => {
it('renders the correct setup block', () => {
expect(setupInstruction().props('instruction')).toBe(pypiSetupStr);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment