diff options
89 files changed, 1049 insertions, 352 deletions
diff --git a/app/assets/javascripts/access_tokens/components/projects_token_selector.vue b/app/assets/javascripts/access_tokens/components/projects_token_selector.vue index a746f62b3a1..4843c52fcbb 100644 --- a/app/assets/javascripts/access_tokens/components/projects_token_selector.vue +++ b/app/assets/javascripts/access_tokens/components/projects_token_selector.vue @@ -148,7 +148,7 @@ export default { </template> <template #dropdown-footer> <gl-intersection-observer v-if="projects.pageInfo.hasNextPage" @appear="loadMoreProjects"> - <gl-loading-icon v-if="isLoadingMoreProjects" size="md" /> + <gl-loading-icon v-if="isLoadingMoreProjects" class="gl-mb-3" size="sm" /> </gl-intersection-observer> </template> </gl-token-selector> diff --git a/app/assets/javascripts/admin/statistics_panel/components/app.vue b/app/assets/javascripts/admin/statistics_panel/components/app.vue index 1f0db422807..f250bdae4f5 100644 --- a/app/assets/javascripts/admin/statistics_panel/components/app.vue +++ b/app/assets/javascripts/admin/statistics_panel/components/app.vue @@ -29,7 +29,7 @@ export default { <div class="gl-card"> <div class="gl-card-body"> <h4>{{ __('Statistics') }}</h4> - <gl-loading-icon v-if="isLoading" size="md" class="my-3" /> + <gl-loading-icon v-if="isLoading" size="lg" class="my-3" /> <template v-else> <p v-for="statistic in getStatistics(statisticsLabels)" diff --git a/app/assets/javascripts/blob/components/blob_content.vue b/app/assets/javascripts/blob/components/blob_content.vue index 9832ebbea5c..f032e2e7fb8 100644 --- a/app/assets/javascripts/blob/components/blob_content.vue +++ b/app/assets/javascripts/blob/components/blob_content.vue @@ -66,7 +66,7 @@ export default { </script> <template> <div class="blob-viewer" :data-type="activeViewer.type" :data-loaded="!loading"> - <gl-loading-icon v-if="loading" size="md" color="dark" class="my-4 mx-auto" /> + <gl-loading-icon v-if="loading" size="lg" color="dark" class="my-4 mx-auto" /> <template v-else> <blob-content-error diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index af1d0bf0807..98ce1ac7f97 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -253,7 +253,7 @@ export default { <div class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden" > - <gl-loading-icon v-if="item.isLoading" size="md" class="mt-3" /> + <gl-loading-icon v-if="item.isLoading" size="lg" class="mt-3" /> <span v-if="item.referencePath" class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3" diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue index f1cc7ae7d75..247910301e7 100644 --- a/app/assets/javascripts/boards/components/project_select.vue +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -140,7 +140,7 @@ export default { <span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span> </gl-dropdown-text> <gl-intersection-observer v-if="hasNextPage" @appear="loadMoreProjects"> - <gl-loading-icon v-if="groupProjectsFlags.isLoadingMore" size="md" /> + <gl-loading-icon v-if="groupProjectsFlags.isLoadingMore" size="lg" /> </gl-intersection-observer> </gl-dropdown> </div> diff --git a/app/assets/javascripts/clusters/agents/components/activity_events_list.vue b/app/assets/javascripts/clusters/agents/components/activity_events_list.vue index d996da259f5..18c6503bfb2 100644 --- a/app/assets/javascripts/clusters/agents/components/activity_events_list.vue +++ b/app/assets/javascripts/clusters/agents/components/activity_events_list.vue @@ -124,7 +124,7 @@ export default { <template> <div> - <gl-loading-icon v-if="isLoading" size="md" /> + <gl-loading-icon v-if="isLoading" size="lg" /> <div v-else-if="hasEvents"> <div diff --git a/app/assets/javascripts/clusters/agents/components/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue index 5df3e0811a5..e3de8339325 100644 --- a/app/assets/javascripts/clusters/agents/components/show.vue +++ b/app/assets/javascripts/clusters/agents/components/show.vue @@ -140,7 +140,7 @@ export default { </span> </template> - <gl-loading-icon v-if="isLoading" size="md" class="gl-m-3" /> + <gl-loading-icon v-if="isLoading" size="lg" class="gl-m-3" /> <div v-else> <token-table :tokens="tokens" :cluster-agent-id="clusterAgent.id" :cursor="cursor" /> diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue index 89b18ed6d06..8a4a81d3e96 100644 --- a/app/assets/javascripts/clusters_list/components/agents.vue +++ b/app/assets/javascripts/clusters_list/components/agents.vue @@ -175,7 +175,7 @@ export default { </script> <template> - <gl-loading-icon v-if="isLoading" size="md" /> + <gl-loading-icon v-if="isLoading" size="lg" /> <section v-else-if="agentList"> <div v-if="agentList.length"> diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index 59cfdde731d..02eac5a6268 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -224,7 +224,7 @@ export default { </script> <template> - <gl-loading-icon v-if="loadingClusters" size="md" /> + <gl-loading-icon v-if="loadingClusters" size="lg" /> <section v-else> <ancestor-notice /> diff --git a/app/assets/javascripts/clusters_list/components/clusters_view_all.vue b/app/assets/javascripts/clusters_list/components/clusters_view_all.vue index 73ca804e111..d831d79b994 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_view_all.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_view_all.vue @@ -89,7 +89,7 @@ export default { </script> <template> <div> - <gl-loading-icon v-if="isLoading" size="md" /> + <gl-loading-icon v-if="isLoading" size="lg" /> <div v-show="!isLoading" data-testid="clusters-cards-container"> <gl-card header-class="gl-bg-white gl-display-flex gl-align-items-center gl-justify-content-space-between gl-py-4" diff --git a/app/assets/javascripts/content_editor/components/loading_indicator.vue b/app/assets/javascripts/content_editor/components/loading_indicator.vue index 620324adb06..7bc953e0dc3 100644 --- a/app/assets/javascripts/content_editor/components/loading_indicator.vue +++ b/app/assets/javascripts/content_editor/components/loading_indicator.vue @@ -34,7 +34,7 @@ export default { class="gl-w-full gl-display-flex gl-justify-content-center gl-align-items-center gl-absolute gl-top-0 gl-bottom-0" > <div class="gl-bg-white gl-absolute gl-w-full gl-h-full gl-opacity-3"></div> - <gl-loading-icon size="md" /> + <gl-loading-icon size="lg" /> </div> </editor-state-observer> </template> diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue new file mode 100644 index 00000000000..9e886fd7a48 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_app.vue @@ -0,0 +1,73 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import createFlash from '~/flash'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import GetFailedJobsQuery from '../../graphql/queries/get_failed_jobs.query.graphql'; +import { prepareFailedJobs } from './utils'; +import FailedJobsTable from './failed_jobs_table.vue'; + +export default { + components: { + GlLoadingIcon, + FailedJobsTable, + }, + inject: { + fullPath: { + default: '', + }, + pipelineIid: { + default: '', + }, + }, + props: { + failedJobsSummary: { + type: Array, + required: true, + }, + }, + apollo: { + failedJobs: { + query: GetFailedJobsQuery, + variables() { + return { + fullPath: this.fullPath, + pipelineIid: this.pipelineIid, + }; + }, + update({ project }) { + if (project?.pipeline?.jobs?.nodes) { + return project.pipeline.jobs.nodes.map((job) => { + return { normalizedId: getIdFromGraphQLId(job.id), ...job }; + }); + } + return []; + }, + result() { + this.preparedFailedJobs = prepareFailedJobs(this.failedJobs, this.failedJobsSummary); + }, + error() { + createFlash({ message: s__('Jobs|There was a problem fetching the failed jobs.') }); + }, + }, + }, + data() { + return { + failedJobs: [], + preparedFailedJobs: [], + }; + }, + computed: { + loading() { + return this.$apollo.queries.failedJobs.loading; + }, + }, +}; +</script> + +<template> + <div> + <gl-loading-icon v-if="loading" size="lg" class="gl-mt-4" /> + <failed-jobs-table v-else :failed-jobs="preparedFailedJobs" /> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue new file mode 100644 index 00000000000..1c646bdf3d6 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue @@ -0,0 +1,111 @@ +<script> +import { GlButton, GlLink, GlSafeHtmlDirective, GlTableLite } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import createFlash from '~/flash'; +import { redirectTo } from '~/lib/utils/url_utility'; +import CiBadge from '~/vue_shared/components/ci_badge_link.vue'; +import RetryFailedJobMutation from '../../graphql/mutations/retry_failed_job.mutation.graphql'; +import { DEFAULT_FIELDS } from '../../constants'; + +export default { + fields: DEFAULT_FIELDS, + retry: __('Retry'), + components: { + CiBadge, + GlButton, + GlLink, + GlTableLite, + }, + directives: { + SafeHtml: GlSafeHtmlDirective, + }, + props: { + failedJobs: { + type: Array, + required: true, + }, + }, + methods: { + async retryJob(id) { + try { + const { + data: { + jobRetry: { errors, job }, + }, + } = await this.$apollo.mutate({ + mutation: RetryFailedJobMutation, + variables: { id }, + }); + if (errors.length > 0) { + this.showErrorMessage(); + } else { + redirectTo(job.detailedStatus.detailsPath); + } + } catch { + this.showErrorMessage(); + } + }, + canRetryJob(job) { + return job.retryable && job.userPermissions.updateBuild; + }, + showErrorMessage() { + createFlash({ message: s__('Job|There was a problem retrying the failed job.') }); + }, + }, +}; +</script> + +<template> + <gl-table-lite :items="failedJobs" :fields="$options.fields" stacked="lg" fixed> + <template #table-colgroup="{ fields }"> + <col v-for="field in fields" :key="field.key" :class="field.columnClass" /> + </template> + + <template #cell(name)="{ item }"> + <div + class="gl-display-flex gl-align-items-center gl-lg-justify-content-start gl-justify-content-end" + > + <ci-badge :status="item.detailedStatus" :show-text="false" class="gl-mr-3" /> + <div class="gl-text-truncate"> + <gl-link + :href="item.detailedStatus.detailsPath" + class="gl-font-weight-bold gl-text-gray-900!" + > + {{ item.name }} + </gl-link> + </div> + </div> + </template> + + <template #cell(stage)="{ item }"> + <div class="gl-text-truncate"> + <span>{{ item.stage.name }}</span> + </div> + </template> + + <template #cell(failure)="{ item }"> + <span>{{ item.failure }}</span> + </template> + + <template #cell(actions)="{ item }"> + <gl-button + v-if="canRetryJob(item)" + icon="repeat" + :title="$options.retry" + :aria-label="$options.retry" + @click="retryJob(item.id)" + /> + </template> + + <template #row-details="{ item }"> + <pre + v-if="item.userPermissions.readBuild" + class="gl-w-full gl-text-left gl-border-none" + data-testid="job-log" + > + <code v-safe-html="item.failureSummary" class="gl-reset-bg gl-p-0" > + </code> + </pre> + </template> + </gl-table-lite> +</template> diff --git a/app/assets/javascripts/pipelines/components/jobs/utils.js b/app/assets/javascripts/pipelines/components/jobs/utils.js new file mode 100644 index 00000000000..c8414d44d14 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/jobs/utils.js @@ -0,0 +1,33 @@ +/* + We get the failure and failure summary from Rails which has + a summary failure log. Here we combine that data with the data + from GraphQL to display the log. + + failedJobs is from GraphQL + failedJobsSummary is from Rails + */ + +export const prepareFailedJobs = (failedJobs = [], failedJobsSummary = []) => { + const combinedJobs = []; + + if (failedJobs.length > 0 && failedJobsSummary.length > 0) { + failedJobs.forEach((failedJob) => { + const foundJob = failedJobsSummary.find( + (failedJobSummary) => failedJob.normalizedId === failedJobSummary.id, + ); + + if (foundJob) { + combinedJobs.push({ + ...failedJob, + failure: foundJob?.failure, + failureSummary: foundJob?.failure_summary, + // this field is needed for the slot row-details + // on the failed_jobs_table.vue component + _showDetails: true, + }); + } + }); + } + + return combinedJobs; +}; diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js index 8da6bf6d4c4..0510992e962 100644 --- a/app/assets/javascripts/pipelines/constants.js +++ b/app/assets/javascripts/pipelines/constants.js @@ -85,3 +85,27 @@ export const TOAST_MESSAGE = s__('Pipeline|Creating pipeline.'); export const BUTTON_TOOLTIP_RETRY = __('Retry failed jobs'); export const BUTTON_TOOLTIP_CANCEL = __('Cancel'); + +export const DEFAULT_FIELDS = [ + { + key: 'name', + label: __('Name'), + columnClass: 'gl-w-20p', + }, + { + key: 'stage', + label: __('Stage'), + columnClass: 'gl-w-20p', + }, + { + key: 'failure', + label: __('Failure'), + columnClass: 'gl-w-40p', + }, + { + key: 'actions', + label: '', + tdClass: 'gl-text-right', + columnClass: 'gl-w-20p', + }, +]; diff --git a/app/assets/javascripts/pipelines/graphql/mutations/retry_failed_job.mutation.graphql b/app/assets/javascripts/pipelines/graphql/mutations/retry_failed_job.mutation.graphql new file mode 100644 index 00000000000..1955cc9b0ac --- /dev/null +++ b/app/assets/javascripts/pipelines/graphql/mutations/retry_failed_job.mutation.graphql @@ -0,0 +1,12 @@ +mutation retryFailedJob($id: CiBuildID!) { + jobRetry(input: { id: $id }) { + job { + id + detailedStatus { + id + detailsPath + } + } + errors + } +} diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_failed_jobs.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_failed_jobs.query.graphql new file mode 100644 index 00000000000..14e9a838f4b --- /dev/null +++ b/app/assets/javascripts/pipelines/graphql/queries/get_failed_jobs.query.graphql @@ -0,0 +1,41 @@ +query getFailedJobs($fullPath: ID!, $pipelineIid: ID!) { + project(fullPath: $fullPath) { + id + pipeline(iid: $pipelineIid) { + id + jobs(statuses: FAILED) { + nodes { + status + detailedStatus { + id + detailsPath + group + icon + label + text + tooltip + action { + id + buttonTitle + icon + method + path + title + } + } + id + stage { + id + name + } + name + retryable + userPermissions { + readBuild + updateBuild + } + } + } + } + } +} diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 338de65e795..fd869014570 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -1,10 +1,11 @@ import createFlash from '~/flash'; -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; import createDagApp from './pipeline_details_dag'; import { createPipelinesDetailApp } from './pipeline_details_graph'; import { createPipelineHeaderApp } from './pipeline_details_header'; import { createPipelineNotificationApp } from './pipeline_details_notification'; import { createPipelineJobsApp } from './pipeline_details_jobs'; +import { createPipelineFailedJobsApp } from './pipeline_details_failed_jobs'; import { apolloProvider } from './pipeline_shared_client'; import { createTestDetails } from './pipeline_test_details'; @@ -16,6 +17,7 @@ const SELECTORS = { PIPELINE_TABS: '#js-pipeline-tabs', PIPELINE_TESTS: '#js-pipeline-tests-detail', PIPELINE_JOBS: '#js-pipeline-jobs-vue', + PIPELINE_FAILED_JOBS: '#js-pipeline-failed-jobs-vue', }; export default async function initPipelineDetailsBundle() { @@ -79,5 +81,15 @@ export default async function initPipelineDetailsBundle() { message: __('An error occurred while loading the Jobs tab.'), }); } + + if (gon.features?.failedJobsTabVue) { + try { + createPipelineFailedJobsApp(SELECTORS.PIPELINE_FAILED_JOBS); + } catch { + createFlash({ + message: s__('Jobs|An error occurred while loading the Failed Jobs tab.'), + }); + } + } } } diff --git a/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js b/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js new file mode 100644 index 00000000000..7bf3b64bf47 --- /dev/null +++ b/app/assets/javascripts/pipelines/pipeline_details_failed_jobs.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import FailedJobsApp from './components/jobs/failed_jobs_app.vue'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +export const createPipelineFailedJobsApp = (selector) => { + const containerEl = document.querySelector(selector); + + if (!containerEl) { + return false; + } + + const { fullPath, pipelineIid, failedJobsSummaryData } = containerEl.dataset; + + return new Vue({ + el: containerEl, + apolloProvider, + provide: { + fullPath, + pipelineIid, + }, + render(createElement) { + return createElement(FailedJobsApp, { + props: { + failedJobsSummary: JSON.parse(failedJobsSummaryData), + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql b/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql index 1a7a9629b07..d9b9c04fd63 100644 --- a/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/remove_attention_request.mutation.graphql @@ -1,4 +1,4 @@ -mutation mergeRequestRemoveAttentionRequest($projectPath: ID!, $iid: String!, $userId: ID!) { +mutation mergeRequestRemoveAttentionRequest($projectPath: ID!, $iid: String!, $userId: UserID!) { mergeRequestRemoveAttentionRequest( input: { projectPath: $projectPath, iid: $iid, userId: $userId } ) { diff --git a/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql b/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql index 99f9beea17b..99a86e4fe5c 100644 --- a/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/request_attention.mutation.graphql @@ -1,4 +1,4 @@ -mutation mergeRequestRequestAttention($projectPath: ID!, $iid: String!, $userId: ID!) { +mutation mergeRequestRequestAttention($projectPath: ID!, $iid: String!, $userId: UserID!) { mergeRequestRequestAttention(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) { errors } diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index ebbc1bfb037..388353bc35b 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -172,7 +172,9 @@ export default { :img-src="author.avatar_url" :img-alt="userImageAltDescription" :tooltip-text="author.username" + :img-size="16" class="avatar-image-container text-decoration-none" + img-css-classes="gl-mr-3" /> <tooltip-on-truncate :title="title" class="flex-truncate-child"> <gl-link :href="commitUrl" class="commit-row-message cgray">{{ title }}</gl-link> diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index f4e7755c838..a958f10e778 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -23,6 +23,7 @@ class Projects::PipelinesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:pipeline_tabs_vue, @project, default_enabled: :yaml) push_frontend_feature_flag(:downstream_retry_action, @project, default_enabled: :yaml) + push_frontend_feature_flag(:failed_jobs_tab_vue, @project, default_enabled: :yaml) end # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 1321111faaf..8f83e34411b 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -10,8 +10,6 @@ class Projects::ServicesController < Projects::ApplicationController before_action :integration before_action :default_integration, only: [:edit, :update] before_action :web_hook_logs, only: [:edit, :update] - before_action :set_deprecation_notice_for_prometheus_integration, only: [:edit, :update] - before_action :redirect_deprecated_prometheus_integration, only: [:update] respond_to :html @@ -118,18 +116,6 @@ class Projects::ServicesController < Projects::ApplicationController .merge(errors: integration.errors.as_json) end - def redirect_deprecated_prometheus_integration - redirect_to edit_project_integration_path(project, integration) if integration.is_a?(::Integrations::Prometheus) && Feature.enabled?(:settings_operations_prometheus_service, project) - end - - def set_deprecation_notice_for_prometheus_integration - return if !integration.is_a?(::Integrations::Prometheus) || !Feature.enabled?(:settings_operations_prometheus_service, project) - - operations_link_start = "<a href=\"#{project_settings_operations_path(project)}\">" - message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page have been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "</a>" } - flash.now[:alert] = message.html_safe - end - def use_inherited_settings?(attributes) default_integration && attributes[:inherit_from_id] == default_integration.id.to_s end diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index 22b5d217af9..d4126cbd708 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -134,7 +134,7 @@ module Projects # overridden in EE def permitted_project_params - project_params = { + { incident_management_setting_attributes: ::Gitlab::Tracking::IncidentManagement.tracking_keys.keys, metrics_setting_attributes: [:external_dashboard_url, :dashboard_timezone], @@ -150,12 +150,6 @@ module Projects grafana_integration_attributes: [:token, :grafana_url, :enabled], tracing_setting_attributes: [:external_url] } - - if Feature.enabled?(:settings_operations_prometheus_service, project) - project_params[:prometheus_integration_attributes] = [:manual_configuration, :api_url] - end - - project_params end end end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index bdc5bfa62eb..9c604209409 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -35,6 +35,12 @@ module Types field :executor_name, GraphQL::Types::String, null: true, description: 'Executor last advertised by the runner.', method: :executor_name + field :platform_name, GraphQL::Types::String, null: true, + description: 'Platform provided by the runner.', + method: :platform + field :architecture_name, GraphQL::Types::String, null: true, + description: 'Architecture provided by the the runner.', + method: :architecture field :groups, ::Types::GroupType.connection_type, null: true, description: 'Groups the runner is associated with. For group runners only.' field :id, ::Types::GlobalIDType[::Ci::Runner], null: false, diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb index bfdb830f2c3..b4a2cf7bb1e 100644 --- a/app/helpers/ci/builds_helper.rb +++ b/app/helpers/ci/builds_helper.rb @@ -36,5 +36,15 @@ module Ci description: project_job_url(@project, @build) } end + + def prepare_failed_jobs_summary_data(failed_builds) + failed_builds.map do |build| + { + id: build.id, + failure: build.present.callout_failure_message, + failure_summary: build_summary(build) + } + end.to_json + end end end diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index bc2fedec69c..66b8b783ac2 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -115,4 +115,4 @@ = render 'admin/application_settings/snowplow' = render 'admin/application_settings/eks' = render 'admin/application_settings/floc' -= render_if_exists 'admin/application_settings/license_file' += render_if_exists 'admin/application_settings/add_license' diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 5c0487db0fc..50a1b474504 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -9,7 +9,7 @@ = render 'shared/milestones/search_form' = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @group) - = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm", data: { qa_selector: "new_group_milestone_link" } + = link_to _('New milestone'), new_group_milestone_path(@group), class: "btn gl-button btn-confirm gl-ml-3", data: { qa_selector: "new_group_milestone_link" } - if @milestones.blank? = render 'shared/empty_states/milestones_tab', learn_more_path: help_page_path('user/project/milestones/index') do diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index 154a92e6ec8..326a7c4027f 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -9,7 +9,7 @@ = render 'shared/milestones/search_form' = render 'shared/milestones_sort_dropdown' - if can?(current_user, :admin_milestone, @project) - = link_to new_project_milestone_path(@project), class: 'gl-button btn btn-confirm', data: { qa_selector: "new_project_milestone_link" }, title: _('New milestone') do + = link_to new_project_milestone_path(@project), class: 'gl-button btn btn-confirm gl-ml-3', data: { qa_selector: "new_project_milestone_link" }, title: _('New milestone') do = _('New milestone') - if @milestones.blank? diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 03fc7548c84..4ef2bdd35bf 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -32,41 +32,45 @@ #js-pipeline-jobs-vue{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid } } - if @pipeline.failed_builds.present? - #js-tab-failures.build-failures.tab-pane.build-page - %table.table.gl-table.responsive-table.ci-table.responsive-table-sm-rounded - %thead - %th - %th= _('Name') - %th= _('Stage') - %th= _('Failure') - %th + #js-tab-failures.tab-pane + - if Feature.enabled?(:failed_jobs_tab_vue, @project, default_enabled: :yaml) + #js-pipeline-failed-jobs-vue{ data: { full_path: @project.full_path, pipeline_iid: @pipeline.iid, failed_jobs_summary_data: prepare_failed_jobs_summary_data(@pipeline.failed_builds) } } + - else + .build-failures.build-page + %table.table.gl-table.responsive-table.ci-table.responsive-table-sm-rounded + %thead + %th + %th= _('Name') + %th= _('Stage') + %th= _('Failure') + %th - %tbody - - @pipeline.failed_builds.each_with_index do |build, index| - - job = build.present(current_user: current_user) - %tr.build-state.responsive-table-border-start - %td.responsive-table-cell.ci-status-icon-failed{ data: { column: _('Status')} } - .d-none.d-md-block.build-icon - = sprite_icon("status_#{build.status}") - .d-md-none.build-badge - = render "ci/status/badge", link: false, status: job.detailed_status(current_user) - %td.responsive-table-cell.build-name{ data: { column: _('Name')} } - = link_to build.name, pipeline_job_url(pipeline, build) - %td.responsive-table-cell.build-stage{ data: { column: _('Stage')} } - = build.stage.titleize - %td.responsive-table-cell.build-failure{ data: { column: _('Failure')} } - = build.present.callout_failure_message - %td.responsive-table-cell.build-actions - - if can?(current_user, :update_build, job) && job.retryable? - = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'gl-button btn btn-default btn-icon' do - = sprite_icon('repeat', css_class: 'gl-icon') - - if can?(current_user, :read_build, job) - %tr.build-log-row.responsive-table-border-end - %td - %td.responsive-table-cell.build-log-container{ colspan: 4 } - %pre.build-log.build-log-rounded - %code.bash.js-build-output - = build_summary(build) + %tbody + - @pipeline.failed_builds.each_with_index do |build, index| + - job = build.present(current_user: current_user) + %tr.build-state.responsive-table-border-start + %td.responsive-table-cell.ci-status-icon-failed{ data: { column: _('Status')} } + .d-none.d-md-block.build-icon + = sprite_icon("status_#{build.status}") + .d-md-none.build-badge + = render "ci/status/badge", link: false, status: job.detailed_status(current_user) + %td.responsive-table-cell.build-name{ data: { column: _('Name')} } + = link_to build.name, pipeline_job_url(pipeline, build) + %td.responsive-table-cell.build-stage{ data: { column: _('Stage')} } + = build.stage.titleize + %td.responsive-table-cell.build-failure{ data: { column: _('Failure')} } + = build.present.callout_failure_message + %td.responsive-table-cell.build-actions + - if can?(current_user, :update_build, job) && job.retryable? + = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'gl-button btn btn-default btn-icon' do + = sprite_icon('repeat', css_class: 'gl-icon') + - if can?(current_user, :read_build, job) + %tr.build-log-row.responsive-table-border-end + %td + %td.responsive-table-cell.build-log-container{ colspan: 4 } + %pre.build-log.build-log-rounded + %code.bash.js-build-output + = build_summary(build) #js-tab-dag.tab-pane #js-pipeline-dag-vue{ data: { pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), about_dag_doc_path: help_page_path('ci/directed_acyclic_graph/index.md'), dag_doc_path: help_page_path('ci/yaml/index.md', anchor: 'needs')} } diff --git a/app/views/projects/settings/operations/_prometheus.html.haml b/app/views/projects/settings/operations/_prometheus.html.haml deleted file mode 100644 index 93281cc225b..00000000000 --- a/app/views/projects/settings/operations/_prometheus.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -%section.settings.no-animate.js-prometheus-settings - .settings-header - %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only - = _('Prometheus') - %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' } - = _('Expand') - %p - = _('Link Prometheus monitoring to GitLab.') - = link_to _('More information'), help_page_path('user/project/integrations/prometheus'), target: '_blank', rel: 'noopener noreferrer' - .settings-content - - if @project - = render 'shared/prometheus_configuration_banner', project: @project, integration: service, header_tag: :b, info_well_classes: 'gl-p-3 gl-mt-3' - - %b.gl-mb-3 - = s_('PrometheusService|Manual configuration') - %p - = s_('PrometheusService|Auto configuration settings are used unless you override their values here.') diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml index 9a31666c316..63eb2814309 100644 --- a/app/views/projects/settings/operations/show.html.haml +++ b/app/views/projects/settings/operations/show.html.haml @@ -24,4 +24,3 @@ = render 'projects/settings/operations/incidents' = render 'projects/settings/operations/grafana_integration' = render_if_exists 'projects/settings/operations/status_page' -= render 'projects/settings/operations/prometheus', service: prometheus_integration if Feature.enabled?(:settings_operations_prometheus_service) diff --git a/app/views/shared/_milestones_sort_dropdown.html.haml b/app/views/shared/_milestones_sort_dropdown.html.haml index 1c6eb7aa96b..b68022bfeda 100644 --- a/app/views/shared/_milestones_sort_dropdown.html.haml +++ b/app/views/shared/_milestones_sort_dropdown.html.haml @@ -1,4 +1,4 @@ - milestones_sort_options = milestones_sort_options_hash.map { |value, text| { value: value, text: text, href: page_filter_path(sort: value) } } %div{ data: {testid: 'milestone_sort_by_dropdown'} } - = gl_redirect_listbox_tag milestones_sort_options, @sort, class: 'gl-ml-3' + = gl_redirect_listbox_tag milestones_sort_options, @sort diff --git a/config/feature_flags/development/settings_operations_prometheus_service.yml b/config/feature_flags/development/failed_jobs_tab_vue.yml index 93afe504b21..1c70bd5b418 100644 --- a/config/feature_flags/development/settings_operations_prometheus_service.yml +++ b/config/feature_flags/development/failed_jobs_tab_vue.yml @@ -1,8 +1,8 @@ --- -name: settings_operations_prometheus_service -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560 -milestone: '12.8' +name: failed_jobs_tab_vue +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86151 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360849 +milestone: '15.0' type: development -group: group::respond +group: group::pipeline execution default_enabled: false diff --git a/config/feature_flags/development/omniauth_initializer_fullhost_proc.yml b/config/feature_flags/development/omniauth_initializer_fullhost_proc.yml index 75ed8e642c6..52743818b5a 100644 --- a/config/feature_flags/development/omniauth_initializer_fullhost_proc.yml +++ b/config/feature_flags/development/omniauth_initializer_fullhost_proc.yml @@ -1,8 +1,8 @@ --- name: omniauth_initializer_fullhost_proc -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82401 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82703 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355579 milestone: '14.10' type: development group: group::geo -default_enabled: false +default_enabled: true diff --git a/config/initializers/0_marginalia.rb b/config/initializers/0_marginalia.rb index 344b24252e5..e88599fd93c 100644 --- a/config/initializers/0_marginalia.rb +++ b/config/initializers/0_marginalia.rb @@ -17,9 +17,9 @@ Marginalia::Comment.components = [:application, :correlation_id, :jid, :endpoint # As mentioned in https://github.com/basecamp/marginalia/pull/93/files, # adding :line has some overhead because a regexp on the backtrace has -# to be run on every SQL query. Only enable this in development because +# to be run on every SQL query. Only enable this in development and test because # we've seen it slow things down. -if Rails.env.development? +if Gitlab.dev_or_test_env? Marginalia::Comment.components << :line Marginalia::Comment.lines_to_ignore = Regexp.union( Gitlab::BacktraceCleaner::IGNORE_BACKTRACES + %w[ diff --git a/doc/administration/monitoring/performance/request_profiling.md b/doc/administration/monitoring/performance/request_profiling.md new file mode 100644 index 00000000000..d0d05c16ea0 --- /dev/null +++ b/doc/administration/monitoring/performance/request_profiling.md @@ -0,0 +1,12 @@ +--- +stage: Monitor +group: Respond +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +remove_date: '2022-07-26' +redirect_to: 'index.md' +--- + +# Request profiling (removed) **(FREE SELF)** + +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/352488) in GitLab 14.8 +and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/350152) in 15.0. diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 25364361cc1..d07b84cbe69 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -9483,6 +9483,7 @@ Represents the total number of issues and their weights for a particular day. | <a id="cirunneraccesslevel"></a>`accessLevel` | [`CiRunnerAccessLevel!`](#cirunneraccesslevel) | Access level of the runner. | | <a id="cirunneractive"></a>`active` **{warning-solid}** | [`Boolean!`](#boolean) | **Deprecated** in 14.8. Use paused. | | <a id="cirunneradminurl"></a>`adminUrl` | [`String`](#string) | Admin URL of the runner. Only available for administrators. | +| <a id="cirunnerarchitecturename"></a>`architectureName` | [`String`](#string) | Architecture provided by the the runner. | | <a id="cirunnercontactedat"></a>`contactedAt` | [`Time`](#time) | Timestamp of last contact from this runner. | | <a id="cirunnercreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of creation of this runner. | | <a id="cirunnerdescription"></a>`description` | [`String`](#string) | Description of the runner. | @@ -9495,6 +9496,7 @@ Represents the total number of issues and their weights for a particular day. | <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. | | <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. | | <a id="cirunnerpaused"></a>`paused` | [`Boolean!`](#boolean) | Indicates the runner is paused and not available to run jobs. | +| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. | | <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). | | <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. | | <a id="cirunnerprojects"></a>`projects` | [`ProjectConnection`](#projectconnection) | Projects the runner is associated with. For project runners only. (see [Connections](#connections)) | diff --git a/doc/api/linked_epics.md b/doc/api/linked_epics.md index df302be0555..0d2176dfc61 100644 --- a/doc/api/linked_epics.md +++ b/doc/api/linked_epics.md @@ -6,10 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Linked epics API **(ULTIMATE)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352493) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `related_epics_widget`. Enabled by default. - -FLAG: -On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../administration/feature_flags.md) named `related_epics_widget`. On GitLab.com, this feature is available. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352493) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `related_epics_widget`. Enabled by default. +> - [Feature flag `related_epics_widget`](https://gitlab.com/gitlab-org/gitlab/-/issues/357089) removed in GitLab 15.0. If the Related Epics feature is not available in your GitLab plan, a `403` status code is returned. diff --git a/doc/user/admin_area/license_file.md b/doc/user/admin_area/license_file.md index 5999e774d26..ff9e87680f9 100644 --- a/doc/user/admin_area/license_file.md +++ b/doc/user/admin_area/license_file.md @@ -18,8 +18,7 @@ Otherwise, to add your license: 1. Sign in to GitLab as an administrator. 1. On the top bar, select **Menu > Admin**. 1. On the left sidebar, select **Settings > General**. -1. In the **License file** area, select **Add a license**. -1. Add a license by either uploading the file or pasting the key. +1. In the **Add License** area, add a license by either uploading the file or entering the key. 1. Select the **Terms of Service** checkbox. 1. Select **Add license**. diff --git a/doc/user/group/epics/linked_epics.md b/doc/user/group/epics/linked_epics.md index 1fdf2ee3e52..89699661efa 100644 --- a/doc/user/group/epics/linked_epics.md +++ b/doc/user/group/epics/linked_epics.md @@ -6,12 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Linked epics **(ULTIMATE)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353473) in GitLab 14.9 [with a flag](../../../administration/feature_flags.md) named `related_epics_widget`. Enabled by default. - -FLAG: -On self-managed GitLab, by default this feature is available. To hide the feature, -ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) -named `related_epics_widget`. On GitLab.com, this feature is available. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/353473) in GitLab 14.9 [with a flag](../../../administration/feature_flags.md) named `related_epics_widget`. Enabled by default. +> - [Feature flag `related_epics_widget`](https://gitlab.com/gitlab-org/gitlab/-/issues/357089) removed in GitLab 15.0. Linked epics are a bi-directional relationship between any two epics and appear in a block below the epic description. You can link epics in different groups. diff --git a/lefthook.yml b/lefthook.yml index fb5e39c27c3..8bfa5d015c7 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -12,7 +12,7 @@ pre-push: tags: view haml style files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD glob: '*.html.haml' - run: bundle exec haml-lint --config .haml-lint.yml {files} + run: REVEAL_RUBOCOP_TODO=0 bundle exec haml-lint --config .haml-lint.yml {files} markdownlint: tags: documentation style files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 50de84a560e..9fb2a9c8c1c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -225,11 +225,6 @@ msgid_plural "%d errors" msgstr[0] "" msgstr[1] "" -msgid "%d error found:" -msgid_plural "%d errors found:" -msgstr[0] "" -msgstr[1] "" - msgid "%d exporter" msgid_plural "%d exporters" msgstr[0] "" @@ -769,9 +764,6 @@ msgstr "" msgid "%{level_name} is not allowed since the fork source project has lower visibility." msgstr "" -msgid "%{link_start}Add a license%{link_end} that you have received from GitLab Inc." -msgstr "" - msgid "%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it's ready." msgstr "" @@ -21684,6 +21676,9 @@ msgstr "" msgid "Jobs|All" msgstr "" +msgid "Jobs|An error occurred while loading the Failed Jobs tab." +msgstr "" + msgid "Jobs|Are you sure you want to proceed?" msgstr "" @@ -21714,6 +21709,9 @@ msgstr "" msgid "Jobs|Status" msgstr "" +msgid "Jobs|There was a problem fetching the failed jobs." +msgstr "" + msgid "Jobs|Use jobs to automate your tasks" msgstr "" @@ -21816,6 +21814,9 @@ msgstr "" msgid "Job|The artifacts will be removed" msgstr "" +msgid "Job|There was a problem retrying the failed job." +msgstr "" + msgid "Job|These artifacts are the latest. They will not be deleted (even if expired) until newer artifacts are available." msgstr "" @@ -22484,9 +22485,6 @@ msgstr "" msgid "License compliance" msgstr "" -msgid "License file" -msgstr "" - msgid "License key" msgstr "" @@ -22711,9 +22709,6 @@ msgstr "" msgid "Link (optional)" msgstr "" -msgid "Link Prometheus monitoring to GitLab." -msgstr "" - msgid "Link Sentry to GitLab to discover and view the errors your application generates." msgstr "" @@ -30090,9 +30085,6 @@ msgstr "" msgid "ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}" msgstr "" -msgid "Prometheus" -msgstr "" - msgid "PrometheusAlerts|exceeded" msgstr "" @@ -30189,9 +30181,6 @@ msgstr "" msgid "PrometheusService|Waiting for your first deployment to an environment to find common metrics" msgstr "" -msgid "PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page have been deprecated." -msgstr "" - msgid "PrometheusService|You have a cluster with the Prometheus integration enabled." msgstr "" @@ -37574,9 +37563,6 @@ msgstr[1] "" msgid "The fork relationship has been removed." msgstr "" -msgid "The form contains the following errors:" -msgstr "" - msgid "The form contains the following warning:" msgstr "" @@ -64,5 +64,10 @@ module QA "registry_with_cdn" => "RegistryWithCDN" ) + # Configure knapsack at the very begining of the setup + loader.on_setup do + QA::Support::KnapsackReport.configure! + end + loader.setup end diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb index 829bdbbc6ee..99906ca44d9 100644 --- a/qa/qa/scenario/template.rb +++ b/qa/qa/scenario/template.rb @@ -30,11 +30,6 @@ module QA Runtime::Scenario.define(:large_setup?, args.include?('can_use_large_setup')) ## - # Setup knapsack and download latest report - # - Support::KnapsackReport.configure! - - ## # Configure browser # Runtime::Browser.configure! diff --git a/qa/qa/specs/knapsack_runner.rb b/qa/qa/specs/knapsack_runner.rb index 123ecc5e1c3..4908553e43d 100644 --- a/qa/qa/specs/knapsack_runner.rb +++ b/qa/qa/specs/knapsack_runner.rb @@ -6,6 +6,7 @@ module QA def self.run(args) allocator = Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator + Knapsack.logger.info '==== Knapsack specs to execute =====' Knapsack.logger.info 'Report specs:' Knapsack.logger.info allocator.report_node_tests Knapsack.logger.info 'Leftover specs:' diff --git a/qa/qa/support/knapsack_report.rb b/qa/qa/support/knapsack_report.rb index afe60664bec..b8cf6743746 100644 --- a/qa/qa/support/knapsack_report.rb +++ b/qa/qa/support/knapsack_report.rb @@ -108,7 +108,7 @@ module QA # # @return [Logger] def logger - @logger ||= Logger.new($stdout) + @logger ||= Knapsack.logger end # GCS client diff --git a/qa/tasks/knapsack.rake b/qa/tasks/knapsack.rake index 03c295f1aff..61c153291b7 100644 --- a/qa/tasks/knapsack.rake +++ b/qa/tasks/knapsack.rake @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Rails/RakeEnvironment namespace :knapsack do desc "Run tests with knapsack runner" task :rspec, [:rspec_args] do |_, args| @@ -12,7 +13,6 @@ namespace :knapsack do exit RSpec::Core::Runner.run([*rspec_args, "qa/specs/features"]) end - QA::Support::KnapsackReport.configure! exit QA::Specs::KnapsackRunner.run(rspec_args) end @@ -31,3 +31,4 @@ namespace :knapsack do QA::Support::LongRunningSpecReporter.execute end end +# rubocop:enable Rails/RakeEnvironment diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index e9f1232b5e7..162c36f5069 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -929,13 +929,13 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do context 'when continue url is present' do let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) } + before do + post_cancel(continue: { to: url }) + end + context 'when continue to is a safe url' do let(:url) { '/test' } - before do - post_cancel(continue: { to: url }) - end - it 'redirects to the continue url' do expect(response).to have_gitlab_http_status(:found) expect(response).to redirect_to(url) @@ -949,8 +949,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do context 'when continue to is not a safe url' do let(:url) { 'http://example.com' } - it 'raises an error' do - expect { cancel_with_redirect(url) }.to raise_error + it 'redirects to the builds page' do + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(builds_namespace_project_pipeline_path(id: pipeline.id)) end end end diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 7e96c59fbb1..6802ebeb63e 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -326,56 +326,6 @@ RSpec.describe Projects::ServicesController do end end end - - context 'with Prometheus integration' do - let_it_be(:prometheus_integration) { create(:prometheus_integration, project: project) } - - let(:integration) { prometheus_integration } - let(:integration_params) { { manual_configuration: '1', api_url: 'http://example.com' } } - - context 'when feature flag :settings_operations_prometheus_service is enabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: true) - end - - it 'redirects user back to edit page with alert' do - put :update, params: project_params.merge(service: integration_params) - - expect(response).to redirect_to(edit_project_integration_path(project, integration)) - expected_alert = [ - "You can now manage your Prometheus settings on the", - %(<a href="#{project_settings_operations_path(project)}">Operations</a> page.), - "Fields on this page have been deprecated." - ].join(' ') - - expect(controller).to set_flash.now[:alert].to(expected_alert) - end - - it 'does not modify integration' do - expect { put :update, params: project_params.merge(service: integration_params) } - .not_to change { prometheus_integration_as_data } - end - - def prometheus_integration_as_data - pi = project.prometheus_integration.reload - attrs = pi.attributes.except('encrypted_properties', - 'encrypted_properties_iv') - - [attrs, pi.properties] - end - end - - context 'when feature flag :settings_operations_prometheus_service is disabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: false) - end - - it 'modifies integration' do - expect { put :update, params: project_params.merge(service: integration_params) } - .to change { project.prometheus_integration.reload.attributes } - end - end - end end describe 'GET #edit' do @@ -392,38 +342,6 @@ RSpec.describe Projects::ServicesController do end end end - - context 'with Prometheus service' do - let(:integration_param) { 'prometheus' } - - context 'when feature flag :settings_operations_prometheus_service is enabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: true) - get :edit, params: project_params(id: integration_param) - end - - it 'renders deprecation warning notice' do - expected_alert = [ - "You can now manage your Prometheus settings on the", - %(<a href="#{project_settings_operations_path(project)}">Operations</a> page.), - "Fields on this page have been deprecated." - ].join(' ') - - expect(controller).to set_flash.now[:alert].to(expected_alert) - end - end - - context 'when feature flag :settings_operations_prometheus_service is disabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: false) - get :edit, params: project_params(id: integration_param) - end - - it 'does not render deprecation warning notice' do - expect(controller).not_to set_flash.now[:alert] - end - end - end end private diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb index 7ef5371f2b5..c1fa91e9f8b 100644 --- a/spec/controllers/projects/settings/operations_controller_spec.rb +++ b/spec/controllers/projects/settings/operations_controller_spec.rb @@ -354,37 +354,6 @@ RSpec.describe Projects::Settings::OperationsController do end context 'prometheus integration' do - describe 'PATCH #update' do - let(:params) do - { - prometheus_integration_attributes: { - manual_configuration: '0', - api_url: 'https://gitlab.prometheus.rocks' - } - } - end - - context 'feature flag :settings_operations_prometheus_service is enabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: true) - end - - it_behaves_like 'PATCHable' - end - - context 'feature flag :settings_operations_prometheus_service is disabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: false) - end - - it_behaves_like 'PATCHable' do - let(:permitted_params) do - ActionController::Parameters.new(params.except(:prometheus_integration_attributes)).permit! - end - end - end - end - describe 'POST #reset_alerting_token' do context 'with existing alerting setting' do let!(:alerting_setting) do diff --git a/spec/features/projects/integrations/user_activates_prometheus_spec.rb b/spec/features/projects/integrations/user_activates_prometheus_spec.rb index 80629af6fce..56b895919b8 100644 --- a/spec/features/projects/integrations/user_activates_prometheus_spec.rb +++ b/spec/features/projects/integrations/user_activates_prometheus_spec.rb @@ -9,14 +9,13 @@ RSpec.describe 'User activates Prometheus' do stub_request(:get, /.*prometheus.example.com.*/) end - it 'does not activate integration and informs about deprecation', :js do + it 'saves and activates integration', :js do visit_project_integration('Prometheus') check('Active') fill_in('API URL', with: 'http://prometheus.example.com') click_button('Save changes') - expect(page).not_to have_content('Prometheus settings saved and active.') - expect(page).to have_content('Fields on this page have been deprecated.') + expect(page).to have_content('Prometheus settings saved and active.') end end diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 8581584aea4..c6a2ccb3e8c 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -1188,12 +1188,43 @@ RSpec.describe 'Pipeline', :js do expect(page).to have_content('There is an unknown failure, please try again') end + context 'when failed_jobs_tab_vue feature flag is disabled' do + before do + stub_feature_flags(failed_jobs_tab_vue: false) + end + + context 'when user does not have permission to retry build' do + it 'shows retry button for failed build' do + subject + + page.within(find('.build-failures', match: :first)) do + expect(page).not_to have_link('Retry') + end + end + end + + context 'when user does have permission to retry build' do + before do + create(:protected_branch, :developers_can_merge, + name: pipeline.ref, project: project) + end + + it 'shows retry button for failed build' do + subject + + page.within(find('.build-failures', match: :first)) do + expect(page).to have_link('Retry') + end + end + end + end + context 'when user does not have permission to retry build' do it 'shows retry button for failed build' do subject - page.within(find('.build-failures', match: :first)) do - expect(page).not_to have_link('Retry') + page.within(find('#js-tab-failures', match: :first)) do + expect(page).not_to have_button('Retry') end end end @@ -1207,8 +1238,8 @@ RSpec.describe 'Pipeline', :js do it 'shows retry button for failed build' do subject - page.within(find('.build-failures', match: :first)) do - expect(page).to have_link('Retry') + page.within(find('#js-tab-failures', match: :first)) do + expect(page).to have_button('Retry') end end end diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js new file mode 100644 index 00000000000..3b5632a8a4e --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js @@ -0,0 +1,87 @@ +import { GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import createFlash from '~/flash'; +import FailedJobsApp from '~/pipelines/components/jobs/failed_jobs_app.vue'; +import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue'; +import GetFailedJobsQuery from '~/pipelines/graphql/queries/get_failed_jobs.query.graphql'; +import { mockFailedJobsQueryResponse, mockFailedJobsSummaryData } from '../../mock_data'; + +Vue.use(VueApollo); + +jest.mock('~/flash'); + +describe('Failed Jobs App', () => { + let wrapper; + let resolverSpy; + + const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon); + const findJobsTable = () => wrapper.findComponent(FailedJobsTable); + + const createMockApolloProvider = (resolver) => { + const requestHandlers = [[GetFailedJobsQuery, resolver]]; + + return createMockApollo(requestHandlers); + }; + + const createComponent = (resolver, failedJobsSummaryData = mockFailedJobsSummaryData) => { + wrapper = shallowMount(FailedJobsApp, { + provide: { + fullPath: 'root/ci-project', + pipelineIid: 1, + }, + propsData: { + failedJobsSummary: failedJobsSummaryData, + }, + apolloProvider: createMockApolloProvider(resolver), + }); + }; + + beforeEach(() => { + resolverSpy = jest.fn().mockResolvedValue(mockFailedJobsQueryResponse); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('loading spinner', () => { + beforeEach(() => { + createComponent(resolverSpy); + }); + + it('displays loading spinner when fetching failed jobs', () => { + expect(findLoadingSpinner().exists()).toBe(true); + }); + + it('hides loading spinner after the failed jobs have been fetched', async () => { + await waitForPromises(); + + expect(findLoadingSpinner().exists()).toBe(false); + }); + }); + + it('displays the failed jobs table', async () => { + createComponent(resolverSpy); + + await waitForPromises(); + + expect(findJobsTable().exists()).toBe(true); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('handles query fetch error correctly', async () => { + resolverSpy = jest.fn().mockRejectedValue(new Error('GraphQL error')); + + createComponent(resolverSpy); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem fetching the failed jobs.', + }); + }); +}); diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js new file mode 100644 index 00000000000..b597a3bf4b0 --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js @@ -0,0 +1,117 @@ +import { GlButton, GlLink, GlTableLite } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import createFlash from '~/flash'; +import { redirectTo } from '~/lib/utils/url_utility'; +import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue'; +import RetryFailedJobMutation from '~/pipelines/graphql/mutations/retry_failed_job.mutation.graphql'; +import { + successRetryMutationResponse, + failedRetryMutationResponse, + mockPreparedFailedJobsData, + mockPreparedFailedJobsDataNoPermission, +} from '../../mock_data'; + +jest.mock('~/flash'); +jest.mock('~/lib/utils/url_utility'); + +Vue.use(VueApollo); + +describe('Failed Jobs Table', () => { + let wrapper; + + const successRetryMutationHandler = jest.fn().mockResolvedValue(successRetryMutationResponse); + const failedRetryMutationHandler = jest.fn().mockResolvedValue(failedRetryMutationResponse); + + const findJobsTable = () => wrapper.findComponent(GlTableLite); + const findRetryButton = () => wrapper.findComponent(GlButton); + const findJobLink = () => wrapper.findComponent(GlLink); + const findJobLog = () => wrapper.findByTestId('job-log'); + + const createMockApolloProvider = (resolver) => { + const requestHandlers = [[RetryFailedJobMutation, resolver]]; + return createMockApollo(requestHandlers); + }; + + const createComponent = (resolver, failedJobsData = mockPreparedFailedJobsData) => { + wrapper = mountExtended(FailedJobsTable, { + propsData: { + failedJobs: failedJobsData, + }, + apolloProvider: createMockApolloProvider(resolver), + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + it('displays the failed jobs table', () => { + createComponent(); + + expect(findJobsTable().exists()).toBe(true); + }); + + it('calls the retry failed job mutation correctly', () => { + createComponent(successRetryMutationHandler); + + findRetryButton().trigger('click'); + + expect(successRetryMutationHandler).toHaveBeenCalledWith({ + id: mockPreparedFailedJobsData[0].id, + }); + }); + + it('redirects to the new job after the mutation', async () => { + const { + data: { + jobRetry: { job }, + }, + } = successRetryMutationResponse; + + createComponent(successRetryMutationHandler); + + findRetryButton().trigger('click'); + + await waitForPromises(); + + expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); + }); + + it('shows error message if the retry failed job mutation fails', async () => { + createComponent(failedRetryMutationHandler); + + findRetryButton().trigger('click'); + + await waitForPromises(); + + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was a problem retrying the failed job.', + }); + }); + + it('hides the job log and retry button if a user does not have permission', () => { + createComponent([[]], mockPreparedFailedJobsDataNoPermission); + + expect(findJobLog().exists()).toBe(false); + expect(findRetryButton().exists()).toBe(false); + }); + + it('displays the job log and retry button if a user has permission', () => { + createComponent(); + + expect(findJobLog().exists()).toBe(true); + expect(findRetryButton().exists()).toBe(true); + }); + + it('job name links to the correct job', () => { + createComponent(); + + expect(findJobLink().attributes('href')).toBe( + mockPreparedFailedJobsData[0].detailedStatus.detailsPath, + ); + }); +}); diff --git a/spec/frontend/pipelines/components/jobs/utils_spec.js b/spec/frontend/pipelines/components/jobs/utils_spec.js new file mode 100644 index 00000000000..720446cfda3 --- /dev/null +++ b/spec/frontend/pipelines/components/jobs/utils_spec.js @@ -0,0 +1,14 @@ +import { prepareFailedJobs } from '~/pipelines/components/jobs/utils'; +import { + mockFailedJobsData, + mockFailedJobsSummaryData, + mockPreparedFailedJobsData, +} from '../../mock_data'; + +describe('Utils', () => { + it('prepares failed jobs data correctly', () => { + expect(prepareFailedJobs(mockFailedJobsData, mockFailedJobsSummaryData)).toEqual( + mockPreparedFailedJobsData, + ); + }); +}); diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js index 59d4e808b32..57d1511d859 100644 --- a/spec/frontend/pipelines/mock_data.js +++ b/spec/frontend/pipelines/mock_data.js @@ -1141,3 +1141,218 @@ export const mockPipelineBranch = () => { viewType: 'root', }; }; + +export const mockFailedJobsQueryResponse = { + data: { + project: { + __typename: 'Project', + id: 'gid://gitlab/Project/20', + pipeline: { + __typename: 'Pipeline', + id: 'gid://gitlab/Ci::Pipeline/300', + jobs: { + __typename: 'CiJobConnection', + nodes: [ + { + __typename: 'CiJob', + status: 'FAILED', + detailedStatus: { + __typename: 'DetailedStatus', + id: 'failed-1848-1848', + detailsPath: '/root/ci-project/-/jobs/1848', + group: 'failed', + icon: 'status_failed', + label: 'failed', + text: 'failed', + tooltip: 'failed - (script failure)', + action: { + __typename: 'StatusAction', + id: 'Ci::Build-failed-1848', + buttonTitle: 'Retry this job', + icon: 'retry', + method: 'post', + path: '/root/ci-project/-/jobs/1848/retry', + title: 'Retry', + }, + }, + id: 'gid://gitlab/Ci::Build/1848', + stage: { + __typename: 'CiStage', + id: 'gid://gitlab/Ci::Stage/358', + name: 'build', + }, + name: 'wait_job', + retryable: true, + userPermissions: { + __typename: 'JobPermissions', + readBuild: true, + updateBuild: true, + }, + }, + { + __typename: 'CiJob', + status: 'FAILED', + detailedStatus: { + __typename: 'DetailedStatus', + id: 'failed-1710-1710', + detailsPath: '/root/ci-project/-/jobs/1710', + group: 'failed', + icon: 'status_failed', + label: 'failed', + text: 'failed', + tooltip: 'failed - (script failure) (retried)', + action: null, + }, + id: 'gid://gitlab/Ci::Build/1710', + stage: { + __typename: 'CiStage', + id: 'gid://gitlab/Ci::Stage/358', + name: 'build', + }, + name: 'wait_job', + retryable: false, + userPermissions: { + __typename: 'JobPermissions', + readBuild: true, + updateBuild: true, + }, + }, + ], + }, + }, + }, + }, +}; + +export const mockFailedJobsSummaryData = [ + { + id: 1848, + failure: null, + failure_summary: + '<span>Pulling docker image node:latest ...<br/></span><span>Using docker image sha256:738d733448be00c72cb6618b7a06a1424806c6d239d8885e92f9b1e8727092b5 for node:latest with digest node@sha256:e5b7b349d517159246070bf14242027a9e220ffa8bd98a67ba1495d969c06c01 ...<br/></span><div class="section-start" data-timestamp="1651175313" data-section="prepare-script" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-prepare-script">Preparing environment</span><span class="section section-header js-s-prepare-script"><br/></span><span class="section line js-s-prepare-script">Running on runner-kvkqh24-project-20-concurrent-0 via 0706719b1b8d...<br/></span><div class="section-end" data-section="prepare-script"></div><div class="section-start" data-timestamp="1651175313" data-section="get-sources" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-get-sources">Getting source from Git repository</span><span class="section section-header js-s-get-sources"><br/></span><span class="term-fg-l-green term-bold section line js-s-get-sources">Fetching changes with git depth set to 50...</span><span class="section line js-s-get-sources"><br/>Reinitialized existing Git repository in /builds/root/ci-project/.git/<br/>fatal: couldn\'t find remote ref refs/heads/test<br/></span><div class="section-end" data-section="get-sources"></div><span class="term-fg-l-red term-bold">ERROR: Job failed: exit code 1<br/></span>', + }, +]; + +export const mockFailedJobsData = [ + { + normalizedId: 1848, + __typename: 'CiJob', + status: 'FAILED', + detailedStatus: { + __typename: 'DetailedStatus', + id: 'failed-1848-1848', + detailsPath: '/root/ci-project/-/jobs/1848', + group: 'failed', + icon: 'status_failed', + label: 'failed', + text: 'failed', + tooltip: 'failed - (script failure)', + action: { + __typename: 'StatusAction', + id: 'Ci::Build-failed-1848', + buttonTitle: 'Retry this job', + icon: 'retry', + method: 'post', + path: '/root/ci-project/-/jobs/1848/retry', + title: 'Retry', + }, + }, + id: 'gid://gitlab/Ci::Build/1848', + stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, + name: 'wait_job', + retryable: true, + userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, + }, + { + normalizedId: 1710, + __typename: 'CiJob', + status: 'FAILED', + detailedStatus: { + __typename: 'DetailedStatus', + id: 'failed-1710-1710', + detailsPath: '/root/ci-project/-/jobs/1710', + group: 'failed', + icon: 'status_failed', + label: 'failed', + text: 'failed', + tooltip: 'failed - (script failure) (retried)', + action: null, + }, + id: 'gid://gitlab/Ci::Build/1710', + stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, + name: 'wait_job', + retryable: false, + userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, + }, +]; + +export const mockPreparedFailedJobsData = [ + { + __typename: 'CiJob', + _showDetails: true, + detailedStatus: { + __typename: 'DetailedStatus', + action: { + __typename: 'StatusAction', + buttonTitle: 'Retry this job', + icon: 'retry', + id: 'Ci::Build-failed-1848', + method: 'post', + path: '/root/ci-project/-/jobs/1848/retry', + title: 'Retry', + }, + detailsPath: '/root/ci-project/-/jobs/1848', + group: 'failed', + icon: 'status_failed', + id: 'failed-1848-1848', + label: 'failed', + text: 'failed', + tooltip: 'failed - (script failure)', + }, + failure: null, + failureSummary: + '<span>Pulling docker image node:latest ...<br/></span><span>Using docker image sha256:738d733448be00c72cb6618b7a06a1424806c6d239d8885e92f9b1e8727092b5 for node:latest with digest node@sha256:e5b7b349d517159246070bf14242027a9e220ffa8bd98a67ba1495d969c06c01 ...<br/></span><div class="section-start" data-timestamp="1651175313" data-section="prepare-script" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-prepare-script">Preparing environment</span><span class="section section-header js-s-prepare-script"><br/></span><span class="section line js-s-prepare-script">Running on runner-kvkqh24-project-20-concurrent-0 via 0706719b1b8d...<br/></span><div class="section-end" data-section="prepare-script"></div><div class="section-start" data-timestamp="1651175313" data-section="get-sources" role="button"></div><span class="term-fg-l-cyan term-bold section section-header js-s-get-sources">Getting source from Git repository</span><span class="section section-header js-s-get-sources"><br/></span><span class="term-fg-l-green term-bold section line js-s-get-sources">Fetching changes with git depth set to 50...</span><span class="section line js-s-get-sources"><br/>Reinitialized existing Git repository in /builds/root/ci-project/.git/<br/>fatal: couldn\'t find remote ref refs/heads/test<br/></span><div class="section-end" data-section="get-sources"></div><span class="term-fg-l-red term-bold">ERROR: Job failed: exit code 1<br/></span>', + id: 'gid://gitlab/Ci::Build/1848', + name: 'wait_job', + normalizedId: 1848, + retryable: true, + stage: { __typename: 'CiStage', id: 'gid://gitlab/Ci::Stage/358', name: 'build' }, + status: 'FAILED', + userPermissions: { __typename: 'JobPermissions', readBuild: true, updateBuild: true }, + }, +]; + +export const mockPreparedFailedJobsDataNoPermission = [ + { + ...mockPreparedFailedJobsData[0], + userPermissions: { __typename: 'JobPermissions', readBuild: false, updateBuild: false }, + }, +]; + +export const successRetryMutationResponse = { + data: { + jobRetry: { + job: { + __typename: 'CiJob', + id: '"gid://gitlab/Ci::Build/1985"', + detailedStatus: { + detailsPath: '/root/project/-/jobs/1985', + id: 'pending-1985-1985', + __typename: 'DetailedStatus', + }, + }, + errors: [], + __typename: 'JobRetryPayload', + }, + }, +}; + +export const failedRetryMutationResponse = { + data: { + jobRetry: { + job: {}, + errors: ['New Error'], + __typename: 'JobRetryPayload', + }, + }, +}; diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb index 7697cd0ef79..b2fbfda898e 100644 --- a/spec/graphql/types/ci/runner_type_spec.rb +++ b/spec/graphql/types/ci/runner_type_spec.rb @@ -11,7 +11,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do expected_fields = %w[ id description created_at contacted_at maximum_timeout access_level active paused status version short_sha revision locked run_untagged ip_address runner_type tag_list - project_count job_count admin_url edit_admin_url user_permissions executor_name + project_count job_count admin_url edit_admin_url user_permissions executor_name architecture_name platform_name groups projects jobs token_expires_at ] diff --git a/spec/helpers/ci/builds_helper_spec.rb b/spec/helpers/ci/builds_helper_spec.rb index 143d96cf632..ea3b5aac4ea 100644 --- a/spec/helpers/ci/builds_helper_spec.rb +++ b/spec/helpers/ci/builds_helper_spec.rb @@ -97,6 +97,20 @@ RSpec.describe Ci::BuildsHelper do end end + describe '#prepare_failed_jobs_summary_data' do + let(:failed_build) { create(:ci_build, :failed, :trace_live) } + + subject { helper.prepare_failed_jobs_summary_data([failed_build]) } + + it 'returns array of failed jobs with id, failure and failure summary' do + expect(subject).to eq([{ + id: failed_build.id, + failure: failed_build.present.callout_failure_message, + failure_summary: helper.build_summary(failed_build) + }].to_json) + end + end + def assign_project build(:project).tap do |project| assign(:project, project) diff --git a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb index a111007a984..65d55f85a98 100644 --- a/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb +++ b/spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Gitlab::BackgroundMigration::ExtractProjectTopicsIntoSeparateTabl # Tagging records expect { tagging_1.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { tagging_2.reload }.to raise_error(ActiveRecord::RecordNotFound) - expect { other_tagging.reload }.not_to raise_error(ActiveRecord::RecordNotFound) + expect { other_tagging.reload }.not_to raise_error expect { tagging_3.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { tagging_4.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { tagging_5.reload }.to raise_error(ActiveRecord::RecordNotFound) diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb index c06d26d1441..d6280d3c28c 100644 --- a/spec/lib/gitlab/checks/branch_check_spec.rb +++ b/spec/lib/gitlab/checks/branch_check_spec.rb @@ -103,7 +103,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do it 'prevents force push' do expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true) - expect { subject.validate! }.to raise_error + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError) end end end @@ -126,7 +126,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do it 'prevents force push' do expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true) - expect { subject.validate! }.to raise_error + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError) end end @@ -141,7 +141,7 @@ RSpec.describe Gitlab::Checks::BranchCheck do it 'prevents force push' do expect(Gitlab::Checks::ForcePush).to receive(:force_push?).and_return(true) - expect { subject.validate! }.to raise_error + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::ForbiddenError) end end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 28fd0011673..fd80cf3b396 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -3180,15 +3180,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do context 'without proper permissions' do before do - allow(model).to receive(:execute).with(/CREATE EXTENSION IF NOT EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied') + allow(model).to receive(:execute) + .with(/CREATE EXTENSION IF NOT EXISTS #{extension}/) + .and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied') end - it 'raises the exception' do - expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/) - end - - it 'prints an error message' do - expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error + it 'raises an exception and prints an error message' do + expect { subject } + .to output(/user is not allowed/).to_stderr + .and raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/) end end end @@ -3206,15 +3206,15 @@ RSpec.describe Gitlab::Database::MigrationHelpers do context 'without proper permissions' do before do - allow(model).to receive(:execute).with(/DROP EXTENSION IF EXISTS #{extension}/).and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied') - end - - it 'raises the exception' do - expect { subject }.to raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/) + allow(model).to receive(:execute) + .with(/DROP EXTENSION IF EXISTS #{extension}/) + .and_raise(ActiveRecord::StatementInvalid, 'InsufficientPrivilege: permission denied') end - it 'prints an error message' do - expect { subject }.to output(/user is not allowed/).to_stderr.and raise_error + it 'raises an exception and prints an error message' do + expect { subject } + .to output(/user is not allowed/).to_stderr + .and raise_error(ActiveRecord::StatementInvalid, /InsufficientPrivilege/) end end end diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index 59b87c5d8e7..9ff395070ea 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -62,7 +62,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do end it 'does not raise a UserNotFoundError' do - expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError) + expect { receiver.execute }.not_to raise_error end end end @@ -71,7 +71,7 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do let(:original_recipient) { User.support_bot } it 'does not raise a UserNotFoundError' do - expect { receiver.execute }.not_to raise_error(Gitlab::Email::UserNotFoundError) + expect { receiver.execute }.not_to raise_error end end end diff --git a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb index 8bd873cf008..dfa18c27d5e 100644 --- a/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb +++ b/spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Gitlab::Email::Message::InProductMarketing::Base do let(:series) { 0 } it 'does not raise error' do - expect { subject }.not_to raise_error(ArgumentError) + expect { subject }.not_to raise_error end end end diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 4c47912e218..2c931a999f1 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -177,9 +177,9 @@ EOT expect(diff_two.diff).to include(Gitlab::EncodingHelper::UNICODE_REPLACEMENT_CHARACTER) expect(diff_three.diff).to include(Gitlab::EncodingHelper::UNICODE_REPLACEMENT_CHARACTER) - expect { Oj.dump(diff) }.not_to raise_error(EncodingError) - expect { Oj.dump(diff_two) }.not_to raise_error(EncodingError) - expect { Oj.dump(diff_three) }.not_to raise_error(EncodingError) + expect { Oj.dump(diff) }.not_to raise_error + expect { Oj.dump(diff_two) }.not_to raise_error + expect { Oj.dump(diff_three) }.not_to raise_error end context 'when the diff is binary' do diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index 8211806a809..0a186b07d19 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -103,7 +103,7 @@ RSpec.describe Gitlab::Popen do it 'raises error' do expect do @klass.new.popen(%w[foobar]) - end.to raise_error + end.to raise_error(Errno::ENOENT) end end end diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb index 51c62840819..623009e9a30 100644 --- a/spec/lib/gitlab/tracking/event_definition_spec.rb +++ b/spec/lib/gitlab/tracking/event_definition_spec.rb @@ -33,7 +33,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition do end it 'has all definitions valid' do - expect { described_class.definitions }.not_to raise_error(Gitlab::Tracking::InvalidEventError) + expect { described_class.definitions }.not_to raise_error end describe '#validate' do diff --git a/spec/migrations/finalize_project_namespaces_backfill_spec.rb b/spec/migrations/finalize_project_namespaces_backfill_spec.rb index 3d0b0ec13fe..f70f1612f9d 100644 --- a/spec/migrations/finalize_project_namespaces_backfill_spec.rb +++ b/spec/migrations/finalize_project_namespaces_backfill_spec.rb @@ -42,7 +42,7 @@ RSpec.describe FinalizeProjectNamespacesBackfill, :migration do context 'when project namespace backfilling migration finished successfully' do it 'does not raise exception' do - expect { migrate! }.not_to raise_error(/Expected batched background migration for the given configuration to be marked as 'finished'/) + expect { migrate! }.not_to raise_error end end diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index 8c0f07047e4..a0ede9fb0d9 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -494,7 +494,7 @@ RSpec.describe Clusters::Platforms::Kubernetes do end it 'does not raise kubeclient http error' do - expect { subject }.not_to raise_error(Kubeclient::HttpError) + expect { subject }.not_to raise_error end end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index 4f3b95e43cd..5468699f9dd 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -237,7 +237,7 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do end it 'does not raise the exception' do - expect { go! }.not_to raise_exception(ReactiveCaching::ExceededReactiveCacheLimit) + expect { go! }.not_to raise_exception end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 3cab2b67ec5..2769e94cef7 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -621,7 +621,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do expect(close_action.processed).to be_falsey # it encounters the StaleObjectError at first, but reloads the object and runs `build.play` - expect { subject }.not_to raise_error(ActiveRecord::StaleObjectError) + expect { subject }.not_to raise_error # Now the build should be processed. expect(close_action.reload.processed).to be_truthy diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index b7f75bfe8bf..7d4a50788f3 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -681,7 +681,7 @@ RSpec.describe Integration do integration.properties = { foo: 1, bar: 2 } - expect { integration.properties[:foo] = 3 }.to raise_error + expect { integration.properties[:foo] = 3 }.to raise_error(FrozenError) end end diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb index 6c86db1197f..a9ed811e77d 100644 --- a/spec/models/packages/package_spec.rb +++ b/spec/models/packages/package_spec.rb @@ -1182,7 +1182,7 @@ RSpec.describe Packages::Package, type: :model do it "plan_limits includes column #{plan_limit_name}" do expect { package.project.actual_limits.send(plan_limit_name) } - .not_to raise_error(NoMethodError) + .not_to raise_error end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 42fe9f03141..d315b62b23e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2030,7 +2030,7 @@ RSpec.describe Project, factory_default: :keep do it 'returns nil if the path detection throws an error' do expect(Rails.application.routes).to receive(:recognize_path).with(url) { raise ActionController::RoutingError, 'test' } - expect { subject }.not_to raise_error(ActionController::RoutingError) + expect { subject }.not_to raise_error expect(subject).to be_nil end end diff --git a/spec/models/protected_branch/push_access_level_spec.rb b/spec/models/protected_branch/push_access_level_spec.rb index 13d33b95b16..008ae6275f0 100644 --- a/spec/models/protected_branch/push_access_level_spec.rb +++ b/spec/models/protected_branch/push_access_level_spec.rb @@ -30,7 +30,7 @@ RSpec.describe ProtectedBranch::PushAccessLevel do it 'checks that a deploy key is enabled for the same project as the protected branch\'s' do level = build(:protected_branch_push_access_level, deploy_key: create(:deploy_key)) - expect { level.save! }.to raise_error + expect { level.save! }.to raise_error(ActiveRecord::RecordInvalid) expect(level.errors.full_messages).to contain_exactly('Deploy key is not enabled for this project') end end diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 125fec61d72..4ae1927dcca 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -53,7 +53,10 @@ RSpec.describe Release do context 'when a release is tied to a milestone for another project' do it 'creates a validation error' do milestone = build(:milestone, project: create(:project)) - expect { release.milestones << milestone }.to raise_error + + expect { release.milestones << milestone } + .to raise_error(ActiveRecord::RecordInvalid, + 'Validation failed: Release does not have the same project as the milestone') end end diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 6102e15012b..542876438cc 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -62,6 +62,8 @@ RSpec.describe 'Query.runner(id)' do 'ipAddress' => runner.ip_address, 'runnerType' => runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE', 'executorName' => runner.executor_type&.dasherize, + 'architectureName' => runner.architecture, + 'platformName' => runner.platform, 'jobCount' => 0, 'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything), 'projectCount' => nil, diff --git a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb index 5f6822223ca..4891e64aab8 100644 --- a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Delete a cluster agent' do 'or you don\'t have permission to perform this action'] it 'does not delete cluster agent' do - expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound) + expect { cluster_agent.reload }.not_to raise_error end end diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index c7f121c48ab..d69f542cd18 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -506,10 +506,12 @@ RSpec.describe 'getting merge request listings nested in a project' do end context 'when only the count is requested' do + let_it_be(:merged_at) { Time.new(2020, 1, 3) } + context 'when merged at filter is present' do let_it_be(:merge_request) do create(:merge_request, :unique_branches, source_project: project).tap do |mr| - mr.metrics.update!(merged_at: Time.new(2020, 1, 3)) + mr.metrics.update!(merged_at: merged_at, created_at: merged_at - 2.days) end end @@ -526,12 +528,18 @@ RSpec.describe 'getting merge request listings nested in a project' do it 'does not query the merge requests table for the count' do query_recorder = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - queries = query_recorder.data.each_value.first[:occurrences] + queries = query_recorder.log expect(queries).not_to include(match(/SELECT COUNT\(\*\) FROM "merge_requests"/)) expect(queries).to include(match(/SELECT COUNT\(\*\) FROM "merge_request_metrics"/)) end context 'when total_time_to_merge and count is queried' do + let_it_be(:merge_request_2) do + create(:merge_request, :unique_branches, source_project: project).tap do |mr| + mr.metrics.update!(merged_at: merged_at, created_at: merged_at - 1.day) + end + end + let(:query) do graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY) mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0) { @@ -541,11 +549,18 @@ RSpec.describe 'getting merge request listings nested in a project' do QUERY end - it 'does not query the merge requests table for the total_time_to_merge' do + it 'uses the merge_request_metrics table for total_time_to_merge' do query_recorder = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - queries = query_recorder.data.each_value.first[:occurrences] - expect(queries).to include(match(/SELECT.+SUM.+FROM "merge_request_metrics" WHERE/)) + expect(query_recorder.log).to include(match(/SELECT.+SUM.+FROM "merge_request_metrics" WHERE/)) + end + + it 'returns the correct total time to merge' do + post_graphql(query, current_user: current_user) + + sum = graphql_data_at(:project, :merge_requests, :total_time_to_merge) + + expect(sum).to eq(3.days.to_f) end end diff --git a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb index b48ea70aa4c..98b01e2b303 100644 --- a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb +++ b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb @@ -38,7 +38,7 @@ RSpec.describe ::Ci::PipelineArtifacts::CoverageReportService do context 'when pipeline artifact has already been created' do it 'do not raise an error and do not persist the same artifact twice' do - expect { 2.times { described_class.new.execute(pipeline) } }.not_to raise_error(ActiveRecord::RecordNotUnique) + expect { 2.times { described_class.new.execute(pipeline) } }.not_to raise_error expect(Ci::PipelineArtifact.count).to eq(1) end diff --git a/spec/services/clusters/agents/delete_service_spec.rb b/spec/services/clusters/agents/delete_service_spec.rb index 1d6bc9618dd..abe1bdaab27 100644 --- a/spec/services/clusters/agents/delete_service_spec.rb +++ b/spec/services/clusters/agents/delete_service_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Clusters::Agents::DeleteService do expect(response.status).to eq(:error) expect(response.message).to eq('You have insufficient permissions to delete this cluster agent') - expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound) + expect { cluster_agent.reload }.not_to raise_error end end diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index b4a25fd121d..30e48b3baf1 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -14,6 +14,8 @@ require_relative "helpers/fast_rails_root" require 'rubocop' require 'rubocop/rspec/support' +RSpec::Expectations.configuration.on_potential_false_positives = :raise + RSpec.configure do |config| config.mock_with :rspec do |mocks| mocks.verify_doubled_constant_names = true diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb index 0dc66eeb2ee..086cdf50e9d 100644 --- a/spec/support/shared_contexts/email_shared_context.rb +++ b/spec/support/shared_contexts/email_shared_context.rb @@ -148,7 +148,7 @@ RSpec.shared_examples :note_handler_shared_examples do |forwardable| end it 'allows email to only have quoted text', if: forwardable do - expect { receiver.execute }.not_to raise_error(Gitlab::Email::EmptyEmailError) + expect { receiver.execute }.not_to raise_error end end diff --git a/spec/support_specs/helpers/active_record/query_recorder_spec.rb b/spec/support_specs/helpers/active_record/query_recorder_spec.rb index f1af9ceffb9..d6c52b22449 100644 --- a/spec/support_specs/helpers/active_record/query_recorder_spec.rb +++ b/spec/support_specs/helpers/active_record/query_recorder_spec.rb @@ -78,12 +78,14 @@ RSpec.describe ActiveRecord::QueryRecorder do end describe 'detecting the right number of calls and their origin' do - it 'detects two separate queries' do - control = ActiveRecord::QueryRecorder.new query_recorder_debug: true do + let(:control) do + ActiveRecord::QueryRecorder.new query_recorder_debug: true do 2.times { TestQueries.count } TestQueries.first end + end + it 'detects two separate queries' do # Check #find_query expect(control.find_query(/.*/, 0).size) .to eq(control.data.keys.size) @@ -98,8 +100,8 @@ RSpec.describe ActiveRecord::QueryRecorder do expect(control.log.size).to eq(3) # Ensure memoization value match the raw value above expect(control.count).to eq(control.log.size) - # Ensure we have only two sources of queries - expect(control.data.keys.size).to eq(1) + # Ensure we have two sources of queries + expect(control.data.keys.size).to eq(2) end end end diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb index 7d28175d134..503e41eabc9 100644 --- a/spec/views/admin/application_settings/general.html.haml_spec.rb +++ b/spec/views/admin/application_settings/general.html.haml_spec.rb @@ -6,13 +6,16 @@ RSpec.describe 'admin/application_settings/general.html.haml' do let(:app_settings) { build(:application_setting) } let(:user) { create(:admin) } + before do + assign(:application_setting, app_settings) + allow(view).to receive(:current_user).and_return(user) + end + describe 'sourcegraph integration' do let(:sourcegraph_flag) { true } before do - assign(:application_setting, app_settings) allow(Gitlab::Sourcegraph).to receive(:feature_available?).and_return(sourcegraph_flag) - allow(view).to receive(:current_user).and_return(user) end context 'when sourcegraph feature is enabled' do @@ -35,11 +38,6 @@ RSpec.describe 'admin/application_settings/general.html.haml' do end describe 'prompt user about registration features' do - before do - assign(:application_setting, app_settings) - allow(view).to receive(:current_user).and_return(user) - end - context 'when service ping is enabled' do before do stub_application_setting(usage_ping_enabled: true) @@ -60,4 +58,14 @@ RSpec.describe 'admin/application_settings/general.html.haml' do it_behaves_like 'renders registration features prompt', :application_setting_disabled_repository_size_limit end end + + describe 'add license' do + before do + render + end + + it 'does not show the Add License section' do + expect(rendered).not_to have_css('#js-add-license-toggle') + end + end end diff --git a/spec/views/projects/settings/operations/show.html.haml_spec.rb b/spec/views/projects/settings/operations/show.html.haml_spec.rb index c0ec86a41a7..8853b34074a 100644 --- a/spec/views/projects/settings/operations/show.html.haml_spec.rb +++ b/spec/views/projects/settings/operations/show.html.haml_spec.rb @@ -52,30 +52,6 @@ RSpec.describe 'projects/settings/operations/show' do end end - describe 'Operations > Prometheus' do - context 'when settings_operations_prometheus_service flag is enabled' do - it 'renders the Operations Settings page' do - render - - expect(rendered).to have_content _('Prometheus') - expect(rendered).to have_content _('Link Prometheus monitoring to GitLab.') - expect(rendered).to have_content _('To use a Prometheus installed on a cluster, deactivate the manual configuration.') - end - end - - context 'when settings_operations_prometheus_service is disabled' do - before do - stub_feature_flags(settings_operations_prometheus_service: false) - end - - it 'renders the Operations Settings page' do - render - - expect(rendered).not_to have_content _('Auto configuration settings are used unless you override their values here.') - end - end - end - describe 'Operations > Tracing' do context 'Settings page ' do it 'renders the Tracing Settings page' do diff --git a/spec/workers/clusters/applications/activate_service_worker_spec.rb b/spec/workers/clusters/applications/activate_service_worker_spec.rb index 019bfe7a750..d13ff76613c 100644 --- a/spec/workers/clusters/applications/activate_service_worker_spec.rb +++ b/spec/workers/clusters/applications/activate_service_worker_spec.rb @@ -46,7 +46,7 @@ RSpec.describe Clusters::Applications::ActivateServiceWorker, '#perform' do context 'cluster does not exist' do it 'does not raise Record Not Found error' do - expect { described_class.new.perform(0, 'ignored in this context') }.not_to raise_error(ActiveRecord::RecordNotFound) + expect { described_class.new.perform(0, 'ignored in this context') }.not_to raise_error end end end diff --git a/spec/workers/delete_diff_files_worker_spec.rb b/spec/workers/delete_diff_files_worker_spec.rb index cf26dbabb97..c124847ca45 100644 --- a/spec/workers/delete_diff_files_worker_spec.rb +++ b/spec/workers/delete_diff_files_worker_spec.rb @@ -34,11 +34,13 @@ RSpec.describe DeleteDiffFilesWorker do end it 'rollsback if something goes wrong' do + error = RuntimeError.new('something went wrong') + expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all) - .and_raise + .and_raise(error) expect { described_class.new.perform(merge_request_diff.id) } - .to raise_error + .to raise_error(error) merge_request_diff.reload |