diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-27 12:13:56 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-27 12:13:56 +0000 |
commit | cddf2db96b2280ad995b589b70ff23ff77cceb7b (patch) | |
tree | a880d70b014d5bc776e439013605bbb77bb76336 | |
parent | 1b044a566c8c1b7d835dacbd2d7471d7fed32082 (diff) | |
download | gitlab-ce-cddf2db96b2280ad995b589b70ff23ff77cceb7b.tar.gz |
Add latest changes from gitlab-org/gitlab@master
55 files changed, 159 insertions, 2643 deletions
diff --git a/app/assets/javascripts/ide/components/preview/navigator.vue b/app/assets/javascripts/ide/components/preview/navigator.vue index 838c363a6a3..96f9a85c23f 100644 --- a/app/assets/javascripts/ide/components/preview/navigator.vue +++ b/app/assets/javascripts/ide/components/preview/navigator.vue @@ -117,7 +117,7 @@ export default { class="ide-navigator-btn d-flex align-items-center d-transparent border-0 bg-transparent" @click="refresh" > - <gl-icon :size="18" name="retry" use-deprecated-sizes class="m-auto" /> + <gl-icon :size="16" name="retry" class="m-auto" /> </button> <div class="position-relative w-100 gl-ml-2"> <input diff --git a/app/assets/javascripts/packages/details/components/additional_metadata.vue b/app/assets/javascripts/packages/details/components/additional_metadata.vue deleted file mode 100644 index 4e99099b0a1..00000000000 --- a/app/assets/javascripts/packages/details/components/additional_metadata.vue +++ /dev/null @@ -1,94 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; -import { PackageType } from '../../shared/constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/app.vue b/app/assets/javascripts/packages/details/components/app.vue index 59da32e6666..462252ce944 100644 --- a/app/assets/javascripts/packages/details/components/app.vue +++ b/app/assets/javascripts/packages/details/components/app.vue @@ -1,6 +1,5 @@ <script> import { - GlBadge, GlButton, GlModal, GlModalDirective, @@ -14,36 +13,30 @@ import { mapActions, mapState } from 'vuex'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import { objectToQuery } from '~/lib/utils/url_utility'; import { s__, __ } from '~/locale'; +import TerraformTitle from '~/packages_and_registries/infrastructure_registry/components/details_title.vue'; +import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; import Tracking from '~/tracking'; import PackageListRow from '../../shared/components/package_list_row.vue'; import PackagesListLoader from '../../shared/components/packages_list_loader.vue'; -import { PackageType, TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants'; +import { TrackingActions, SHOW_DELETE_SUCCESS_ALERT } from '../../shared/constants'; import { packageTypeToTrackCategory } from '../../shared/utils'; -import AdditionalMetadata from './additional_metadata.vue'; -import DependencyRow from './dependency_row.vue'; -import InstallationCommands from './installation_commands.vue'; import PackageFiles from './package_files.vue'; import PackageHistory from './package_history.vue'; export default { name: 'PackagesApp', components: { - GlBadge, GlButton, GlEmptyState, GlModal, GlTab, GlTabs, GlSprintf, - PackageTitle: () => import('./package_title.vue'), - TerraformTitle: () => - import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'), + TerraformTitle, PackagesListLoader, PackageListRow, - DependencyRow, PackageHistory, - AdditionalMetadata, - InstallationCommands, + TerraformInstallation, PackageFiles, }, directives: { @@ -51,12 +44,6 @@ export default { GlModal: GlModalDirective, }, mixins: [Tracking.mixin()], - inject: { - titleComponent: { - default: 'PackageTitle', - from: 'titleComponent', - }, - }, trackingActions: { ...TrackingActions }, data() { return { @@ -87,15 +74,6 @@ export default { hasVersions() { return this.packageEntity.versions?.length > 0; }, - packageDependencies() { - return this.packageEntity.dependency_links || []; - }, - showDependencies() { - return this.packageEntity.package_type === PackageType.NUGET; - }, - showFiles() { - return this.packageEntity?.package_type !== PackageType.COMPOSER; - }, }, methods: { ...mapActions(['deletePackage', 'fetchPackageVersions', 'deletePackageFile']), @@ -167,7 +145,7 @@ export default { /> <div v-else class="packages-app"> - <component :is="titleComponent"> + <terraform-title> <template #delete-button> <gl-button v-if="canDelete" @@ -180,24 +158,16 @@ export default { {{ __('Delete') }} </gl-button> </template> - </component> + </terraform-title> <gl-tabs> <gl-tab :title="__('Detail')"> <div data-qa-selector="package_information_content"> <package-history :package-entity="packageEntity" :project-name="projectName" /> - - <installation-commands - :package-entity="packageEntity" - :npm-path="npmPath" - :npm-help-path="npmHelpPath" - /> - - <additional-metadata :package-entity="packageEntity" /> + <terraform-installation /> </div> <package-files - v-if="showFiles" :package-files="packageFiles" :can-delete="canDelete" @download-file="track($options.trackingActions.PULL_PACKAGE)" @@ -205,27 +175,6 @@ export default { /> </gl-tab> - <gl-tab v-if="showDependencies" title-item-class="js-dependencies-tab"> - <template #title> - <span>{{ __('Dependencies') }}</span> - <gl-badge size="sm" data-testid="dependencies-badge">{{ - packageDependencies.length - }}</gl-badge> - </template> - - <template v-if="packageDependencies.length > 0"> - <dependency-row - v-for="(dep, index) in packageDependencies" - :key="index" - :dependency="dep" - /> - </template> - - <p v-else class="gl-mt-3" data-testid="no-dependencies-message"> - {{ s__('PackageRegistry|This NuGet package has no dependencies.') }} - </p> - </gl-tab> - <gl-tab :title="__('Other versions')" title-item-class="js-versions-tab" @@ -254,7 +203,6 @@ export default { <gl-modal ref="deleteModal" - class="js-delete-modal" modal-id="delete-modal" :action-primary="$options.modal.packageDeletePrimaryAction" :action-cancel="$options.modal.cancelAction" diff --git a/app/assets/javascripts/packages/details/components/composer_installation.vue b/app/assets/javascripts/packages/details/components/composer_installation.vue deleted file mode 100644 index bf1e5083e12..00000000000 --- a/app/assets/javascripts/packages/details/components/composer_installation.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/conan_installation.vue b/app/assets/javascripts/packages/details/components/conan_installation.vue deleted file mode 100644 index 1d855f6cf3e..00000000000 --- a/app/assets/javascripts/packages/details/components/conan_installation.vue +++ /dev/null @@ -1,59 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/dependency_row.vue b/app/assets/javascripts/packages/details/components/dependency_row.vue deleted file mode 100644 index 1a2202b23c8..00000000000 --- a/app/assets/javascripts/packages/details/components/dependency_row.vue +++ /dev/null @@ -1,35 +0,0 @@ -<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> diff --git a/app/assets/javascripts/packages/details/components/installation_commands.vue b/app/assets/javascripts/packages/details/components/installation_commands.vue deleted file mode 100644 index ed55d7fe782..00000000000 --- a/app/assets/javascripts/packages/details/components/installation_commands.vue +++ /dev/null @@ -1,55 +0,0 @@ -<script> -import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; -import { PackageType, TERRAFORM_PACKAGE_TYPE } from '../../shared/constants'; -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> diff --git a/app/assets/javascripts/packages/details/components/installation_title.vue b/app/assets/javascripts/packages/details/components/installation_title.vue deleted file mode 100644 index 43133bf7825..00000000000 --- a/app/assets/javascripts/packages/details/components/installation_title.vue +++ /dev/null @@ -1,38 +0,0 @@ -<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> diff --git a/app/assets/javascripts/packages/details/components/maven_installation.vue b/app/assets/javascripts/packages/details/components/maven_installation.vue deleted file mode 100644 index 6974de99344..00000000000 --- a/app/assets/javascripts/packages/details/components/maven_installation.vue +++ /dev/null @@ -1,153 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; - -import { TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/npm_installation.vue b/app/assets/javascripts/packages/details/components/npm_installation.vue deleted file mode 100644 index 6b0fcf5e4fe..00000000000 --- a/app/assets/javascripts/packages/details/components/npm_installation.vue +++ /dev/null @@ -1,103 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { NpmManager, TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/nuget_installation.vue b/app/assets/javascripts/packages/details/components/nuget_installation.vue deleted file mode 100644 index d5e64722f24..00000000000 --- a/app/assets/javascripts/packages/details/components/nuget_installation.vue +++ /dev/null @@ -1,58 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/assets/javascripts/packages/details/components/package_title.vue b/app/assets/javascripts/packages/details/components/package_title.vue deleted file mode 100644 index d02a7b3ec27..00000000000 --- a/app/assets/javascripts/packages/details/components/package_title.vue +++ /dev/null @@ -1,113 +0,0 @@ -<script> -/* eslint-disable vue/v-slot-style */ -import { GlIcon, GlSprintf, GlTooltipDirective, GlBadge } from '@gitlab/ui'; -import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { mapState, mapGetters } from 'vuex'; -import { numberToHumanSize } from '~/lib/utils/number_utils'; -import { __ } from '~/locale'; -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'; -import PackageTags from '../../shared/components/package_tags.vue'; - -export default { - name: 'PackageTitle', - components: { - TitleArea, - GlIcon, - GlSprintf, - PackageTags, - MetadataItem, - GlBadge, - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - mixins: [timeagoMixin], - i18n: { - packageInfo: __('v%{version} published %{timeAgo}'), - }, - data() { - return { - isDesktop: true, - }; - }, - computed: { - ...mapState(['packageEntity', 'packageFiles']), - ...mapGetters(['packageTypeDisplay', 'packagePipeline', 'packageIcon']), - hasTagsToDisplay() { - return Boolean(this.packageEntity.tags && this.packageEntity.tags.length); - }, - totalSize() { - return numberToHumanSize(this.packageFiles.reduce((acc, p) => acc + p.size, 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.web_url" - /> - </template> - - <template v-if="packagePipeline" #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" 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" - v-else-if="hasTagsToDisplay" - v-slot:[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> diff --git a/app/assets/javascripts/packages/details/components/pypi_installation.vue b/app/assets/javascripts/packages/details/components/pypi_installation.vue deleted file mode 100644 index fe4709d5feb..00000000000 --- a/app/assets/javascripts/packages/details/components/pypi_installation.vue +++ /dev/null @@ -1,71 +0,0 @@ -<script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { mapGetters, mapState } from 'vuex'; -import { s__ } from '~/locale'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue'; -import { TrackingActions, TrackingLabels } from '../constants'; - -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> diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb index f3fa79cc08c..45207581348 100644 --- a/app/graphql/types/packages/package_type.rb +++ b/app/graphql/types/packages/package_type.rb @@ -6,6 +6,8 @@ module Types graphql_name 'Package' description 'Represents a package in the Package Registry. Note that this type is in beta and susceptible to changes' + connection_type_class(Types::CountableConnectionType) + authorize :read_package field :id, ::Types::GlobalIDType[::Packages::Package], null: false, diff --git a/app/models/concerns/issue_available_features.rb b/app/models/concerns/issue_available_features.rb index 933e8b5f687..209456f8b67 100644 --- a/app/models/concerns/issue_available_features.rb +++ b/app/models/concerns/issue_available_features.rb @@ -12,7 +12,8 @@ module IssueAvailableFeatures { assignee: %w(issue incident), confidentiality: %w(issue incident), - time_tracking: %w(issue incident) + time_tracking: %w(issue incident), + move_and_clone: %w(issue incident) }.with_indifferent_access end end diff --git a/app/models/issue.rb b/app/models/issue.rb index edf5dd760c3..64254d3923c 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -542,6 +542,10 @@ class Issue < ApplicationRecord issue_type_supports?(:time_tracking) end + def supports_move_and_clone? + issue_type_supports?(:move_and_clone) + end + def email_participants_emails issue_email_participants.pluck(:email) end diff --git a/app/services/issues/clone_service.rb b/app/services/issues/clone_service.rb index cb42334fe32..c675f957cd7 100644 --- a/app/services/issues/clone_service.rb +++ b/app/services/issues/clone_service.rb @@ -8,13 +8,7 @@ module Issues @target_project = target_project @with_notes = with_notes - unless issue.can_clone?(current_user, target_project) - raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!') - end - - if target_project.pending_delete? - raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.') - end + verify_can_clone_issue!(issue, target_project) super(issue, target_project) @@ -30,6 +24,20 @@ module Issues attr_reader :target_project attr_reader :with_notes + def verify_can_clone_issue!(issue, target_project) + unless issue.supports_move_and_clone? + raise CloneError, s_('CloneIssue|Cannot clone issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type } + end + + unless issue.can_clone?(current_user, target_project) + raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!') + end + + if target_project.pending_delete? + raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.') + end + end + def update_new_entity # we don't call `super` because we want to be able to decide whether or not to copy all comments over. update_new_entity_description diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index ff78221c941..4418b4eb2bf 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -7,13 +7,7 @@ module Issues def execute(issue, target_project) @target_project = target_project - unless issue.can_move?(current_user, @target_project) - raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!') - end - - if @project == @target_project - raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!') - end + verify_can_move_issue!(issue, target_project) super @@ -32,6 +26,20 @@ module Issues attr_reader :target_project + def verify_can_move_issue!(issue, target_project) + unless issue.supports_move_and_clone? + raise MoveError, s_('MoveIssue|Cannot move issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type } + end + + unless issue.can_move?(current_user, @target_project) + raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!') + end + + if @project == @target_project + raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!') + end + end + def update_service_desk_sent_notifications return unless original_entity.from_service_desk? diff --git a/config/feature_flags/development/paginated_tree_graphql_query.yml b/config/feature_flags/development/paginated_tree_graphql_query.yml index 13096412f25..d56d8fc336c 100644 --- a/config/feature_flags/development/paginated_tree_graphql_query.yml +++ b/config/feature_flags/development/paginated_tree_graphql_query.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337214 milestone: '14.2' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/doc/api/environments.md b/doc/api/environments.md index 5187ac7c1b2..8188e0e7b85 100644 --- a/doc/api/environments.md +++ b/doc/api/environments.md @@ -64,6 +64,8 @@ Example of response "slug": "review-fix-foo-dfjre3", "external_url": "https://review-fix-foo-dfjre3.gitlab.example.com", "state": "available", + "created_at": "2019-05-25T18:55:13.252Z", + "updated_at": "2019-05-27T18:55:13.252Z", "last_deployment": { "id": 100, "iid": 34, @@ -176,7 +178,9 @@ Example response: "name": "deploy", "slug": "deploy", "external_url": "https://deploy.gitlab.example.com", - "state": "available" + "state": "available", + "created_at": "2019-05-25T18:55:13.252Z", + "updated_at": "2019-05-27T18:55:13.252Z" } ``` @@ -210,7 +214,9 @@ Example response: "name": "staging", "slug": "staging", "external_url": "https://staging.gitlab.example.com", - "state": "available" + "state": "available", + "created_at": "2019-05-25T18:55:13.252Z", + "updated_at": "2019-05-27T18:55:13.252Z" } ``` @@ -302,6 +308,8 @@ Example response: "name": "deploy", "slug": "deploy", "external_url": "https://deploy.gitlab.example.com", - "state": "stopped" + "state": "stopped", + "created_at": "2019-05-25T18:55:13.252Z", + "updated_at": "2019-05-27T18:55:13.252Z" } ``` diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 6ffc4de5eff..d2736260e0c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -6510,6 +6510,7 @@ The connection type for [`Package`](#package). | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="packageconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. | | <a id="packageconnectionedges"></a>`edges` | [`[PackageEdge]`](#packageedge) | A list of edges. | | <a id="packageconnectionnodes"></a>`nodes` | [`[Package]`](#package) | A list of nodes. | | <a id="packageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | @@ -13534,7 +13535,7 @@ Returns [`[String!]`](#string). ##### `Repository.paginatedTree` -Paginated tree of the repository. Available only when feature flag `paginated_tree_graphql_query` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. +Paginated tree of the repository. Available only when feature flag `paginated_tree_graphql_query` is enabled. This flag is enabled by default. Returns [`TreeConnection`](#treeconnection). diff --git a/doc/development/import_project.md b/doc/development/import_project.md index b5b8d7129eb..9872aa239dc 100644 --- a/doc/development/import_project.md +++ b/doc/development/import_project.md @@ -111,9 +111,9 @@ public folder (for example `/tmp/`) fixes the issue. ##### `Name can contain only letters, digits, emojis ...` ```plaintext -Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, -digit, emoji or '_'. and Path can contain only letters, digits, '_', '-' and '.'. Cannot start -with '-', end in '.git' or end in '.atom' +Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces. It must start with a letter, +digit, emoji, or '_', and Path can contain only letters, digits, '_', '-', or '.'. It cannot start +with '-', end in '.git', or end in '.atom'. ``` The project name specified in `project_path` is not valid for one of the specified reasons. diff --git a/doc/raketasks/index.md b/doc/raketasks/index.md index df71b8791f8..65a5a06e26a 100644 --- a/doc/raketasks/index.md +++ b/doc/raketasks/index.md @@ -51,3 +51,14 @@ The following Rake tasks are available for use with GitLab: | [User management](user_management.md) | Perform user management tasks. | | [Webhooks administration](web_hooks.md) | Maintain project webhooks. | | [X.509 signatures](x509_signatures.md) | Update X.509 commit signatures, which can be useful if the certificate store changed. | + +To list all available Rake tasks: + +```shell +# Omnibus GitLab +sudo gitlab-rake -vT + +# Installations from source +cd /home/git/gitlab +sudo -u git -H bundle exec rake -vT RAILS_ENV=production +``` diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index d903ce58982..6754e02f3d3 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -82,6 +82,7 @@ table.supported-languages tr td:last-child { } table.supported-languages ul { + font-size: 1em; list-style-type: none; padding-left: 0px; margin-bottom: 0px; diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md index 6fd75f5c2ce..305f66c5ec5 100644 --- a/doc/user/clusters/management_project_template.md +++ b/doc/user/clusters/management_project_template.md @@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w With a [cluster management project](management_project.md) you can manage your cluster's deployment and applications through a repository in GitLab. -The Custer Management project template provides you a baseline to get +The Cluster Management project template provides you a baseline to get started and flexibility to customize your project to your cluster's needs. For instance, you can: diff --git a/lib/api/entities/environment_basic.rb b/lib/api/entities/environment_basic.rb index 061d4739874..d9894eac147 100644 --- a/lib/api/entities/environment_basic.rb +++ b/lib/api/entities/environment_basic.rb @@ -3,7 +3,7 @@ module API module Entities class EnvironmentBasic < Grape::Entity - expose :id, :name, :slug, :external_url + expose :id, :name, :slug, :external_url, :created_at, :updated_at end end end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index a88ef5fe73e..6331aced604 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -220,12 +220,12 @@ module Gitlab # The character range \p{Alnum} overlaps with \u{00A9}-\u{1f9ff} # hence the Ruby warning. # https://gitlab.com/gitlab-org/gitlab/merge_requests/23165#not-easy-fixable - @project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9ff}_][\p{Alnum}\p{Pd}\u{00A9}-\u{1f9ff}_\. ]*\z/.freeze + @project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9ff}_][\p{Alnum}\p{Pd}\u{002B}\u{00A9}-\u{1f9ff}_\. ]*\z/.freeze end def project_name_regex_message - "can contain only letters, digits, emojis, '_', '.', dash, space. " \ - "It must start with letter, digit, emoji or '_'." + "can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces. " \ + "It must start with a letter, digit, emoji, or '_'." end def group_name_regex diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2d92e242378..a355063d5ea 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7106,6 +7106,9 @@ msgstr "" msgid "CloneIssue|Cannot clone issue to target project as it is pending deletion." msgstr "" +msgid "CloneIssue|Cannot clone issues of '%{issue_type}' type." +msgstr "" + msgid "Cloned this issue to %{path_to_project}." msgstr "" @@ -22168,6 +22171,9 @@ msgstr "" msgid "MoveIssue|Cannot move issue to project it originates from!" msgstr "" +msgid "MoveIssue|Cannot move issues of '%{issue_type}' type." +msgstr "" + msgid "Moved issue to %{label} column in the board." msgstr "" diff --git a/spec/features/projects/infrastructure_registry_spec.rb b/spec/features/projects/infrastructure_registry_spec.rb index 16dd96e6c02..ee35e02b5e8 100644 --- a/spec/features/projects/infrastructure_registry_spec.rb +++ b/spec/features/projects/infrastructure_registry_spec.rb @@ -45,10 +45,8 @@ RSpec.describe 'Infrastructure Registry' do expect(page).to have_css('.packages-app h1[data-testid="title"]', text: terraform_module.name) - page.within(%Q([name="#{terraform_module.name}"])) do - expect(page).to have_content('Provision instructions') - expect(page).to have_content('Registry setup') - end + expect(page).to have_content('Provision instructions') + expect(page).to have_content('Registry setup') end end diff --git a/spec/fixtures/api/schemas/graphql/packages/package_details.json b/spec/fixtures/api/schemas/graphql/packages/package_details.json index 9e8bf7c52d0..9f483d1daf4 100644 --- a/spec/fixtures/api/schemas/graphql/packages/package_details.json +++ b/spec/fixtures/api/schemas/graphql/packages/package_details.json @@ -50,6 +50,7 @@ "type": "object", "additionalProperties": false, "properties": { + "count": { "type": "integer" }, "pageInfo": { "type": "object" }, "edges": { "type": "array" }, "nodes": { "type": "array" } @@ -72,6 +73,7 @@ "type": "object", "additionalProperties": false, "properties": { + "count": { "type": "integer" }, "pageInfo": { "type": "object" }, "edges": { "type": "array" }, "nodes": { "type": "array" } @@ -91,6 +93,7 @@ "type": "object", "additionalProperties": false, "properties": { + "count": { "type": "integer" }, "pageInfo": { "type": "object" }, "edges": { "type": "array" }, "nodes": { "type": "array" } @@ -106,6 +109,7 @@ "properties": { "pageInfo": { "type": "object" }, "edges": { "type": "array" }, + "count": { "type": "integer" }, "nodes": { "type": "array", "items": { diff --git a/spec/fixtures/api/schemas/public_api/v4/environment.json b/spec/fixtures/api/schemas/public_api/v4/environment.json index b90bfe8de55..30104adaf5c 100644 --- a/spec/fixtures/api/schemas/public_api/v4/environment.json +++ b/spec/fixtures/api/schemas/public_api/v4/environment.json @@ -5,7 +5,9 @@ "name", "slug", "external_url", - "last_deployment" + "state", + "created_at", + "updated_at" ], "properties": { "id": { "type": "integer" }, @@ -19,6 +21,9 @@ ] }, "state": { "type": "string" }, + "created_at": { "type": "string", "format": "date-time" }, + "updated_at": { "type": "string", "format": "date-time" }, + "project": { "$ref": "project.json" }, "enable_advanced_logs_querying": { "type": "boolean" }, "logs_api_path": { "type": "string" }, "gitlab_managed_apps_logs_path": { "type": "string" } diff --git a/spec/frontend/packages/details/components/__snapshots__/conan_installation_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/conan_installation_spec.js.snap deleted file mode 100644 index a3423e3f4d7..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/conan_installation_spec.js.snap +++ /dev/null @@ -1,36 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/dependency_row_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/dependency_row_spec.js.snap deleted file mode 100644 index 39469bf4fd0..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/dependency_row_spec.js.snap +++ /dev/null @@ -1,34 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/maven_installation_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/maven_installation_spec.js.snap deleted file mode 100644 index 8a2793c0010..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/maven_installation_spec.js.snap +++ /dev/null @@ -1,112 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/npm_installation_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/npm_installation_spec.js.snap deleted file mode 100644 index 015c7b94dde..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/npm_installation_spec.js.snap +++ /dev/null @@ -1,36 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/nuget_installation_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/nuget_installation_spec.js.snap deleted file mode 100644 index 04532743952..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/nuget_installation_spec.js.snap +++ /dev/null @@ -1,36 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/package_title_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/package_title_spec.js.snap deleted file mode 100644 index 318cea98b92..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/package_title_spec.js.snap +++ /dev/null @@ -1,168 +0,0 @@ -// 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" - > - Test package - </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="maven" - 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="300 bytes" - 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],[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" - > - Test package - </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="maven" - 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="300 bytes" - texttooltip="" - /> - </div> - </div> - </div> - - <!----> - </div> - - <p /> -</div> -`; diff --git a/spec/frontend/packages/details/components/__snapshots__/pypi_installation_spec.js.snap b/spec/frontend/packages/details/components/__snapshots__/pypi_installation_spec.js.snap deleted file mode 100644 index d5bb825d8d1..00000000000 --- a/spec/frontend/packages/details/components/__snapshots__/pypi_installation_spec.js.snap +++ /dev/null @@ -1,45 +0,0 @@ -// 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> -`; diff --git a/spec/frontend/packages/details/components/additional_metadata_spec.js b/spec/frontend/packages/details/components/additional_metadata_spec.js deleted file mode 100644 index b339aa84348..00000000000 --- a/spec/frontend/packages/details/components/additional_metadata_spec.js +++ /dev/null @@ -1,119 +0,0 @@ -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import component from '~/packages/details/components/additional_metadata.vue'; -import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; - -import { mavenPackage, conanPackage, nugetPackage, npmPackage } from '../../mock_data'; - -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); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js index 377e7e05f09..206c5fcf544 100644 --- a/spec/frontend/packages/details/components/app_spec.js +++ b/spec/frontend/packages/details/components/app_spec.js @@ -5,28 +5,19 @@ import Vuex from 'vuex'; import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; import stubChildren from 'helpers/stub_children'; -import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue'; import PackagesApp from '~/packages/details/components/app.vue'; -import DependencyRow from '~/packages/details/components/dependency_row.vue'; -import InstallationCommands from '~/packages/details/components/installation_commands.vue'; import PackageFiles from '~/packages/details/components/package_files.vue'; import PackageHistory from '~/packages/details/components/package_history.vue'; -import PackageTitle from '~/packages/details/components/package_title.vue'; import * as getters from '~/packages/details/store/getters'; import PackageListRow from '~/packages/shared/components/package_list_row.vue'; import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue'; import { TrackingActions } from '~/packages/shared/constants'; import * as SharedUtils from '~/packages/shared/utils'; +import TerraformTitle from '~/packages_and_registries/infrastructure_registry/components/details_title.vue'; +import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; import Tracking from '~/tracking'; -import { - composerPackage, - conanPackage, - mavenPackage, - mavenFiles, - npmPackage, - nugetPackage, -} from '../../mock_data'; +import { mavenPackage, mavenFiles, npmPackage } from '../../mock_data'; const localVue = createLocalVue(); localVue.use(Vuex); @@ -73,7 +64,7 @@ describe('PackagesApp', () => { store, stubs: { ...stubChildren(PackagesApp), - PackageTitle: false, + TerraformTitle: false, TitleArea: false, GlButton: false, GlModal: false, @@ -84,23 +75,18 @@ describe('PackagesApp', () => { }); } - const packageTitle = () => wrapper.find(PackageTitle); - const emptyState = () => wrapper.find(GlEmptyState); + const packageTitle = () => wrapper.findComponent(TerraformTitle); + const emptyState = () => wrapper.findComponent(GlEmptyState); const deleteButton = () => wrapper.find('.js-delete-button'); const findDeleteModal = () => wrapper.find({ ref: 'deleteModal' }); const findDeleteFileModal = () => wrapper.find({ ref: 'deleteFileModal' }); const versionsTab = () => wrapper.find('.js-versions-tab > a'); - const packagesLoader = () => wrapper.find(PackagesListLoader); - const packagesVersionRows = () => wrapper.findAll(PackageListRow); + const packagesLoader = () => wrapper.findComponent(PackagesListLoader); + const packagesVersionRows = () => wrapper.findAllComponents(PackageListRow); const noVersionsMessage = () => wrapper.find('[data-testid="no-versions-message"]'); - const dependenciesTab = () => wrapper.find('.js-dependencies-tab > a'); - const dependenciesCountBadge = () => wrapper.find('[data-testid="dependencies-badge"]'); - const noDependenciesMessage = () => wrapper.find('[data-testid="no-dependencies-message"]'); - const dependencyRows = () => wrapper.findAll(DependencyRow); - const findPackageHistory = () => wrapper.find(PackageHistory); - const findAdditionalMetadata = () => wrapper.find(AdditionalMetadata); - const findInstallationCommands = () => wrapper.find(InstallationCommands); - const findPackageFiles = () => wrapper.find(PackageFiles); + const findPackageHistory = () => wrapper.findComponent(PackageHistory); + const findTerraformInstallation = () => wrapper.findComponent(TerraformInstallation); + const findPackageFiles = () => wrapper.findComponent(PackageFiles); afterEach(() => { wrapper.destroy(); @@ -129,21 +115,10 @@ describe('PackagesApp', () => { expect(findPackageHistory().props('projectName')).toEqual(wrapper.vm.projectName); }); - it('additional metadata has the right props', () => { + it('terraform installation exists', () => { createComponent(); - expect(findAdditionalMetadata().exists()).toBe(true); - expect(findAdditionalMetadata().props('packageEntity')).toEqual(wrapper.vm.packageEntity); - }); - - it('installation commands has the right props', () => { - createComponent(); - expect(findInstallationCommands().exists()).toBe(true); - expect(findInstallationCommands().props('packageEntity')).toEqual(wrapper.vm.packageEntity); - }); - it('hides the files table if package type is COMPOSER', () => { - createComponent({ packageEntity: composerPackage }); - expect(findPackageFiles().exists()).toBe(false); + expect(findTerraformInstallation().exists()).toBe(true); }); describe('deleting packages', () => { @@ -198,45 +173,6 @@ describe('PackagesApp', () => { }); }); - describe('dependency links', () => { - it('does not show the dependency links for a non nuget package', () => { - createComponent(); - - expect(dependenciesTab().exists()).toBe(false); - }); - - it('shows the dependencies tab with 0 count when a nuget package with no dependencies', () => { - createComponent({ - packageEntity: { - ...nugetPackage, - dependency_links: [], - }, - }); - - return wrapper.vm.$nextTick(() => { - const dependenciesBadge = dependenciesCountBadge(); - - expect(dependenciesTab().exists()).toBe(true); - expect(dependenciesBadge.exists()).toBe(true); - expect(dependenciesBadge.text()).toBe('0'); - expect(noDependenciesMessage().exists()).toBe(true); - }); - }); - - it('renders the correct number of dependency rows for a nuget package', () => { - createComponent({ packageEntity: nugetPackage }); - - return wrapper.vm.$nextTick(() => { - const dependenciesBadge = dependenciesCountBadge(); - - expect(dependenciesTab().exists()).toBe(true); - expect(dependenciesBadge.exists()).toBe(true); - expect(dependenciesBadge.text()).toBe(nugetPackage.dependency_links.length.toString()); - expect(dependencyRows()).toHaveLength(nugetPackage.dependency_links.length); - }); - }); - }); - describe('tracking and delete', () => { describe('delete package', () => { const originalReferrer = document.referrer; @@ -305,9 +241,9 @@ describe('PackagesApp', () => { }); it('tracking category calls packageTypeToTrackCategory', () => { - createComponent({ packageEntity: conanPackage }); + createComponent({ packageEntity: npmPackage }); expect(wrapper.vm.tracking.category).toBe(category); - expect(utilSpy).toHaveBeenCalledWith('conan'); + expect(utilSpy).toHaveBeenCalledWith('npm'); }); it(`delete button on delete modal call event with ${TrackingActions.DELETE_PACKAGE}`, () => { @@ -371,7 +307,7 @@ describe('PackagesApp', () => { }); it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => { - createComponent({ packageEntity: conanPackage }); + createComponent({ packageEntity: npmPackage }); findPackageFiles().vm.$emit('download-file'); expect(eventSpy).toHaveBeenCalledWith( diff --git a/spec/frontend/packages/details/components/composer_installation_spec.js b/spec/frontend/packages/details/components/composer_installation_spec.js deleted file mode 100644 index 18d11c7dd57..00000000000 --- a/spec/frontend/packages/details/components/composer_installation_spec.js +++ /dev/null @@ -1,133 +0,0 @@ -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 ComposerInstallation from '~/packages/details/components/composer_installation.vue'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; - -import { TrackingActions } from '~/packages/details/constants'; - -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); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/conan_installation_spec.js b/spec/frontend/packages/details/components/conan_installation_spec.js deleted file mode 100644 index 78a7d265a21..00000000000 --- a/spec/frontend/packages/details/components/conan_installation_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import Vuex from 'vuex'; -import ConanInstallation from '~/packages/details/components/conan_installation.vue'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import CodeInstructions from '~/vue_shared/components/registry/code_instruction.vue'; -import { conanPackage as packageEntity } from '../../mock_data'; -import { registryUrl as conanPath } from '../mock_data'; - -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); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/dependency_row_spec.js b/spec/frontend/packages/details/components/dependency_row_spec.js deleted file mode 100644 index 7d3ee92908d..00000000000 --- a/spec/frontend/packages/details/components/dependency_row_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import DependencyRow from '~/packages/details/components/dependency_row.vue'; -import { dependencyLinks } from '../../mock_data'; - -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})`); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/installation_title_spec.js b/spec/frontend/packages/details/components/installation_title_spec.js deleted file mode 100644 index 14e990d3011..00000000000 --- a/spec/frontend/packages/details/components/installation_title_spec.js +++ /dev/null @@ -1,58 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; - -import InstallationTitle from '~/packages/details/components/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']]); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/installations_commands_spec.js b/spec/frontend/packages/details/components/installations_commands_spec.js deleted file mode 100644 index 164f9f69741..00000000000 --- a/spec/frontend/packages/details/components/installations_commands_spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import ComposerInstallation from '~/packages/details/components/composer_installation.vue'; -import ConanInstallation from '~/packages/details/components/conan_installation.vue'; -import InstallationCommands from '~/packages/details/components/installation_commands.vue'; - -import MavenInstallation from '~/packages/details/components/maven_installation.vue'; -import NpmInstallation from '~/packages/details/components/npm_installation.vue'; -import NugetInstallation from '~/packages/details/components/nuget_installation.vue'; -import PypiInstallation from '~/packages/details/components/pypi_installation.vue'; -import TerraformInstallation from '~/packages_and_registries/infrastructure_registry/components/terraform_installation.vue'; - -import { - conanPackage, - mavenPackage, - npmPackage, - nugetPackage, - pypiPackage, - composerPackage, - terraformModule, -} from '../../mock_data'; - -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(); - }); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/maven_installation_spec.js b/spec/frontend/packages/details/components/maven_installation_spec.js deleted file mode 100644 index 4972fe70a3d..00000000000 --- a/spec/frontend/packages/details/components/maven_installation_spec.js +++ /dev/null @@ -1,184 +0,0 @@ -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 InstallationTitle from '~/packages/details/components/installation_title.vue'; -import MavenInstallation from '~/packages/details/components/maven_installation.vue'; -import { TrackingActions } from '~/packages/details/constants'; -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, - }); - }); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/npm_installation_spec.js b/spec/frontend/packages/details/components/npm_installation_spec.js deleted file mode 100644 index 1c49110bdf8..00000000000 --- a/spec/frontend/packages/details/components/npm_installation_spec.js +++ /dev/null @@ -1,123 +0,0 @@ -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 InstallationTitle from '~/packages/details/components/installation_title.vue'; -import NpmInstallation from '~/packages/details/components/npm_installation.vue'; -import { TrackingActions } from '~/packages/details/constants'; -import { npmInstallationCommand, npmSetupCommand } from '~/packages/details/store/getters'; -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, - }); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/nuget_installation_spec.js b/spec/frontend/packages/details/components/nuget_installation_spec.js deleted file mode 100644 index 8839a8f1108..00000000000 --- a/spec/frontend/packages/details/components/nuget_installation_spec.js +++ /dev/null @@ -1,79 +0,0 @@ -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 InstallationTitle from '~/packages/details/components/installation_title.vue'; -import NugetInstallation from '~/packages/details/components/nuget_installation.vue'; -import { TrackingActions } from '~/packages/details/constants'; -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, - }); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/package_title_spec.js b/spec/frontend/packages/details/components/package_title_spec.js deleted file mode 100644 index 512cec85b40..00000000000 --- a/spec/frontend/packages/details/components/package_title_spec.js +++ /dev/null @@ -1,189 +0,0 @@ -import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import Vuex from 'vuex'; -import PackageTitle from '~/packages/details/components/package_title.vue'; -import PackageTags from '~/packages/shared/components/package_tags.vue'; -import TitleArea from '~/vue_shared/components/registry/title_area.vue'; -import { - conanPackage, - mavenFiles, - mavenPackage, - mockTags, - npmFiles, - npmPackage, - nugetPackage, -} from '../../mock_data'; - -const localVue = createLocalVue(); -localVue.use(Vuex); - -describe('PackageTitle', () => { - let wrapper; - let store; - - function createComponent({ - packageEntity = mavenPackage, - packageFiles = mavenFiles, - icon = null, - } = {}) { - store = new Vuex.Store({ - state: { - packageEntity, - packageFiles, - }, - getters: { - packageTypeDisplay: ({ packageEntity: { package_type: type } }) => type, - packagePipeline: ({ packageEntity: { pipeline = null } }) => pipeline, - packageIcon: () => icon, - }, - }); - - wrapper = shallowMount(PackageTitle, { - localVue, - store, - stubs: { - TitleArea, - }, - }); - return wrapper.vm.$nextTick(); - } - - const findTitleArea = () => wrapper.find(TitleArea); - const packageType = () => wrapper.find('[data-testid="package-type"]'); - const packageSize = () => wrapper.find('[data-testid="package-size"]'); - const pipelineProject = () => wrapper.find('[data-testid="pipeline-project"]'); - const packageRef = () => wrapper.find('[data-testid="package-ref"]'); - const packageTags = () => wrapper.find(PackageTags); - const packageBadges = () => wrapper.findAll('[data-testid="tag-badge"]'); - - afterEach(() => { - wrapper.destroy(); - }); - - describe('renders', () => { - it('without tags', async () => { - await createComponent(); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('with tags', async () => { - await createComponent({ packageEntity: { ...mavenPackage, tags: mockTags } }); - - expect(wrapper.element).toMatchSnapshot(); - }); - - it('with tags on mobile', async () => { - jest.spyOn(GlBreakpointInstance, 'isDesktop').mockReturnValue(false); - await createComponent({ packageEntity: { ...mavenPackage, tags: mockTags } }); - await wrapper.vm.$nextTick(); - - expect(packageBadges()).toHaveLength(mockTags.length); - }); - }); - - describe('package title', () => { - it('is correctly bound', async () => { - await createComponent(); - - expect(findTitleArea().props('title')).toBe('Test package'); - }); - }); - - describe('package icon', () => { - const fakeSrc = 'a-fake-src'; - - it('binds an icon when provided one from vuex', async () => { - await createComponent({ icon: fakeSrc }); - - expect(findTitleArea().props('avatar')).toBe(fakeSrc); - }); - - it('do not binds an icon when not provided one', async () => { - await createComponent(); - - expect(findTitleArea().props('avatar')).toBe(null); - }); - }); - - describe.each` - packageEntity | text - ${conanPackage} | ${'conan'} - ${mavenPackage} | ${'maven'} - ${npmPackage} | ${'npm'} - ${nugetPackage} | ${'nuget'} - `(`package type`, ({ packageEntity, text }) => { - beforeEach(() => createComponent({ packageEntity })); - - it(`${packageEntity.package_type} should render from Vuex getters ${text}`, () => { - expect(packageType().props()).toEqual(expect.objectContaining({ text, icon: 'package' })); - }); - }); - - describe('calculates the package size', () => { - it('correctly calculates when there is only 1 file', async () => { - await createComponent({ packageEntity: npmPackage, packageFiles: npmFiles }); - - expect(packageSize().props()).toMatchObject({ text: '200 bytes', icon: 'disk' }); - }); - - it('correctly calulates when there are multiple files', async () => { - await createComponent(); - - expect(packageSize().props('text')).toBe('300 bytes'); - }); - }); - - describe('package tags', () => { - it('displays the package-tags component when the package has tags', async () => { - await createComponent({ - packageEntity: { - ...npmPackage, - tags: mockTags, - }, - }); - - expect(packageTags().exists()).toBe(true); - }); - - it('does not display the package-tags component when there are no tags', async () => { - await createComponent(); - - expect(packageTags().exists()).toBe(false); - }); - }); - - describe('package ref', () => { - it('does not display the ref if missing', async () => { - await createComponent(); - - expect(packageRef().exists()).toBe(false); - }); - - it('correctly shows the package ref if there is one', async () => { - await createComponent({ packageEntity: npmPackage }); - expect(packageRef().props()).toMatchObject({ - text: npmPackage.pipeline.ref, - icon: 'branch', - }); - }); - }); - - describe('pipeline project', () => { - it('does not display the project if missing', async () => { - await createComponent(); - - expect(pipelineProject().exists()).toBe(false); - }); - - it('correctly shows the pipeline project if there is one', async () => { - await createComponent({ packageEntity: npmPackage }); - - expect(pipelineProject().props()).toMatchObject({ - text: npmPackage.pipeline.project.name, - icon: 'review-list', - link: npmPackage.pipeline.project.web_url, - }); - }); - }); -}); diff --git a/spec/frontend/packages/details/components/pypi_installation_spec.js b/spec/frontend/packages/details/components/pypi_installation_spec.js deleted file mode 100644 index 2cec84282d9..00000000000 --- a/spec/frontend/packages/details/components/pypi_installation_spec.js +++ /dev/null @@ -1,72 +0,0 @@ -import { shallowMount, createLocalVue } from '@vue/test-utils'; -import Vuex from 'vuex'; -import { pypiPackage as packageEntity } from 'jest/packages/mock_data'; -import InstallationTitle from '~/packages/details/components/installation_title.vue'; -import PypiInstallation from '~/packages/details/components/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); - }); - }); -}); diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index f1b4e50b1eb..b47f8520514 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -12,22 +12,29 @@ RSpec.describe Gitlab::Regex do it { is_expected.to match('Dash – is this') } end - shared_examples_for 'project/group name regex' do + shared_examples_for 'group name regex' do it_behaves_like 'project/group name chars regex' it { is_expected.not_to match('?gitlab') } it { is_expected.not_to match("Users's something") } end + shared_examples_for 'project name regex' do + it_behaves_like 'project/group name chars regex' + it { is_expected.to match("Gitlab++") } + it { is_expected.not_to match('?gitlab') } + it { is_expected.not_to match("Users's something") } + end + describe '.project_name_regex' do subject { described_class.project_name_regex } - it_behaves_like 'project/group name regex' + it_behaves_like 'project name regex' end describe '.group_name_regex' do subject { described_class.group_name_regex } - it_behaves_like 'project/group name regex' + it_behaves_like 'group name regex' it 'allows parenthesis' do is_expected.to match('Group One (Test)') @@ -51,7 +58,7 @@ RSpec.describe Gitlab::Regex do describe '.project_name_regex_message' do subject { described_class.project_name_regex_message } - it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'.") } + it { is_expected.to eq("can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces. It must start with a letter, digit, emoji, or '_'.") } end describe '.group_name_regex_message' do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 1747972e8ae..d3d5a429a91 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1505,6 +1505,26 @@ RSpec.describe Issue do end end + describe '#supports_move_and_clone?' do + let_it_be(:project) { create(:project) } + let_it_be_with_refind(:issue) { create(:incident, project: project) } + + where(:issue_type, :supports_move_and_clone) do + :issue | true + :incident | true + end + + with_them do + before do + issue.update!(issue_type: issue_type) + end + + it do + expect(issue.supports_move_and_clone?).to eq(supports_move_and_clone) + end + end + end + describe '#email_participants_emails' do let_it_be(:issue) { create(:issue) } diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index bc7bb7523c9..5fb24dc91a4 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -18,6 +18,7 @@ RSpec.describe API::Environments do get api("/projects/#{project.id}/environments", user) expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/environments') expect(response).to include_pagination_headers expect(json_response).to be_an Array expect(json_response.size).to eq(1) @@ -167,6 +168,7 @@ RSpec.describe API::Environments do post api("/projects/#{project.id}/environments", user), params: { name: "mepmep" } expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema('public_api/v4/environment') expect(json_response['name']).to eq('mepmep') expect(json_response['slug']).to eq('mepmep') expect(json_response['external']).to be nil @@ -212,6 +214,7 @@ RSpec.describe API::Environments do params: { name: 'Mepmep', external_url: url } expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/environment') expect(json_response['name']).to eq('Mepmep') expect(json_response['external_url']).to eq(url) end @@ -250,7 +253,7 @@ RSpec.describe API::Environments do expect(response).to have_gitlab_http_status(:forbidden) end - it 'returns a 200 for stopped environment' do + it 'returns a 204 for stopped environment' do environment.stop delete api("/projects/#{project.id}/environments/#{environment.id}", user) @@ -294,6 +297,7 @@ RSpec.describe API::Environments do it 'returns a 200' do expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/environment') end it 'actually stops the environment' do @@ -327,6 +331,7 @@ RSpec.describe API::Environments do expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('public_api/v4/environment') + expect(json_response['last_deployment']).to be_present end end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 3138dbc56db..4923ef169e8 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -374,7 +374,7 @@ RSpec.describe Projects::UpdateService do expect(result).to eq({ status: :error, - message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." + message: "Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces. It must start with a letter, digit, emoji, or '_'." }) end end diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb index af4c9286e7c..f1d8665468c 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb @@ -17,9 +17,11 @@ RSpec.shared_examples 'group and project packages query' do let(:package_names) { graphql_data_at(resource_type, :packages, :nodes, :name) } let(:target_shas) { graphql_data_at(resource_type, :packages, :nodes, :metadata, :target_sha) } let(:packages) { graphql_data_at(resource_type, :packages, :nodes) } + let(:packages_count) { graphql_data_at(resource_type, :packages, :count) } let(:fields) do <<~QUERY + count nodes { #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} metadata { #{query_graphql_fragment('ComposerMetadata')} } @@ -55,6 +57,10 @@ RSpec.shared_examples 'group and project packages query' do it 'deals with metadata' do expect(target_shas).to contain_exactly(composer_metadatum.target_sha) end + + it 'returns the count of the packages' do + expect(packages_count).to eq(4) + end end context 'when the user does not have access to the resource' do |