diff options
48 files changed, 738 insertions, 353 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d271dd615b9..283dbf2aec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 14.2.4 (2021-09-17) + +### Fixed (2 changes) + +- [Fix Elastic::MigrationWorker current_migration (2nd attempt)](gitlab-org/gitlab@65bf8636d35edc6f580c7f09e1ffafc46ca5fbdb) ([merge request](gitlab-org/gitlab!70494)) **GitLab Enterprise Edition** +- [Removes cleanup job from Terraform.latest](gitlab-org/gitlab@6085d73d1a88aa98310f775fe2ff74584948e1a9) ([merge request](gitlab-org/gitlab!70494)) + ## 14.2.3 (2021-09-01) ### Fixed (4 changes) diff --git a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue index 5f778af1dbb..59066162960 100644 --- a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue +++ b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue @@ -96,12 +96,23 @@ export default { } }, }, + i18n: { + project: __('Project'), + privateForkSelected: __( + "To protect this issue's confidentiality, a private fork of this project was selected.", + ), + noForks: __('No forks are available to you.'), + forkTheProject: __( + `To protect this issue's confidentiality, %{linkStart}fork this project%{linkEnd} and set the fork's visibility to private.`, + ), + readMore: __('Read more'), + }, }; </script> <template> <div class="confidential-merge-request-fork-group form-group"> - <label>{{ __('Project') }}</label> + <label>{{ $options.i18n.project }}</label> <div> <dropdown v-if="projects.length" @@ -111,25 +122,13 @@ export default { /> <p class="text-muted mt-1 mb-0"> <template v-if="projects.length"> - {{ - __( - "To protect this issue's confidentiality, a private fork of this project was selected.", - ) - }} + {{ $options.i18n.privateForkSelected }} </template> <template v-else> - {{ __('No forks are available to you.') }}<br /> - <gl-sprintf - :message=" - __( - `To protect this issue's confidentiality, %{forkLink} and set the fork's visibility to private.`, - ) - " - > - <template #forkLink> - <a :href="newForkPath" target="_blank" class="help-link">{{ - __('fork this project') - }}</a> + {{ $options.i18n.noForks }}<br /> + <gl-sprintf :message="$options.i18n.forkTheProject"> + <template #link="{ content }"> + <a :href="newForkPath" target="_blank" class="help-link">{{ content }}</a> </template> </gl-sprintf> </template> @@ -138,7 +137,7 @@ export default { class="w-auto p-0 d-inline-block text-primary bg-transparent" target="_blank" > - <span class="sr-only">{{ __('Read more') }}</span> + <span class="sr-only">{{ $options.i18n.readMore }}</span> <gl-icon name="question-o" /> </gl-link> </p> diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue index fa9ee56c049..059772e8cb9 100644 --- a/app/assets/javascripts/jobs/components/job_app.vue +++ b/app/assets/javascripts/jobs/components/job_app.vue @@ -5,7 +5,7 @@ import { throttle, isEmpty } from 'lodash'; import { mapGetters, mapState, mapActions } from 'vuex'; import CodeQualityWalkthrough from '~/code_quality_walkthrough/components/step.vue'; import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; -import { sprintf } from '~/locale'; +import { __, sprintf } from '~/locale'; import CiHeader from '~/vue_shared/components/header_ci_component.vue'; import delayedJobMixin from '../mixins/delayed_job_mixin'; import EmptyState from './empty_state.vue'; @@ -126,6 +126,9 @@ export default { shouldRenderCodeQualityWalkthrough() { return this.job.status.group === 'failed-with-warnings'; }, + itemName() { + return sprintf(__('Job %{jobName}'), { jobName: this.job.name }); + }, }, watch: { // Once the job log is loaded, @@ -205,12 +208,11 @@ export default { <div class="build-header top-area"> <ci-header :status="job.status" - :item-id="job.id" :time="headerTime" :user="job.user" :has-sidebar-button="true" :should-render-triggered-label="shouldRenderTriggeredLabel" - :item-name="__('Job')" + :item-name="itemName" @clickedSidebarButton="toggleSidebar" /> </div> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue index 3595f83baf0..74c0cb44c51 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue @@ -1,35 +1,24 @@ <script> -import { GlLink, GlSprintf } from '@gitlab/ui'; -import { s__ } from '~/locale'; +import Composer from '~/packages_and_registries/package_registry/components/details/metadata/composer.vue'; +import Conan from '~/packages_and_registries/package_registry/components/details/metadata/conan.vue'; +import Maven from '~/packages_and_registries/package_registry/components/details/metadata/maven.vue'; +import Nuget from '~/packages_and_registries/package_registry/components/details/metadata/nuget.vue'; +import Pypi from '~/packages_and_registries/package_registry/components/details/metadata/pypi.vue'; import { - PACKAGE_TYPE_NUGET, + PACKAGE_TYPE_COMPOSER, PACKAGE_TYPE_CONAN, PACKAGE_TYPE_MAVEN, - PACKAGE_TYPE_COMPOSER, + PACKAGE_TYPE_NUGET, PACKAGE_TYPE_PYPI, } from '~/packages_and_registries/package_registry/constants'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; -import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; export default { - i18n: { - sourceText: s__('PackageRegistry|Source project located at %{link}'), - licenseText: s__('PackageRegistry|License information located at %{link}'), - recipeText: s__('PackageRegistry|Recipe: %{recipe}'), - appGroup: s__('PackageRegistry|App group: %{group}'), - appName: s__('PackageRegistry|App name: %{name}'), - targetShaCopyButton: s__('PackageRegistry|Copy target SHA'), - targetSha: s__('PackageRegistry|Target SHA: %{sha}'), - composerJson: s__( - 'PackageRegistry|Composer.json with license: %{license} and version: %{version}', - ), - requiredPython: s__('PackageRegistry|Required Python: %{pythonVersion}'), - }, components: { - DetailsRow, - GlLink, - GlSprintf, - ClipboardButton, + Composer, + Conan, + Maven, + Nuget, + Pypi, }, props: { packageEntity: { @@ -38,31 +27,17 @@ export default { }, }, computed: { - showMetadata() { - return ( - [ - PACKAGE_TYPE_NUGET, - PACKAGE_TYPE_CONAN, - PACKAGE_TYPE_MAVEN, - PACKAGE_TYPE_COMPOSER, - PACKAGE_TYPE_PYPI, - ].includes(this.packageEntity.packageType) && this.packageEntity.metadata - ); - }, - showNugetMetadata() { - return this.packageEntity.packageType === PACKAGE_TYPE_NUGET; - }, - showConanMetadata() { - return this.packageEntity.packageType === PACKAGE_TYPE_CONAN; + metadataComponent() { + return { + [PACKAGE_TYPE_COMPOSER]: Composer, + [PACKAGE_TYPE_CONAN]: Conan, + [PACKAGE_TYPE_MAVEN]: Maven, + [PACKAGE_TYPE_NUGET]: Nuget, + [PACKAGE_TYPE_PYPI]: Pypi, + }[this.packageEntity.packageType]; }, - showMavenMetadata() { - return this.packageEntity.packageType === PACKAGE_TYPE_MAVEN; - }, - showComposerMetadata() { - return this.packageEntity.packageType === PACKAGE_TYPE_COMPOSER; - }, - showPypiMetadata() { - return this.packageEntity.packageType === PACKAGE_TYPE_PYPI; + showMetadata() { + return this.metadataComponent && this.packageEntity.metadata; }, }, }; @@ -71,95 +46,12 @@ export default { <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="showNugetMetadata"> - <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.metadata.projectUrl" target="_blank">{{ - packageEntity.metadata.projectUrl - }}</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.metadata.licenseUrl" target="_blank">{{ - packageEntity.metadata.licenseUrl - }}</gl-link> - </template> - </gl-sprintf> - </details-row> - </template> - - <details-row - v-else-if="showConanMetadata" - icon="information-o" - padding="gl-p-4" - data-testid="conan-recipe" - > - <gl-sprintf :message="$options.i18n.recipeText"> - <template #recipe>{{ packageEntity.metadata.recipe }}</template> - </gl-sprintf> - </details-row> - - <template v-else-if="showMavenMetadata"> - <details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app"> - <gl-sprintf :message="$options.i18n.appName"> - <template #name> - <strong>{{ packageEntity.metadata.appName }}</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.metadata.appGroup }}</strong> - </template> - </gl-sprintf> - </details-row> - </template> - - <template v-else-if="showComposerMetadata"> - <details-row icon="information-o" padding="gl-p-4" dashed data-testid="composer-target-sha"> - <gl-sprintf :message="$options.i18n.targetSha"> - <template #sha> - <strong>{{ packageEntity.metadata.targetSha }}</strong> - <clipboard-button - :title="$options.i18n.targetShaCopyButton" - :text="packageEntity.metadata.targetSha" - category="tertiary" - css-class="gl-p-0!" - /> - </template> - </gl-sprintf> - </details-row> - <details-row icon="information-o" padding="gl-p-4" data-testid="composer-json"> - <gl-sprintf :message="$options.i18n.composerJson"> - <template #license> - <strong>{{ packageEntity.metadata.composerJson.license }}</strong> - </template> - <template #version> - <strong>{{ packageEntity.metadata.composerJson.version }}</strong> - </template> - </gl-sprintf> - </details-row> - </template> - - <details-row - v-else-if="showPypiMetadata" - icon="information-o" - padding="gl-p-4" - data-testid="pypi-required-python" - > - <gl-sprintf :message="$options.i18n.requiredPython"> - <template #pythonVersion> - <strong>{{ packageEntity.metadata.requiredPython }}</strong> - </template> - </gl-sprintf> - </details-row> + <component + :is="metadataComponent" + :package-entity="packageEntity" + data-testid="component-is" + /> </div> </div> </template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/composer.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/composer.vue new file mode 100644 index 00000000000..b6a36a0b00f --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/composer.vue @@ -0,0 +1,55 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + i18n: { + targetShaCopyButton: s__('PackageRegistry|Copy target SHA'), + targetSha: s__('PackageRegistry|Target SHA: %{sha}'), + composerJson: s__( + 'PackageRegistry|Composer.json with license: %{license} and version: %{version}', + ), + }, + components: { + DetailsRow, + GlSprintf, + ClipboardButton, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <details-row icon="information-o" padding="gl-p-4" dashed data-testid="composer-target-sha"> + <gl-sprintf :message="$options.i18n.targetSha"> + <template #sha> + <strong>{{ packageEntity.metadata.targetSha }}</strong> + <clipboard-button + :title="$options.i18n.targetShaCopyButton" + :text="packageEntity.metadata.targetSha" + category="tertiary" + css-class="gl-p-0!" + /> + </template> + </gl-sprintf> + </details-row> + <details-row icon="information-o" padding="gl-p-4" data-testid="composer-json"> + <gl-sprintf :message="$options.i18n.composerJson"> + <template #license> + <strong>{{ packageEntity.metadata.composerJson.license }}</strong> + </template> + <template #version> + <strong>{{ packageEntity.metadata.composerJson.version }}</strong> + </template> + </gl-sprintf> + </details-row> + </div> +</template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/conan.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/conan.vue new file mode 100644 index 00000000000..10797d74acf --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/conan.vue @@ -0,0 +1,32 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + i18n: { + recipeText: s__('PackageRegistry|Recipe: %{recipe}'), + }, + components: { + DetailsRow, + GlSprintf, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <details-row icon="information-o" padding="gl-p-4" data-testid="conan-recipe"> + <gl-sprintf :message="$options.i18n.recipeText"> + <template #recipe>{{ packageEntity.metadata.recipe }}</template> + </gl-sprintf> + </details-row> + </div> +</template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/maven.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/maven.vue new file mode 100644 index 00000000000..fd9fb49a9f2 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/maven.vue @@ -0,0 +1,42 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + i18n: { + appGroup: s__('PackageRegistry|App group: %{group}'), + appName: s__('PackageRegistry|App name: %{name}'), + }, + components: { + DetailsRow, + GlSprintf, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <details-row icon="information-o" padding="gl-p-4" dashed data-testid="maven-app"> + <gl-sprintf :message="$options.i18n.appName"> + <template #name> + <strong>{{ packageEntity.metadata.appName }}</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.metadata.appGroup }}</strong> + </template> + </gl-sprintf> + </details-row> + </div> +</template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/nuget.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/nuget.vue new file mode 100644 index 00000000000..f0da7db6c91 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/nuget.vue @@ -0,0 +1,46 @@ +<script> +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + i18n: { + sourceText: s__('PackageRegistry|Source project located at %{link}'), + licenseText: s__('PackageRegistry|License information located at %{link}'), + }, + components: { + DetailsRow, + GlLink, + GlSprintf, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <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.metadata.projectUrl" target="_blank">{{ + packageEntity.metadata.projectUrl + }}</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.metadata.licenseUrl" target="_blank">{{ + packageEntity.metadata.licenseUrl + }}</gl-link> + </template> + </gl-sprintf> + </details-row> + </div> +</template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/pypi.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/pypi.vue new file mode 100644 index 00000000000..6534eef532c --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/metadata/pypi.vue @@ -0,0 +1,34 @@ +<script> +import { GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +export default { + i18n: { + requiredPython: s__('PackageRegistry|Required Python: %{pythonVersion}'), + }, + components: { + DetailsRow, + GlSprintf, + }, + props: { + packageEntity: { + type: Object, + required: true, + }, + }, +}; +</script> + +<template> + <div> + <details-row icon="information-o" padding="gl-p-4" data-testid="pypi-required-python"> + <gl-sprintf :message="$options.i18n.requiredPython"> + <template #pythonVersion> + <strong>{{ packageEntity.metadata.requiredPython }}</strong> + </template> + </gl-sprintf> + </details-row> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 5db2b604956..4db6a3c9fd8 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -218,7 +218,7 @@ export default { :status="pipeline.detailedStatus" :time="pipeline.createdAt" :user="pipeline.user" - :item-id="Number(pipelineId)" + :item-id="pipelineId" item-name="Pipeline" > <gl-button diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index 9e334f944a0..41613bb3307 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -37,8 +37,9 @@ export default { required: true, }, itemId: { - type: Number, - required: true, + type: String, + required: false, + default: '', }, time: { type: String, @@ -86,6 +87,13 @@ export default { message() { return this.user?.status?.message; }, + item() { + if (this.itemId) { + return `${this.itemName} #${this.itemId}`; + } + + return this.itemName; + }, }, methods: { @@ -106,7 +114,7 @@ export default { <section class="header-main-content gl-mr-3"> <ci-icon-badge :status="status" /> - <strong data-testid="ci-header-item-text"> {{ itemName }} #{{ itemId }} </strong> + <strong data-testid="ci-header-item-text">{{ item }}</strong> <template v-if="shouldRenderTriggeredLabel">{{ __('triggered') }}</template> <template v-else>{{ __('created') }}</template> diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss index fbf21c67d82..f12b2ee2591 100644 --- a/app/assets/stylesheets/themes/_dark.scss +++ b/app/assets/stylesheets/themes/_dark.scss @@ -276,6 +276,10 @@ $well-inner-border: $gray-200; color: $gray-900; } +.gl-label-text-dark.gl-label-text-dark { + color: $gray-10; +} + // This applies to "gl-labels" from "gitlab-ui" .gl-label.gl-label-scoped.gl-label-text-dark, .gl-label.gl-label-scoped.gl-label-text-light { diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index 75dfed6d58f..c32e499c329 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -135,21 +135,21 @@ module RelativePositioning before, after = [before, after].sort_by(&:relative_position) if before && after RelativePositioning.mover.move(self, before, after) - rescue ActiveRecord::QueryCanceled, NoSpaceLeft => e + rescue NoSpaceLeft => e could_not_move(e) raise e end def move_after(before = self) RelativePositioning.mover.move(self, before, nil) - rescue ActiveRecord::QueryCanceled, NoSpaceLeft => e + rescue NoSpaceLeft => e could_not_move(e) raise e end def move_before(after = self) RelativePositioning.mover.move(self, nil, after) - rescue ActiveRecord::QueryCanceled, NoSpaceLeft => e + rescue NoSpaceLeft => e could_not_move(e) raise e end @@ -159,9 +159,6 @@ module RelativePositioning rescue NoSpaceLeft => e could_not_move(e) self.relative_position = MAX_POSITION - rescue ActiveRecord::QueryCanceled => e - could_not_move(e) - raise e end def move_to_start @@ -169,9 +166,6 @@ module RelativePositioning rescue NoSpaceLeft => e could_not_move(e) self.relative_position = MIN_POSITION - rescue ActiveRecord::QueryCanceled => e - could_not_move(e) - raise e end # This method is used during rebalancing - override it to customise the update diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 773dcae1d63..0bb4859dd1e 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -99,6 +99,7 @@ = f.text_field :pronouns, class: 'input-md gl-form-input', help: s_("Profiles|Enter your pronouns to let people know how to refer to you") = f.text_field :pronunciation, class: 'input-md gl-form-input', help: s_("Profiles|Enter how your name is pronounced to help people address you correctly") + = render_if_exists 'profiles/extra_settings', form: f = render_if_exists 'profiles/email_settings', form: f = f.text_field :skype, class: 'input-md gl-form-input', placeholder: s_("Profiles|username") = f.text_field :linkedin, class: 'input-md gl-form-input', help: s_("Profiles|Your LinkedIn profile name from linkedin.com/in/profilename") diff --git a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md index 92717dc1fe9..095eaaec23f 100644 --- a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md +++ b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md @@ -6,6 +6,9 @@ comments: false description: 'Making a GitLab codebase composable - allowing to run parts of the application' --- +NOTE: +Due to our focus on improving the overall availability of GitLab.com and reducing tech debt, we do not have capacity to act on this blueprint. We will re-evaluate in Q1-FY23. + # Composable GitLab codebase - using Rails Engines The one of the major risks of a single codebase is an infinite growth of the whole diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index 8f781e9ff18..9a17ac4c813 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -532,7 +532,7 @@ Example: ```ruby field :foo, GraphQL::Types::String, null: true, - description: 'Some test field. Will always return `null`' \ + description: 'Some test field. Returns `null`' \ 'if `my_feature_flag` feature flag is disabled.' def foo diff --git a/doc/integration/jira/dvcs.md b/doc/integration/jira/dvcs.md index 7f40a4eeeee..664a0361da4 100644 --- a/doc/integration/jira/dvcs.md +++ b/doc/integration/jira/dvcs.md @@ -234,6 +234,14 @@ To resolve this issue: [Contact GitLab Support](https://about.gitlab.com/support/) if none of these reasons apply. +### `410 : Gone` error when connecting to Jira + +When you connect to Jira and synchronize repositories, you may receive a `410 : Gone` error. + +This issue occurs when you use the Jira DVCS connector and your integration is configured to use **GitHub Enterprise**. + +For more information and possible fixes, see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/340160). + ### Fix synchronization issues If Jira displays incorrect information, such as deleted branches, you may have to diff --git a/doc/user/admin_area/settings/sidekiq_job_limits.md b/doc/user/admin_area/settings/sidekiq_job_limits.md index 314bc631db4..c6a783beb3f 100644 --- a/doc/user/admin_area/settings/sidekiq_job_limits.md +++ b/doc/user/admin_area/settings/sidekiq_job_limits.md @@ -22,7 +22,7 @@ To access Sidekiq job size limits: 1. On the left sidebar, select **Settings > Preferences**. 1. Expand **Sidekiq job size limits**. 1. Adjust the compression threshold or size limit. The compression can - be disabled by selecting "Track" mode. + be disabled by selecting the **Track** mode. ## Available settings diff --git a/lib/gem_extensions/active_record/association.rb b/lib/gem_extensions/active_record/association.rb index 91a9f45ce7e..c6634a0524a 100644 --- a/lib/gem_extensions/active_record/association.rb +++ b/lib/gem_extensions/active_record/association.rb @@ -15,7 +15,7 @@ module GemExtensions def scope if disable_joins - DisableJoins::Associations::AssociationScope.create.scope(self) + ::GemExtensions::ActiveRecord::DisableJoins::Associations::AssociationScope.create.scope(self) else super end @@ -25,7 +25,7 @@ module GemExtensions if klass @association_scope ||= begin # rubocop:disable Gitlab/ModuleWithInstanceVariables if disable_joins - DisableJoins::Associations::AssociationScope.scope(self) + ::GemExtensions::ActiveRecord::DisableJoins::Associations::AssociationScope.scope(self) else super end diff --git a/lib/gem_extensions/active_record/disable_joins/associations/association_scope.rb b/lib/gem_extensions/active_record/disable_joins/associations/association_scope.rb index 1e4476330a2..9ff80829bc3 100644 --- a/lib/gem_extensions/active_record/disable_joins/associations/association_scope.rb +++ b/lib/gem_extensions/active_record/disable_joins/associations/association_scope.rb @@ -64,7 +64,7 @@ module GemExtensions end if scope.order_values.empty? && ordered - split_scope = DisableJoins::Relation.create(scope.klass, key, join_ids) + split_scope = ::GemExtensions::ActiveRecord::DisableJoins::Relation.create(scope.klass, key, join_ids) split_scope.where_clause += scope.where_clause split_scope else diff --git a/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml index a130b09c51a..1ec1aa60d88 100644 --- a/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml @@ -41,9 +41,9 @@ echo "Adopting Helm v2 manifests from $release" # some resource kinds must be listed explicitly https://github.com/kubernetes/kubernetes/issues/42885 for name in $(kubectl -n "$KUBE_NAMESPACE" get all,ingress,daemonset -o name -l chart="$chart"); do - kubectl annotate --overwrite "$name" meta.helm.sh/release-name="$release" - kubectl annotate --overwrite "$name" meta.helm.sh/release-namespace="$KUBE_NAMESPACE" - kubectl label --overwrite "$name" app.kubernetes.io/managed-by=Helm + kubectl annotate -n "$KUBE_NAMESPACE" --overwrite "$name" meta.helm.sh/release-name="$release" + kubectl annotate -n "$KUBE_NAMESPACE" --overwrite "$name" meta.helm.sh/release-namespace="$KUBE_NAMESPACE" + kubectl label -n "$KUBE_NAMESPACE" --overwrite "$name" app.kubernetes.io/managed-by=Helm done done # migrate each release diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 163bbae2692..e9091b9bba4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -19351,6 +19351,9 @@ msgstr "" msgid "Job" msgstr "" +msgid "Job %{jobName}" +msgstr "" + msgid "Job Failed #%{build_id}" msgstr "" @@ -35307,7 +35310,7 @@ msgstr "" msgid "To preserve performance only %{strong_open}%{display_size} of %{real_size}%{strong_close} files are displayed." msgstr "" -msgid "To protect this issue's confidentiality, %{forkLink} and set the fork's visibility to private." +msgid "To protect this issue's confidentiality, %{linkStart}fork this project%{linkEnd} and set the fork's visibility to private." msgstr "" msgid "To protect this issue's confidentiality, a private fork of this project was selected." @@ -39911,9 +39914,6 @@ msgstr "" msgid "fork" msgstr "" -msgid "fork this project" -msgstr "" - msgid "from" msgstr "" diff --git a/package.json b/package.json index 0b45f1c29e7..28ed6bf9393 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "1.212.0", "@gitlab/tributejs": "1.0.0", - "@gitlab/ui": "32.10.2", + "@gitlab/ui": "32.11.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "6.1.3-2", "@rails/ujs": "6.1.3-2", diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb index 140d5dee270..a904ba770dd 100644 --- a/spec/features/projects/jobs/permissions_spec.rb +++ b/spec/features/projects/jobs/permissions_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Project Jobs Permissions' do it_behaves_like 'recent job page details responds with status', 200 do it 'renders job details', :js do - expect(page).to have_content "Job ##{job.id}" + expect(page).to have_content "Job #{job.name}" expect(page).to have_css '.log-line' end end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 9b199157d79..060b7ffbfc9 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'User browses a job', :js do it 'erases the job log', :js do wait_for_requests - expect(page).to have_content("Job ##{build.id}") + expect(page).to have_content("Job #{build.name}") expect(page).to have_css('.job-log') # scroll to the top of the page first diff --git a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap index f5dfc5b83cb..ddb188edb10 100644 --- a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap +++ b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap @@ -55,7 +55,10 @@ exports[`Alert integration settings form default state should match the default data-qa-selector="incident_templates_dropdown" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" id="alert-integration-settings-issue-template" + showhighlighteditemstitle="true" size="medium" text="selecte_tmpl" variant="default" diff --git a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap index bc44657f291..b34265b7234 100644 --- a/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap +++ b/spec/frontend/clusters/components/__snapshots__/remove_cluster_confirmation_spec.js.snap @@ -46,11 +46,27 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ > <!----> - <!----> + <div + class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5" + > + <div + class="gl-display-flex" + > + <!----> + </div> + + <div + class="gl-display-flex" + > + <!----> + </div> + </div> <div class="gl-new-dropdown-contents" > + <!----> + <li class="gl-new-dropdown-item" role="presentation" diff --git a/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap index 271c6356f7e..c2fa6556847 100644 --- a/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap +++ b/spec/frontend/confidential_merge_request/components/__snapshots__/project_form_group_spec.js.snap @@ -17,11 +17,15 @@ exports[`Confidential merge request project form group component renders empty s No forks are available to you. <br /> - - <gl-sprintf-stub - message="To protect this issue's confidentiality, %{forkLink} and set the fork's visibility to private." - /> - + To protect this issue's confidentiality, + <a + class="help-link" + href="https://test.com" + target="_blank" + > + fork this project + </a> + and set the fork's visibility to private. <gl-link-stub class="w-auto p-0 d-inline-block text-primary bg-transparent" href="/help" @@ -52,18 +56,16 @@ exports[`Confidential merge request project form group component renders fork dr </label> <div> - <!----> + <dropdown-stub + projects="[object Object],[object Object]" + selectedproject="[object Object]" + /> <p class="text-muted mt-1 mb-0" > - No forks are available to you. - <br /> - - <gl-sprintf-stub - message="To protect this issue's confidentiality, %{forkLink} and set the fork's visibility to private." - /> + To protect this issue's confidentiality, a private fork of this project was selected. <gl-link-stub class="w-auto p-0 d-inline-block text-primary bg-transparent" diff --git a/spec/frontend/confidential_merge_request/components/project_form_group_spec.js b/spec/frontend/confidential_merge_request/components/project_form_group_spec.js index 67f6d360f52..0e73d50fdb5 100644 --- a/spec/frontend/confidential_merge_request/components/project_form_group_spec.js +++ b/spec/frontend/confidential_merge_request/components/project_form_group_spec.js @@ -1,3 +1,4 @@ +import { GlSprintf } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import ProjectFormGroup from '~/confidential_merge_request/components/project_form_group.vue'; @@ -21,55 +22,52 @@ const mockData = [ }, }, ]; -let vm; +let wrapper; let mock; function factory(projects = mockData) { mock = new MockAdapter(axios); mock.onGet(/api\/(.*)\/projects\/gitlab-org%2Fgitlab-ce\/forks/).reply(200, projects); - vm = shallowMount(ProjectFormGroup, { + wrapper = shallowMount(ProjectFormGroup, { propsData: { namespacePath: 'gitlab-org', projectPath: 'gitlab-org/gitlab-ce', newForkPath: 'https://test.com', helpPagePath: '/help', }, + stubs: { GlSprintf }, }); + + return axios.waitForAll(); } describe('Confidential merge request project form group component', () => { afterEach(() => { mock.restore(); - vm.destroy(); + wrapper.destroy(); }); - it('renders fork dropdown', () => { - factory(); + it('renders fork dropdown', async () => { + await factory(); - return vm.vm.$nextTick(() => { - expect(vm.element).toMatchSnapshot(); - }); + expect(wrapper.element).toMatchSnapshot(); }); - it('sets selected project as first fork', () => { - factory(); + it('sets selected project as first fork', async () => { + await factory(); - return vm.vm.$nextTick(() => { - expect(vm.vm.selectedProject).toEqual({ - id: 1, - name: 'root / gitlab-ce', - pathWithNamespace: 'root/gitlab-ce', - namespaceFullpath: 'root', - }); + expect(wrapper.vm.selectedProject).toEqual({ + id: 1, + name: 'root / gitlab-ce', + pathWithNamespace: 'root/gitlab-ce', + namespaceFullpath: 'root', }); }); - it('renders empty state when response is empty', () => { - factory([]); + it('renders empty state when response is empty', async () => { + await factory([]); - return vm.vm.$nextTick(() => { - expect(vm.element).toMatchSnapshot(); - }); + expect(wrapper.element).toMatchSnapshot(); }); }); diff --git a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap index b8035f97f8f..8f5516545eb 100644 --- a/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap +++ b/spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap @@ -11,8 +11,16 @@ exports[`content_editor/components/toolbar_link_button renders dropdown componen <ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\"> <div class=\\"gl-new-dropdown-inner\\"> <!----> - <!----> + <div class=\\"gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5\\"> + <div class=\\"gl-display-flex\\"> + <!----> + </div> + <div class=\\"gl-display-flex\\"> + <!----> + </div> + </div> <div class=\\"gl-new-dropdown-contents\\"> + <!----> <li role=\\"presentation\\" class=\\"gl-px-3!\\"> <form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\"> <div placeholder=\\"Link URL\\"> diff --git a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap index 0eca6d3bb00..67e4a82787c 100644 --- a/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap +++ b/spec/frontend/design_management/components/upload/__snapshots__/design_version_dropdown_spec.js.snap @@ -6,8 +6,11 @@ exports[`Design management design version dropdown component renders design vers clearalltext="Clear all" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" issueiid="" projectpath="" + showhighlighteditemstitle="true" size="small" text="Showing latest version" variant="default" @@ -84,8 +87,11 @@ exports[`Design management design version dropdown component renders design vers clearalltext="Clear all" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" issueiid="" projectpath="" + showhighlighteditemstitle="true" size="small" text="Showing latest version" variant="default" diff --git a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap index 2ca270aeea2..891ba9c223c 100644 --- a/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap +++ b/spec/frontend/jira_import/components/__snapshots__/jira_import_form_spec.js.snap @@ -127,11 +127,27 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] = > <!----> - <!----> + <div + class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5" + > + <div + class="gl-display-flex" + > + <!----> + </div> + + <div + class="gl-display-flex" + > + <!----> + </div> + </div> <div class="gl-new-dropdown-contents" > + <!----> + <div class="gl-search-box-by-type" > @@ -256,11 +272,27 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] = > <!----> - <!----> + <div + class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5" + > + <div + class="gl-display-flex" + > + <!----> + </div> + + <div + class="gl-display-flex" + > + <!----> + </div> + </div> <div class="gl-new-dropdown-contents" > + <!----> + <div class="gl-search-box-by-type" > diff --git a/spec/frontend/jobs/components/job_app_spec.js b/spec/frontend/jobs/components/job_app_spec.js index 1f4dd7d6216..f8a0059bf21 100644 --- a/spec/frontend/jobs/components/job_app_spec.js +++ b/spec/frontend/jobs/components/job_app_spec.js @@ -140,7 +140,7 @@ describe('Job App', () => { it('should render provided job information', () => { expect(wrapper.find('.header-main-content').text().replace(/\s+/g, ' ').trim()).toContain( - 'passed Job #4757 triggered 1 year ago by Root', + 'passed Job test triggered 1 year ago by Root', ); }); @@ -154,7 +154,7 @@ describe('Job App', () => { setupAndMount().then(() => { expect( wrapper.find('.header-main-content').text().replace(/\s+/g, ' ').trim(), - ).toContain('passed Job #4757 created 3 weeks ago by Root'); + ).toContain('passed Job test created 3 weeks ago by Root'); })); }); }); diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap index ac2dea6199d..05538dbaeee 100644 --- a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap +++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap @@ -40,8 +40,11 @@ exports[`Dashboard template matches the default snapshot 1`] = ` data-qa-selector="environments_dropdown" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" id="monitor-environments-dropdown" menu-class="monitor-environment-dropdown-menu" + showhighlighteditemstitle="true" size="medium" text="production" toggleclass="dropdown-menu-toggle" diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js index dda0b5bef8d..7a71a1cea0f 100644 --- a/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js +++ b/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js @@ -1,4 +1,3 @@ -import { GlLink, GlSprintf } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { conanMetadata, @@ -17,8 +16,6 @@ import { PACKAGE_TYPE_COMPOSER, PACKAGE_TYPE_PYPI, } from '~/packages_and_registries/package_registry/constants'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; -import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; const mavenPackage = { packageType: PACKAGE_TYPE_MAVEN, metadata: mavenMetadata() }; const conanPackage = { packageType: PACKAGE_TYPE_CONAN, metadata: conanMetadata() }; @@ -39,8 +36,7 @@ describe('Package Additional Metadata', () => { wrapper = shallowMountExtended(component, { propsData: { ...defaultProps, ...props }, stubs: { - DetailsRow, - GlSprintf, + component: { template: '<div data-testid="component-is"></div>' }, }, }); }; @@ -52,16 +48,7 @@ describe('Package Additional Metadata', () => { const findTitle = () => wrapper.findByTestId('title'); const findMainArea = () => wrapper.findByTestId('main'); - const findNugetSource = () => wrapper.findByTestId('nuget-source'); - const findNugetLicense = () => wrapper.findByTestId('nuget-license'); - const findConanRecipe = () => wrapper.findByTestId('conan-recipe'); - const findMavenApp = () => wrapper.findByTestId('maven-app'); - const findMavenGroup = () => wrapper.findByTestId('maven-group'); - const findElementLink = (container) => container.findComponent(GlLink); - const findComposerTargetSha = () => wrapper.findByTestId('composer-target-sha'); - const findComposerTargetShaCopyButton = () => wrapper.findComponent(ClipboardButton); - const findComposerJson = () => wrapper.findByTestId('composer-json'); - const findPypiRequiredPython = () => wrapper.findByTestId('pypi-required-python'); + const findComponentIs = () => wrapper.findByTestId('component-is'); it('has the correct title', () => { mountComponent(); @@ -87,98 +74,11 @@ describe('Package Additional Metadata', () => { expect(findTitle().exists()).toBe(visible); expect(findMainArea().exists()).toBe(visible); + expect(findComponentIs().exists()).toBe(visible); + + if (visible) { + expect(findComponentIs().props('packageEntity')).toEqual(packageEntity); + } }, ); - - describe('nuget metadata', () => { - beforeEach(() => { - mountComponent({ packageEntity: nugetPackage }); - }); - - it.each` - name | finderFunction | text | link | icon - ${'source'} | ${findNugetSource} | ${'Source project located at projectUrl'} | ${'projectUrl'} | ${'project'} - ${'license'} | ${findNugetLicense} | ${'License information located at licenseUrl'} | ${'licenseUrl'} | ${'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.metadata[link]); - }); - }); - - describe('conan metadata', () => { - beforeEach(() => { - mountComponent({ packageEntity: conanPackage }); - }); - - it.each` - name | finderFunction | text | icon - ${'recipe'} | ${findConanRecipe} | ${'Recipe: package-8/1.0.0@gitlab-org+gitlab-test/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: appName'} | ${'information-o'} - ${'group'} | ${findMavenGroup} | ${'App group: appGroup'} | ${'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('composer metadata', () => { - beforeEach(() => { - mountComponent({ packageEntity: composerPackage }); - }); - - it.each` - name | finderFunction | text | icon - ${'target-sha'} | ${findComposerTargetSha} | ${'Target SHA: b83d6e391c22777fca1ed3012fce84f633d7fed0'} | ${'information-o'} - ${'composer-json'} | ${findComposerJson} | ${'Composer.json with license: MIT and version: 1.0.0'} | ${'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); - }); - - it('target-sha has a copy button', () => { - expect(findComposerTargetShaCopyButton().exists()).toBe(true); - expect(findComposerTargetShaCopyButton().props()).toMatchObject({ - text: 'b83d6e391c22777fca1ed3012fce84f633d7fed0', - title: 'Copy target SHA', - }); - }); - }); - - describe('pypi metadata', () => { - beforeEach(() => { - mountComponent({ packageEntity: pypiPackage }); - }); - - it.each` - name | finderFunction | text | icon - ${'pypi-required-python'} | ${findPypiRequiredPython} | ${'Required Python: 1.0.0'} | ${'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_and_registries/package_registry/components/details/metadata/composer_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/composer_spec.js new file mode 100644 index 00000000000..e744680cb9a --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/composer_spec.js @@ -0,0 +1,58 @@ +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { + packageData, + composerMetadata, +} from 'jest/packages_and_registries/package_registry/mock_data'; +import component from '~/packages_and_registries/package_registry/components/details/metadata/composer.vue'; +import { PACKAGE_TYPE_COMPOSER } from '~/packages_and_registries/package_registry/constants'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +const composerPackage = { packageType: PACKAGE_TYPE_COMPOSER, metadata: composerMetadata() }; + +describe('Composer Metadata', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMountExtended(component, { + propsData: { packageEntity: packageData(composerPackage) }, + stubs: { + DetailsRow, + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findComposerTargetSha = () => wrapper.findByTestId('composer-target-sha'); + const findComposerTargetShaCopyButton = () => wrapper.findComponent(ClipboardButton); + const findComposerJson = () => wrapper.findByTestId('composer-json'); + + beforeEach(() => { + mountComponent(); + }); + + it.each` + name | finderFunction | text | icon + ${'target-sha'} | ${findComposerTargetSha} | ${'Target SHA: b83d6e391c22777fca1ed3012fce84f633d7fed0'} | ${'information-o'} + ${'composer-json'} | ${findComposerJson} | ${'Composer.json with license: MIT and version: 1.0.0'} | ${'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); + }); + + it('target-sha has a copy button', () => { + expect(findComposerTargetShaCopyButton().exists()).toBe(true); + expect(findComposerTargetShaCopyButton().props()).toMatchObject({ + text: 'b83d6e391c22777fca1ed3012fce84f633d7fed0', + title: 'Copy target SHA', + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/metadata/conan_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/conan_spec.js new file mode 100644 index 00000000000..46593047f1f --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/conan_spec.js @@ -0,0 +1,48 @@ +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { + conanMetadata, + packageData, +} from 'jest/packages_and_registries/package_registry/mock_data'; +import component from '~/packages_and_registries/package_registry/components/details/metadata/conan.vue'; +import { PACKAGE_TYPE_CONAN } from '~/packages_and_registries/package_registry/constants'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +const conanPackage = { packageType: PACKAGE_TYPE_CONAN, metadata: conanMetadata() }; + +describe('Conan Metadata', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMountExtended(component, { + propsData: { + packageEntity: packageData(conanPackage), + }, + stubs: { + DetailsRow, + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findConanRecipe = () => wrapper.findByTestId('conan-recipe'); + + beforeEach(() => { + mountComponent(); + }); + + it.each` + name | finderFunction | text | icon + ${'recipe'} | ${findConanRecipe} | ${'Recipe: package-8/1.0.0@gitlab-org+gitlab-test/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); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/metadata/maven_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/maven_spec.js new file mode 100644 index 00000000000..bc54cf1cb98 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/maven_spec.js @@ -0,0 +1,52 @@ +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { + mavenMetadata, + packageData, +} from 'jest/packages_and_registries/package_registry/mock_data'; +import component from '~/packages_and_registries/package_registry/components/details/metadata/maven.vue'; +import { PACKAGE_TYPE_MAVEN } from '~/packages_and_registries/package_registry/constants'; +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +const mavenPackage = { packageType: PACKAGE_TYPE_MAVEN, metadata: mavenMetadata() }; + +describe('Maven Metadata', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMountExtended(component, { + propsData: { + packageEntity: { + ...packageData(mavenPackage), + }, + }, + stubs: { + DetailsRow, + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findMavenApp = () => wrapper.findByTestId('maven-app'); + const findMavenGroup = () => wrapper.findByTestId('maven-group'); + + beforeEach(() => { + mountComponent(); + }); + + it.each` + name | finderFunction | text | icon + ${'app'} | ${findMavenApp} | ${'App name: appName'} | ${'information-o'} + ${'group'} | ${findMavenGroup} | ${'App group: appGroup'} | ${'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_and_registries/package_registry/components/details/metadata/nuget_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/nuget_spec.js new file mode 100644 index 00000000000..279900edff2 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/nuget_spec.js @@ -0,0 +1,55 @@ +import { GlLink, GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { + nugetMetadata, + packageData, +} from 'jest/packages_and_registries/package_registry/mock_data'; +import component from '~/packages_and_registries/package_registry/components/details/metadata/nuget.vue'; +import { PACKAGE_TYPE_NUGET } from '~/packages_and_registries/package_registry/constants'; + +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +const nugetPackage = { packageType: PACKAGE_TYPE_NUGET, metadata: nugetMetadata() }; + +describe('Nuget Metadata', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMountExtended(component, { + propsData: { + packageEntity: { + ...packageData(nugetPackage), + }, + }, + stubs: { + DetailsRow, + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findNugetSource = () => wrapper.findByTestId('nuget-source'); + const findNugetLicense = () => wrapper.findByTestId('nuget-license'); + const findElementLink = (container) => container.findComponent(GlLink); + + beforeEach(() => { + mountComponent({ packageEntity: nugetPackage }); + }); + + it.each` + name | finderFunction | text | link | icon + ${'source'} | ${findNugetSource} | ${'Source project located at projectUrl'} | ${'projectUrl'} | ${'project'} + ${'license'} | ${findNugetLicense} | ${'License information located at licenseUrl'} | ${'licenseUrl'} | ${'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.metadata[link]); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/metadata/pypi_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/pypi_spec.js new file mode 100644 index 00000000000..c4481c3f20b --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/details/metadata/pypi_spec.js @@ -0,0 +1,48 @@ +import { GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { packageData, pypiMetadata } from 'jest/packages_and_registries/package_registry/mock_data'; +import component from '~/packages_and_registries/package_registry/components/details/metadata/pypi.vue'; +import { PACKAGE_TYPE_PYPI } from '~/packages_and_registries/package_registry/constants'; + +import DetailsRow from '~/vue_shared/components/registry/details_row.vue'; + +const pypiPackage = { packageType: PACKAGE_TYPE_PYPI, metadata: pypiMetadata() }; + +describe('Package Additional Metadata', () => { + let wrapper; + + const mountComponent = () => { + wrapper = shallowMountExtended(component, { + propsData: { + packageEntity: { + ...packageData(pypiPackage), + }, + }, + stubs: { + DetailsRow, + GlSprintf, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const findPypiRequiredPython = () => wrapper.findByTestId('pypi-required-python'); + + beforeEach(() => { + mountComponent(); + }); + + it.each` + name | finderFunction | text | icon + ${'pypi-required-python'} | ${findPypiRequiredPython} | ${'Required Python: 1.0.0'} | ${'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/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap index 0d6f31907b6..417567c9f4c 100644 --- a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap +++ b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap @@ -14,6 +14,9 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`] clearalltext="Clear all" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" + showhighlighteditemstitle="true" size="medium" text="rspec" variant="default" diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js index e531e26a858..9e51003da66 100644 --- a/spec/frontend/pipelines/header_component_spec.js +++ b/spec/frontend/pipelines/header_component_spec.js @@ -24,7 +24,7 @@ describe('Pipeline details header', () => { const findLoadingIcon = () => wrapper.find(GlLoadingIcon); const defaultProvideOptions = { - pipelineId: 14, + pipelineId: '14', pipelineIid: 1, paths: { pipelinesPath: '/namespace/my-project/-/pipelines', diff --git a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap index 6ca9b9a5f41..c7758b0faef 100644 --- a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap +++ b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap @@ -6,7 +6,10 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = ` clearalltext="Clear all" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" right="true" + showhighlighteditemstitle="true" size="medium" text="Clone" variant="info" diff --git a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap index b00f681694b..f2ff12b2acd 100644 --- a/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap +++ b/spec/frontend/vue_shared/components/__snapshots__/split_button_spec.js.snap @@ -6,7 +6,10 @@ exports[`SplitButton renders actionItems 1`] = ` clearalltext="Clear all" headertext="" hideheaderborder="true" + highlighteditemstitle="Selected" + highlighteditemstitleclass="gl-px-5" menu-class="" + showhighlighteditemstitle="true" size="medium" split="true" text="professor" diff --git a/spec/frontend/vue_shared/components/header_ci_component_spec.js b/spec/frontend/vue_shared/components/header_ci_component_spec.js index b54d120b55b..42f4439df51 100644 --- a/spec/frontend/vue_shared/components/header_ci_component_spec.js +++ b/spec/frontend/vue_shared/components/header_ci_component_spec.js @@ -16,8 +16,6 @@ describe('Header CI Component', () => { text: 'failed', details_path: 'path', }, - itemName: 'job', - itemId: 123, time: '2017-05-08T14:57:39.781Z', user: { web_url: 'path', @@ -55,17 +53,13 @@ describe('Header CI Component', () => { describe('render', () => { beforeEach(() => { - createComponent(); + createComponent({ itemName: 'Pipeline' }); }); it('should render status badge', () => { expect(findIconBadge().exists()).toBe(true); }); - it('should render item name and id', () => { - expect(findHeaderItemText().text()).toBe('job #123'); - }); - it('should render timeago date', () => { expect(findTimeAgo().exists()).toBe(true); }); @@ -83,9 +77,29 @@ describe('Header CI Component', () => { }); }); + describe('with item id', () => { + beforeEach(() => { + createComponent({ itemName: 'Pipeline', itemId: '123' }); + }); + + it('should render item name and id', () => { + expect(findHeaderItemText().text()).toBe('Pipeline #123'); + }); + }); + + describe('without item id', () => { + beforeEach(() => { + createComponent({ itemName: 'Job build_job' }); + }); + + it('should render item name', () => { + expect(findHeaderItemText().text()).toBe('Job build_job'); + }); + }); + describe('slot', () => { it('should render header action buttons', () => { - createComponent({}, { slots: { default: 'Test Actions' } }); + createComponent({ itemName: 'Job build_job' }, { slots: { default: 'Test Actions' } }); expect(findActionButtons().exists()).toBe(true); expect(findActionButtons().text()).toBe('Test Actions'); @@ -94,7 +108,7 @@ describe('Header CI Component', () => { describe('shouldRenderTriggeredLabel', () => { it('should render created keyword when the shouldRenderTriggeredLabel is false', () => { - createComponent({ shouldRenderTriggeredLabel: false }); + createComponent({ shouldRenderTriggeredLabel: false, itemName: 'Job build_job' }); expect(wrapper.text()).toContain('created'); expect(wrapper.text()).not.toContain('triggered'); diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 481e6504be9..1747972e8ae 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1435,19 +1435,19 @@ RSpec.describe Issue do describe 'scheduling rebalancing' do before do allow_next_instance_of(RelativePositioning::Mover) do |mover| - allow(mover).to receive(:move) { raise ActiveRecord::QueryCanceled } + allow(mover).to receive(:move) { raise RelativePositioning::NoSpaceLeft } end end shared_examples 'schedules issues rebalancing' do let(:issue) { build_stubbed(:issue, relative_position: 100, project: project) } - it 'schedules rebalancing if we time-out when moving' do + it 'schedules rebalancing if there is no space left' do lhs = build_stubbed(:issue, relative_position: 99, project: project) to_move = build(:issue, project: project) expect(IssueRebalancingWorker).to receive(:perform_async).with(nil, project_id, namespace_id) - expect { to_move.move_between(lhs, issue) }.to raise_error(ActiveRecord::QueryCanceled) + expect { to_move.move_between(lhs, issue) }.to raise_error(RelativePositioning::NoSpaceLeft) end end diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index a854a871034..74b1bacc560 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -69,13 +69,19 @@ RSpec.shared_examples 'namespace traversal scopes' do context 'when hierarchy_order is ascending' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_ancestors(hierarchy_order: :asc) } - it { is_expected.to eq [nested_group_1, nested_group_2, group_1, group_2] } + # Recursive order per level is not defined. + it { is_expected.to contain_exactly(nested_group_1, nested_group_2, group_1, group_2) } + it { expect(subject[0, 2]).to contain_exactly(nested_group_1, nested_group_2) } + it { expect(subject[2, 2]).to contain_exactly(group_1, group_2) } end context 'when hierarchy_order is descending' do subject { described_class.where(id: [nested_group_1, nested_group_2]).self_and_ancestors(hierarchy_order: :desc) } - it { is_expected.to eq [group_1, group_2, nested_group_1, nested_group_2] } + # Recursive order per level is not defined. + it { is_expected.to contain_exactly(nested_group_1, nested_group_2, group_1, group_2) } + it { expect(subject[0, 2]).to contain_exactly(group_1, group_2) } + it { expect(subject[2, 2]).to contain_exactly(nested_group_1, nested_group_2) } end end diff --git a/yarn.lock b/yarn.lock index 17e64b8cef7..b27026f47fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,15 +974,15 @@ resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== -"@gitlab/ui@32.10.2": - version "32.10.2" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.10.2.tgz#d663faaddd5aecf52e7e97b598dfb71a14da60c5" - integrity sha512-WJEVktJjwUoTrR9z8xa3gx73dWwLTq+wsXYH0mfRWzkXHnvjdDp3h8cYj3s1NZ0eAPQ6yVrkvYrTTgLVHZgSbA== +"@gitlab/ui@32.11.0": + version "32.11.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.11.0.tgz#8c4a1724c1733a243f96e4a4813ae7f348502ba6" + integrity sha512-EqP5Ub/IWEi5ErX0txx5vsd6hF7d7dOT5GqaRX6rVaLsUhWLYQZ8ld2yEl5Hx7FLki1t3uag17KII5FcvRTDLg== dependencies: "@babel/standalone" "^7.0.0" bootstrap-vue "2.18.1" copy-to-clipboard "^3.0.8" - dompurify "^2.3.1" + dompurify "^2.3.2" echarts "^4.9.0" highlight.js "^10.6.0" js-beautify "^1.8.8" @@ -4675,7 +4675,7 @@ dompurify@2.3.0: resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2" integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw== -dompurify@^2.3.1, dompurify@^2.3.2: +dompurify@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.2.tgz#c773efa410abb5c087c7caf44934fefa448f6e60" integrity sha512-jXJnvWloI+scD+N5uBikpUMsYXZb0LCAXxLFAOLS5duCzKfXLqBCpuINvFOiI4eJgTLggrngljT18HNoakHUsA== |