diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-21 06:10:15 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-21 06:10:15 +0300 |
commit | d63d9aebd74436a74f1ff45e26f6565341d5956b (patch) | |
tree | e4b4606da3edb0dd7b7f9d11a119f956890c5e81 | |
parent | 11bda5e7e1637e5b0ace597aafbd45f12b429c32 (diff) |
Add latest changes from gitlab-org/gitlab@master
27 files changed, 635 insertions, 309 deletions
@@ -253,7 +253,7 @@ gem 'rainbow', '~> 3.0' gem 'ruby-progressbar', '~> 1.10' # Linear-time regex library for untrusted regular expressions -gem 're2', '2.0.0' +gem 're2', '2.1.2' # Misc diff --git a/Gemfile.checksum b/Gemfile.checksum index e1ee62e12ec..702f6471230 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -491,16 +491,16 @@ {"name":"rbtree","version":"0.4.6","platform":"ruby","checksum":"14eea4469b24fd2472542e5f3eb105d6344c8ccf36f0b56d55fdcfeb4e0f10fc"}, {"name":"rchardet","version":"1.8.0","platform":"ruby","checksum":"693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7"}, {"name":"rdoc","version":"6.3.2","platform":"ruby","checksum":"def4a720235c27d56c176ae73555e647eb04ea58a8bbaa927f8f9f79de7805a6"}, -{"name":"re2","version":"2.0.0","platform":"aarch64-linux","checksum":"677ddce4c38d659de899651acbfd7c6b5331f984a7101d9179ac247284f2212a"}, -{"name":"re2","version":"2.0.0","platform":"arm-linux","checksum":"f657d689922e5ac215b486e4f2ca909f1079eab616269a1d8fc0cccd63ef28af"}, -{"name":"re2","version":"2.0.0","platform":"arm64-darwin","checksum":"ffc8e5663381ff344ee6a2e55c7d0be81ef9b43174a41e977c4e18a11f965be1"}, -{"name":"re2","version":"2.0.0","platform":"ruby","checksum":"09075fab88b7ab40c2374d75a20504408dc26539c11931b146d5f72892718925"}, -{"name":"re2","version":"2.0.0","platform":"x64-mingw-ucrt","checksum":"253b3de21ca563cdb93c9fd69738a2a66713e381bae4530ff2cae105c6fd1a8e"}, -{"name":"re2","version":"2.0.0","platform":"x64-mingw32","checksum":"d4b52fc21719f262c2a438912f009da868b31aed1688ec90e4e1696898fb53d3"}, -{"name":"re2","version":"2.0.0","platform":"x86-linux","checksum":"26abee219e3fd69ba5c6a7bdb882880b8af6502cf912da7a7837e38ad02a29e7"}, -{"name":"re2","version":"2.0.0","platform":"x86-mingw32","checksum":"ffec6da4c547e44a6c1a467b0b01b2dcc2940e081923221a2ac3e4b08a219c26"}, -{"name":"re2","version":"2.0.0","platform":"x86_64-darwin","checksum":"48b3ba3fea8cc84709a4195300cd1c627b2496f16e5865662c54e67f7aca1ccf"}, -{"name":"re2","version":"2.0.0","platform":"x86_64-linux","checksum":"1fb161e6e5d9efed59ed0062536f2cb9ab5fba367e209d0dc66f99f2864d42ff"}, +{"name":"re2","version":"2.1.2","platform":"aarch64-linux","checksum":"dbd87fb2432f17734cfb948d38cb0d138335228d31c4316719c75ac0a976731f"}, +{"name":"re2","version":"2.1.2","platform":"arm-linux","checksum":"4c0d903508bc0d82f27d09c84498e0fdc6ab87ef418bea884d711b85f7fca62f"}, +{"name":"re2","version":"2.1.2","platform":"arm64-darwin","checksum":"39bb8a44a4afbd2d3f2e07d531d223728f7c4b83946bc55e8ce6aae8c2c34579"}, +{"name":"re2","version":"2.1.2","platform":"ruby","checksum":"06bd25bf566dda720cbc607ceecb65ed16871427fbcb3e5239c300ec796fee9c"}, +{"name":"re2","version":"2.1.2","platform":"x64-mingw-ucrt","checksum":"a1ad9cda576dae6020664c7578d7e43d2062ca21e5e945aea125f539944ee713"}, +{"name":"re2","version":"2.1.2","platform":"x64-mingw32","checksum":"804fc9bafc6590e3e75d27d289546d7223b51bf3e46e9d81ee89cf5168c1a9be"}, +{"name":"re2","version":"2.1.2","platform":"x86-linux","checksum":"6a048f8a1511a5481f7a386045e67ecbb221a856d2987b33a231efb5e17250bc"}, +{"name":"re2","version":"2.1.2","platform":"x86-mingw32","checksum":"8e0e9d0f3166ff3000ffa38a05c9e5275ba431f2abce494fa7600033206ce108"}, +{"name":"re2","version":"2.1.2","platform":"x86_64-darwin","checksum":"6b41f328b551173e58eb04320c70295de143b5aeb38c78122aa623a4308bc472"}, +{"name":"re2","version":"2.1.2","platform":"x86_64-linux","checksum":"e082a1db722b7da3adc9e1f9d8681cba80a2d1176c54ae741443965ce277e6af"}, {"name":"recaptcha","version":"5.12.3","platform":"ruby","checksum":"37d1894add9e70a54d0c6c7f0ecbeedffbfa7d075acfbd4c509818dfdebdb7ee"}, {"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"}, {"name":"redcarpet","version":"3.6.0","platform":"ruby","checksum":"8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9"}, diff --git a/Gemfile.lock b/Gemfile.lock index 7285dff310c..8b812860e84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1296,7 +1296,7 @@ GEM rbtree (0.4.6) rchardet (1.8.0) rdoc (6.3.2) - re2 (2.0.0) + re2 (2.1.2) mini_portile2 (~> 2.8.4) recaptcha (5.12.3) json @@ -1963,7 +1963,7 @@ DEPENDENCIES rainbow (~> 3.0) rbtrace (~> 0.4) rdoc (~> 6.3.2) - re2 (= 2.0.0) + re2 (= 2.1.2) recaptcha (~> 5.12) redis (~> 4.8.0) redis-actionpack (~> 5.3.0) diff --git a/app/assets/javascripts/ci/common/pipelines_table.vue b/app/assets/javascripts/ci/common/pipelines_table.vue index 807128d2341..0c73c0c412f 100644 --- a/app/assets/javascripts/ci/common/pipelines_table.vue +++ b/app/assets/javascripts/ci/common/pipelines_table.vue @@ -8,9 +8,7 @@ import { TRACKING_CATEGORIES } from '~/ci/constants'; import { keepLatestDownstreamPipelines } from '~/ci/pipeline_details/utils/parsing_utils'; import LegacyPipelineMiniGraph from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph.vue'; import PipelineFailedJobsWidget from '~/ci/pipelines_page/components/failure_widget/pipeline_failed_jobs_widget.vue'; -import eventHub from '~/ci/event_hub'; import PipelineOperations from '../pipelines_page/components/pipeline_operations.vue'; -import PipelineStopModal from '../pipelines_page/components/pipeline_stop_modal.vue'; import PipelineTriggerer from '../pipelines_page/components/pipeline_triggerer.vue'; import PipelineUrl from '../pipelines_page/components/pipeline_url.vue'; import PipelinesStatusBadge from '../pipelines_page/components/pipelines_status_badge.vue'; @@ -19,6 +17,23 @@ const HIDE_TD_ON_MOBILE = 'gl-display-none! gl-lg-display-table-cell!'; const DEFAULT_TH_CLASSES = 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!'; +/** + * Pipelines Table + * + * Presentational component of a table of pipelines. This component does not + * fetch the list of pipelines and instead expects it as a prop. + * GraphQL actions for pipelines, such as retrying, canceling, etc. + * are handled within this component. + * + * Use this `legacy_pipelines_table_wrapper` if you need a fully functional REST component. + * + * IMPORTANT: When using this component, make sure to handle the following events: + * 1- @refresh-pipeline-table + * 2- @cancel-pipeline + * 3- @retry-pipeline + * + */ + export default { components: { GlTableLite, @@ -26,7 +41,6 @@ export default { PipelineFailedJobsWidget, PipelineOperations, PipelinesStatusBadge, - PipelineStopModal, PipelineTriggerer, PipelineUrl, }, @@ -63,14 +77,6 @@ export default { required: true, }, }, - data() { - return { - pipelineId: 0, - pipeline: {}, - endpoint: '', - cancelingPipeline: null, - }; - }, computed: { showFailedJobsWidget() { return this.glFeatures.ciJobFailuresInMr; @@ -131,17 +137,6 @@ export default { return this.pipelines; }, }, - watch: { - pipelines() { - this.cancelingPipeline = null; - }, - }, - created() { - eventHub.$on('openConfirmationModal', this.setModalData); - }, - beforeDestroy() { - eventHub.$off('openConfirmationModal', this.setModalData); - }, methods: { getDownstreamPipelines(pipeline) { const downstream = pipeline.triggered; @@ -153,14 +148,16 @@ export default { failedJobsCount(pipeline) { return pipeline?.failed_builds?.length || 0; }, - setModalData(data) { - this.pipelineId = data.pipeline.id; - this.pipeline = data.pipeline; - this.endpoint = data.endpoint; + onRefreshPipelinesTable() { + this.$emit('refresh-pipelines-table'); + }, + onRetryPipeline(pipeline) { + // This emit is only used by the `legacy_pipelines_table_wrapper`. + this.$emit('retry-pipeline', pipeline); }, - onSubmit() { - eventHub.$emit('postAction', this.endpoint); - this.cancelingPipeline = this.pipelineId; + onCancelPipeline(pipeline) { + // This emit is only used by the `legacy_pipelines_table_wrapper`. + this.$emit('cancel-pipeline', pipeline); }, trackPipelineMiniGraph() { this.track('click_minigraph', { label: TRACKING_CATEGORIES.table }); @@ -219,7 +216,12 @@ export default { </template> <template #cell(actions)="{ item }"> - <pipeline-operations :pipeline="item" :canceling-pipeline="cancelingPipeline" /> + <pipeline-operations + :pipeline="item" + @cancel-pipeline="onCancelPipeline" + @refresh-pipelines-table="onRefreshPipelinesTable" + @retry-pipeline="onRetryPipeline" + /> </template> <template #row-details="{ item }"> @@ -234,7 +236,5 @@ export default { /> </template> </gl-table-lite> - - <pipeline-stop-modal :pipeline="pipeline" @submit="onSubmit" /> </div> </template> diff --git a/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js b/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js index 53f755fda37..5d1f1ac770c 100644 --- a/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js +++ b/app/assets/javascripts/ci/pipeline_details/mixins/pipelines_mixin.js @@ -52,14 +52,12 @@ export default { }); eventHub.$on('postAction', this.postAction); - eventHub.$on('retryPipeline', this.postAction); eventHub.$on('clickedDropdown', this.updateTable); eventHub.$on('updateTable', this.updateTable); eventHub.$on('runMergeRequestPipeline', this.runMergeRequestPipeline); }, beforeDestroy() { eventHub.$off('postAction', this.postAction); - eventHub.$off('retryPipeline', this.postAction); eventHub.$off('clickedDropdown', this.updateTable); eventHub.$off('updateTable', this.updateTable); eventHub.$off('runMergeRequestPipeline', this.runMergeRequestPipeline); @@ -68,6 +66,15 @@ export default { this.poll.stop(); }, methods: { + onCancelPipeline(pipeline) { + this.postAction(pipeline.cancel_path); + }, + onRefreshPipelinesTable() { + this.updateTable(); + }, + onRetryPipeline(pipeline) { + this.postAction(pipeline.retry_path); + }, updateInternalState(parameters) { this.poll.stop(); diff --git a/app/assets/javascripts/ci/pipelines_page/components/nav_controls.vue b/app/assets/javascripts/ci/pipelines_page/components/nav_controls.vue index 235126fea0c..ddcc566af13 100644 --- a/app/assets/javascripts/ci/pipelines_page/components/nav_controls.vue +++ b/app/assets/javascripts/ci/pipelines_page/components/nav_controls.vue @@ -7,28 +7,25 @@ export default { GlButton, }, props: { - newPipelinePath: { + ciLintPath: { type: String, required: false, default: null, }, - - resetCachePath: { - type: String, + isResetCacheButtonLoading: { + type: Boolean, required: false, - default: null, + default: false, }, - - ciLintPath: { + newPipelinePath: { type: String, required: false, default: null, }, - - isResetCacheButtonLoading: { - type: Boolean, + resetCachePath: { + type: String, required: false, - default: false, + default: null, }, }, methods: { diff --git a/app/assets/javascripts/ci/pipelines_page/components/pipeline_operations.vue b/app/assets/javascripts/ci/pipelines_page/components/pipeline_operations.vue index b05bdae65c4..746d605d852 100644 --- a/app/assets/javascripts/ci/pipelines_page/components/pipeline_operations.vue +++ b/app/assets/javascripts/ci/pipelines_page/components/pipeline_operations.vue @@ -1,22 +1,22 @@ <script> -import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui'; +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import Tracking from '~/tracking'; import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, TRACKING_CATEGORIES } from '~/ci/constants'; -import eventHub from '../../event_hub'; import PipelineMultiActions from './pipeline_multi_actions.vue'; import PipelinesManualActions from './pipelines_manual_actions.vue'; +import PipelineStopModal from './pipeline_stop_modal.vue'; export default { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, directives: { GlTooltip: GlTooltipDirective, - GlModalDirective, }, components: { GlButton, PipelineMultiActions, PipelinesManualActions, + PipelineStopModal, }, mixins: [Tracking.mixin()], props: { @@ -24,15 +24,12 @@ export default { type: Object, required: true, }, - cancelingPipeline: { - type: Number, - required: false, - default: null, - }, }, data() { return { + isCanceling: false, isRetrying: false, + showConfirmationModal: false, }; }, computed: { @@ -41,27 +38,36 @@ export default { this.pipeline?.details?.has_manual_actions || this.pipeline?.details?.has_scheduled_actions ); }, - isCancelling() { - return this.cancelingPipeline === this.pipeline.id; - }, }, watch: { pipeline() { - this.isRetrying = false; + if (this.isCanceling || this.isRetrying) { + this.isCanceling = false; + this.isRetrying = false; + } }, }, methods: { + onCloseModal() { + this.showConfirmationModal = false; + }, + onConfirmCancelPipeline() { + this.isCanceling = true; + this.showConfirmationModal = false; + + this.$emit('cancel-pipeline', this.pipeline); + }, handleCancelClick() { + this.showConfirmationModal = true; + this.trackClick('click_cancel_button'); - eventHub.$emit('openConfirmationModal', { - pipeline: this.pipeline, - endpoint: this.pipeline.cancel_path, - }); }, handleRetryClick() { this.isRetrying = true; + this.trackClick('click_retry_button'); - eventHub.$emit('retryPipeline', this.pipeline.retry_path); + + this.$emit('retry-pipeline', this.pipeline); }, trackClick(action) { this.track(action, { label: TRACKING_CATEGORIES.table }); @@ -72,8 +78,19 @@ export default { <template> <div class="gl-text-right"> + <pipeline-stop-modal + :pipeline="pipeline" + :show-confirmation-modal="showConfirmationModal" + @submit="onConfirmCancelPipeline" + @close-modal="onCloseModal" + /> + <div class="btn-group"> - <pipelines-manual-actions v-if="hasActions" :iid="pipeline.iid" /> + <pipelines-manual-actions + v-if="hasActions" + :iid="pipeline.iid" + @refresh-pipeline-table="$emit('refresh-pipelines-table')" + /> <gl-button v-if="pipeline.flags.retryable" @@ -94,11 +111,10 @@ export default { <gl-button v-if="pipeline.flags.cancelable" v-gl-tooltip.hover - v-gl-modal-directive="'confirmation-modal'" :aria-label="$options.BUTTON_TOOLTIP_CANCEL" :title="$options.BUTTON_TOOLTIP_CANCEL" - :loading="isCancelling" - :disabled="isCancelling" + :loading="isCanceling" + :disabled="isCanceling" icon="cancel" variant="danger" category="primary" diff --git a/app/assets/javascripts/ci/pipelines_page/components/pipeline_stop_modal.vue b/app/assets/javascripts/ci/pipelines_page/components/pipeline_stop_modal.vue index 9f38be668f2..d62a68f0dcc 100644 --- a/app/assets/javascripts/ci/pipelines_page/components/pipeline_stop_modal.vue +++ b/app/assets/javascripts/ci/pipelines_page/components/pipeline_stop_modal.vue @@ -7,7 +7,7 @@ import CiIcon from '~/vue_shared/components/ci_icon.vue'; /** * Pipeline Stop Modal. * - * Renders the modal used to confirm stopping a pipeline. + * Renders the modal used to confirm cancelling a pipeline. */ export default { components: { @@ -22,8 +22,15 @@ export default { required: true, deep: true, }, + showConfirmationModal: { + type: Boolean, + required: true, + }, }, computed: { + hasRef() { + return !isEmpty(this.pipeline.ref); + }, modalTitle() { return sprintf( s__('Pipeline|Stop pipeline #%{pipelineId}?'), @@ -34,10 +41,7 @@ export default { ); }, modalText() { - return s__(`Pipeline|You’re about to stop pipeline #%{pipelineId}.`); - }, - hasRef() { - return !isEmpty(this.pipeline.ref); + return s__(`Pipeline|You're about to stop pipeline #%{pipelineId}.`); }, primaryProps() { return { @@ -45,10 +49,13 @@ export default { attributes: { variant: 'danger' }, }; }, - cancelProps() { - return { - text: __('Cancel'), - }; + showModal: { + get() { + return this.showConfirmationModal; + }, + set() { + this.$emit('close-modal'); + }, }, }, methods: { @@ -56,14 +63,16 @@ export default { this.$emit('submit', event); }, }, + cancelProps: { text: __('Cancel') }, }; </script> <template> <gl-modal + v-model="showModal" modal-id="confirmation-modal" :title="modalTitle" :action-primary="primaryProps" - :action-cancel="cancelProps" + :action-cancel="$options.cancelProps" @primary="emitSubmit($event)" > <p> @@ -74,7 +83,7 @@ export default { </gl-sprintf> </p> - <p v-if="pipeline"> + <p> <ci-icon v-if="pipeline.details" :status="pipeline.details.status" diff --git a/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue b/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue index 4dacd474bde..ebf1744aee2 100644 --- a/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue +++ b/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue @@ -6,7 +6,6 @@ import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_m import { s__, __, sprintf } from '~/locale'; import Tracking from '~/tracking'; import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import eventHub from '../../event_hub'; import { TRACKING_CATEGORIES } from '../../constants'; import getPipelineActionsQuery from '../graphql/queries/get_pipeline_actions.query.graphql'; @@ -94,7 +93,7 @@ export default { .post(`${action.playPath}.json`) .then(() => { this.isLoading = false; - eventHub.$emit('updateTable'); + this.$emit('refresh-pipeline-table'); }) .catch(() => { this.isLoading = false; diff --git a/app/assets/javascripts/ci/pipelines_page/pipelines.vue b/app/assets/javascripts/ci/pipelines_page/pipelines.vue index 87ee5463bb0..f4105040f31 100644 --- a/app/assets/javascripts/ci/pipelines_page/pipelines.vue +++ b/app/assets/javascripts/ci/pipelines_page/pipelines.vue @@ -13,7 +13,7 @@ import { RAW_TEXT_WARNING, TRACKING_CATEGORIES, } from '~/ci/constants'; -import PipelinesTableComponent from '~/ci/common/pipelines_table.vue'; +import PipelinesTable from '~/ci/common/pipelines_table.vue'; import PipelinesMixin from '~/ci/pipeline_details/mixins/pipelines_mixin'; import { validateParams } from '~/ci/pipeline_details/utils'; import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue'; @@ -37,7 +37,7 @@ export default { NavigationTabs, NavigationControls, PipelinesFilteredSearch, - PipelinesTableComponent, + PipelinesTable, TablePagination, }, mixins: [PipelinesMixin, Tracking.mixin()], @@ -431,12 +431,15 @@ export default { /> <div v-else-if="stateToRender === $options.stateMap.tableList"> - <pipelines-table-component + <pipelines-table :pipelines="state.pipelines" :pipeline-schedule-url="pipelineScheduleUrl" :update-graph-dropdown="updateGraphDropdown" :view-type="viewType" :pipeline-key-option="selectedPipelineKeyOption" + @cancel-pipeline="onCancelPipeline" + @refresh-pipelines-table="onRefreshPipelinesTable" + @retry-pipeline="onRetryPipeline" /> </div> diff --git a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue index 5e84dcbe48e..5586032233c 100644 --- a/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue +++ b/app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue @@ -2,7 +2,7 @@ import { GlButton, GlEmptyState, GlLoadingIcon, GlModal, GlLink, GlSprintf } from '@gitlab/ui'; import { helpPagePath } from '~/helpers/help_page_helper'; import { getParameterByName } from '~/lib/utils/url_utility'; -import PipelinesTableComponent from '~/ci/common/pipelines_table.vue'; +import PipelinesTable from '~/ci/common/pipelines_table.vue'; import { PipelineKeyOptions } from '~/ci/constants'; import eventHub from '~/ci/event_hub'; import PipelinesMixin from '~/ci/pipeline_details/mixins/pipelines_mixin'; @@ -21,7 +21,7 @@ export default { GlLoadingIcon, GlModal, GlSprintf, - PipelinesTableComponent, + PipelinesTable, TablePagination, }, mixins: [PipelinesMixin, glFeatureFlagMixin()], @@ -279,11 +279,14 @@ export default { {{ $options.i18n.runPipelineText }} </gl-button> - <pipelines-table-component + <pipelines-table :pipelines="state.pipelines" :update-graph-dropdown="updateGraphDropdown" :view-type="viewType" :pipeline-key-option="$options.PipelineKeyOptions[0]" + @cancel-pipeline="onCancelPipeline" + @refresh-pipelines-table="onRefreshPipelinesTable" + @retry-pipeline="onRetryPipeline" > <template #table-header-actions> <div v-if="canRenderPipelineButton" class="gl-text-right"> @@ -296,7 +299,7 @@ export default { </gl-button> </div> </template> - </pipelines-table-component> + </pipelines-table> </div> <gl-modal diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js index beeb9b9ada4..6ca59f634a2 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js +++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js @@ -11,7 +11,7 @@ const apolloProvider = new VueApollo({ /** * Used in: - * - Project Pipelines List (projects:pipelines:index) + * - Project Pipelines List (projects:pipelines) * - Commit details View > Pipelines Tab > Pipelines Table (projects:commit:pipelines) * - Merge request details View > Pipelines Tab > Pipelines Table (projects:merge_requests:show) * - New merge request View > Pipelines Tab > Pipelines Table (projects:merge_requests:creations:new) diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 1bc67522e82..03518d5fdd1 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -93,7 +93,7 @@ function mountPipelines() { const { mrWidgetData } = gl; const table = new Vue({ components: { - CommitPipelinesTable: () => { + MergeRequestPipelinesTable: () => { return gon.features.mrPipelinesGraphql ? import('~/ci/merge_requests/components/pipelines_table_wrapper.vue') : import('~/commit/pipelines/legacy_pipelines_table_wrapper.vue'); @@ -112,7 +112,7 @@ function mountPipelines() { withFailedJobsDetails: true, }, render(createElement) { - return createElement('commit-pipelines-table', { + return createElement('merge-request-pipelines-table', { props: { endpoint: pipelineTableViewEl.dataset.endpoint, emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath, @@ -347,11 +347,11 @@ export default class MergeRequestTabs { } // this.hideSidebar(); this.resetViewContainer(); - this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable); + this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable); } else if (action === 'new') { this.expandView(); this.resetViewContainer(); - this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable); + this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable); } else if (this.isDiffAction(action)) { if (!isInVueNoteablePage()) { /* @@ -366,7 +366,7 @@ export default class MergeRequestTabs { } // this.hideSidebar(); this.expandViewContainer(); - this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable); + this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable); this.commitsTab.classList.remove('active'); } else if (action === 'pipelines') { // this.hideSidebar(); @@ -384,7 +384,7 @@ export default class MergeRequestTabs { // this.showSidebar(); this.resetViewContainer(); - this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable); + this.mergeRequestPipelinesTable = destroyPipelines(this.mergeRequestPipelinesTable); } renderGFM(document.querySelector('.detail-page-description')); @@ -522,7 +522,7 @@ export default class MergeRequestTabs { } mountPipelinesView() { - this.commitPipelinesTable = mountPipelines(); + this.mergeRequestPipelinesTable = mountPipelines(); } // load the diff tab content from the backend diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index a90a16e120c..bdb2744be71 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -78,10 +78,6 @@ module MergeRequestsHelper .execute(include_routes: true) end - def merge_request_button_visibility(merge_request, closed) - return 'hidden' if merge_request_button_hidden?(merge_request, closed) - end - def merge_request_button_hidden?(merge_request, closed) merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_or_merged_without_fork? end diff --git a/app/models/analytics/cycle_analytics/issue_stage_event.rb b/app/models/analytics/cycle_analytics/issue_stage_event.rb index 837eb35c839..4930788c8b5 100644 --- a/app/models/analytics/cycle_analytics/issue_stage_event.rb +++ b/app/models/analytics/cycle_analytics/issue_stage_event.rb @@ -17,12 +17,42 @@ module Analytics where(condition.arel.exists) end - def self.issuable_id_column - :issue_id - end + class << self + def project_column + :project_id + end + + def issuable_id_column + :issue_id + end + + def issuable_model + ::Issue + end + + def select_columns + [ + *super, + issuable_model.arel_table[:weight], + issuable_model.arel_table[:sprint_id] + ] + end + + def column_list + [ + *super, + :weight, + :sprint_id + ] + end - def self.issuable_model - ::Issue + def insert_column_list + [ + *super, + :weight, + :sprint_id + ] + end end end end diff --git a/app/models/analytics/cycle_analytics/merge_request_stage_event.rb b/app/models/analytics/cycle_analytics/merge_request_stage_event.rb index 0dfa322b2c3..7f85d284034 100644 --- a/app/models/analytics/cycle_analytics/merge_request_stage_event.rb +++ b/app/models/analytics/cycle_analytics/merge_request_stage_event.rb @@ -17,6 +17,10 @@ module Analytics where(condition.arel.exists) end + def self.project_column + :target_project_id + end + def self.issuable_id_column :merge_request_id end diff --git a/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb b/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb index d268c32c088..8f20e3880b3 100644 --- a/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb +++ b/app/models/concerns/analytics/cycle_analytics/stage_event_model.rb @@ -57,45 +57,19 @@ module Analytics class_methods do def upsert_data(data) - upsert_values = data.map do |row| - row.values_at( - :stage_event_hash_id, - :issuable_id, - :group_id, - :project_id, - :milestone_id, - :author_id, - :state_id, - :start_event_timestamp, - :end_event_timestamp - ) - end + upsert_values = data.map { |row| row.values_at(*column_list) } value_list = Arel::Nodes::ValuesList.new(upsert_values).to_sql query = <<~SQL INSERT INTO #{quoted_table_name} ( - stage_event_hash_id, - #{connection.quote_column_name(issuable_id_column)}, - group_id, - project_id, - milestone_id, - author_id, - state_id, - start_event_timestamp, - end_event_timestamp + #{insert_column_list.join(",\n")} ) #{value_list} ON CONFLICT(stage_event_hash_id, #{issuable_id_column}) DO UPDATE SET - group_id = excluded.group_id, - project_id = excluded.project_id, - milestone_id = excluded.milestone_id, - author_id = excluded.author_id, - state_id = excluded.state_id, - start_event_timestamp = excluded.start_event_timestamp, - end_event_timestamp = excluded.end_event_timestamp + #{column_updates.join(",\n")} SQL result = connection.execute(query) @@ -113,6 +87,51 @@ module Analytics def arel_order(arel_node, direction) direction.to_sym == :desc ? arel_node.desc : arel_node.asc end + + def select_columns + [ + issuable_model.arel_table[:id], + issuable_model.arel_table[project_column].as('project_id'), + issuable_model.arel_table[:milestone_id], + issuable_model.arel_table[:author_id], + issuable_model.arel_table[:state_id], + Project.arel_table[:parent_id].as('group_id') + ] + end + + def column_list + [ + :stage_event_hash_id, + :issuable_id, + :group_id, + :project_id, + :milestone_id, + :author_id, + :state_id, + :start_event_timestamp, + :end_event_timestamp + ] + end + + def insert_column_list + [ + :stage_event_hash_id, + connection.quote_column_name(issuable_id_column), + :group_id, + :project_id, + :milestone_id, + :author_id, + :state_id, + :start_event_timestamp, + :end_event_timestamp + ] + end + + def column_updates + insert_column_list.map do |column| + "#{column} = excluded.#{column}" + end + end end end end diff --git a/app/views/admin/application_settings/_sentry.html.haml b/app/views/admin/application_settings/_sentry.html.haml index 9f2a40e4e54..7058a4b5cca 100644 --- a/app/views/admin/application_settings/_sentry.html.haml +++ b/app/views/admin/application_settings/_sentry.html.haml @@ -1,28 +1,31 @@ = gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-sentry-settings'), html: { class: 'fieldset-form', id: 'sentry-settings' } do |f| = form_errors(@application_setting) - %span.text-muted - = _('Changing any setting here requires an application restart') + %fieldset.gl-text-secondary + = safe_format(s_('AdminSettings|GitLab uses the %{bold_start}Rails%{bold_end} and %{bold_start}Browser JavaScript%{bold_end} Sentry SDKs to send events to Sentry. For changes to Rails integration settings to take effect, restart GitLab.'), tag_pair(tag.b, :bold_start, :bold_end)) %fieldset .form-group - = f.gitlab_ui_checkbox_component :sentry_enabled, _('Enable Sentry error tracking') + = f.gitlab_ui_checkbox_component :sentry_enabled, s_('AdminSettings|Enable Sentry for Rails and Browser JavaScript') + .form-group + = f.label :sentry_environment, _('Environment'), class: 'label-light' + = f.text_field :sentry_environment, class: 'form-control gl-form-input', placeholder: Rails.env + .form-text.text-muted + = safe_format(s_('AdminSettings|%{setting_name} value used by both Rails and Browser JavaScript SDKs.'), setting_name: content_tag(:code, 'environment')) .form-group = f.label :sentry_dsn, _('DSN'), class: 'label-light' = f.text_field :sentry_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/1' + .form-text.text-muted + = safe_format(s_('AdminSettings|%{setting_name} value used by the Rails SDK.'), setting_name: content_tag(:code, 'dsn')) .form-group = f.label :sentry_clientside_dsn, _('Clientside DSN'), class: 'label-light' = f.text_field :sentry_clientside_dsn, class: 'form-control gl-form-input', placeholder: 'https://public@sentry.example.com/2' - .form-group - = f.label :sentry_environment, _('Environment'), class: 'label-light' - = f.text_field :sentry_environment, class: 'form-control gl-form-input', placeholder: Rails.env - - %p.text-muted - = _("Changing any setting bellow doesn't require an application restart") - - %fieldset + .form-text.text-muted + = safe_format(s_('AdminSettings|%{setting_name} value used by the Browser JavaScript SDK.'), setting_name: content_tag(:code, 'dsn')) .form-group = f.label :sentry_clientside_traces_sample_rate, _('Clientside traces sample rate'), class: 'label-light' = f.number_field :sentry_clientside_traces_sample_rate, class: 'form-control gl-form-input', placeholder: '0.5', min: 0, max: 1, step: 0.001 + .form-text.text-muted + = safe_format(s_('AdminSettings|%{setting_name} value used by the Browser JavaScript SDK.'), setting_name: content_tag(:code, 'tracesSampleRate')) = f.submit _('Save changes'), pajamas_button: true diff --git a/db/migrate/20230918123357_add_sprint_id_and_weight_to_vsa_issues.rb b/db/migrate/20230918123357_add_sprint_id_and_weight_to_vsa_issues.rb new file mode 100644 index 00000000000..0c3b7b3086a --- /dev/null +++ b/db/migrate/20230918123357_add_sprint_id_and_weight_to_vsa_issues.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddSprintIdAndWeightToVsaIssues < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def up + add_column :analytics_cycle_analytics_issue_stage_events, :weight, :integer + add_column :analytics_cycle_analytics_issue_stage_events, :sprint_id, :bigint + end + + def down + remove_column :analytics_cycle_analytics_issue_stage_events, :sprint_id + remove_column :analytics_cycle_analytics_issue_stage_events, :weight + end +end diff --git a/db/schema_migrations/20230918123357 b/db/schema_migrations/20230918123357 new file mode 100644 index 00000000000..5fc1d62a963 --- /dev/null +++ b/db/schema_migrations/20230918123357 @@ -0,0 +1 @@ +99e55170557dcda361f441d1333f4dc9d99133a469f1d17805478f3407d2a093
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index bd055a1ba70..e4f04117e69 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -720,7 +720,9 @@ CREATE TABLE analytics_cycle_analytics_issue_stage_events ( author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ) PARTITION BY HASH (stage_event_hash_id); @@ -733,7 +735,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00 FOR VALUES WITH (modulus 32, remainder 0); @@ -746,7 +750,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01 FOR VALUES WITH (modulus 32, remainder 1); @@ -759,7 +765,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_02 FOR VALUES WITH (modulus 32, remainder 2); @@ -772,7 +780,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_03 FOR VALUES WITH (modulus 32, remainder 3); @@ -785,7 +795,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_04 FOR VALUES WITH (modulus 32, remainder 4); @@ -798,7 +810,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_05 FOR VALUES WITH (modulus 32, remainder 5); @@ -811,7 +825,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_06 FOR VALUES WITH (modulus 32, remainder 6); @@ -824,7 +840,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_07 FOR VALUES WITH (modulus 32, remainder 7); @@ -837,7 +855,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_08 FOR VALUES WITH (modulus 32, remainder 8); @@ -850,7 +870,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_09 FOR VALUES WITH (modulus 32, remainder 9); @@ -863,7 +885,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_10 FOR VALUES WITH (modulus 32, remainder 10); @@ -876,7 +900,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_11 FOR VALUES WITH (modulus 32, remainder 11); @@ -889,7 +915,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_12 FOR VALUES WITH (modulus 32, remainder 12); @@ -902,7 +930,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_13 FOR VALUES WITH (modulus 32, remainder 13); @@ -915,7 +945,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_14 FOR VALUES WITH (modulus 32, remainder 14); @@ -928,7 +960,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_15 FOR VALUES WITH (modulus 32, remainder 15); @@ -941,7 +975,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_16 FOR VALUES WITH (modulus 32, remainder 16); @@ -954,7 +990,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_17 FOR VALUES WITH (modulus 32, remainder 17); @@ -967,7 +1005,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_18 FOR VALUES WITH (modulus 32, remainder 18); @@ -980,7 +1020,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_19 FOR VALUES WITH (modulus 32, remainder 19); @@ -993,7 +1035,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_20 FOR VALUES WITH (modulus 32, remainder 20); @@ -1006,7 +1050,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_21 FOR VALUES WITH (modulus 32, remainder 21); @@ -1019,7 +1065,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_22 FOR VALUES WITH (modulus 32, remainder 22); @@ -1032,7 +1080,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_23 FOR VALUES WITH (modulus 32, remainder 23); @@ -1045,7 +1095,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_24 FOR VALUES WITH (modulus 32, remainder 24); @@ -1058,7 +1110,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_25 FOR VALUES WITH (modulus 32, remainder 25); @@ -1071,7 +1125,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_26 FOR VALUES WITH (modulus 32, remainder 26); @@ -1084,7 +1140,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_27 FOR VALUES WITH (modulus 32, remainder 27); @@ -1097,7 +1155,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_28 FOR VALUES WITH (modulus 32, remainder 28); @@ -1110,7 +1170,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_29 FOR VALUES WITH (modulus 32, remainder 29); @@ -1123,7 +1185,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_30 FOR VALUES WITH (modulus 32, remainder 30); @@ -1136,7 +1200,9 @@ CREATE TABLE gitlab_partitions_static.analytics_cycle_analytics_issue_stage_even author_id bigint, start_event_timestamp timestamp with time zone NOT NULL, end_event_timestamp timestamp with time zone, - state_id smallint DEFAULT 1 NOT NULL + state_id smallint DEFAULT 1 NOT NULL, + weight integer, + sprint_id bigint ); ALTER TABLE ONLY analytics_cycle_analytics_issue_stage_events ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_31 FOR VALUES WITH (modulus 32, remainder 31); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2bf98cdc780..5d568647f4d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3375,6 +3375,15 @@ msgstr "" msgid "AdminProjects|Delete Project %{projectName}?" msgstr "" +msgid "AdminSettings|%{setting_name} value used by both Rails and Browser JavaScript SDKs." +msgstr "" + +msgid "AdminSettings|%{setting_name} value used by the Browser JavaScript SDK." +msgstr "" + +msgid "AdminSettings|%{setting_name} value used by the Rails SDK." +msgstr "" + msgid "AdminSettings|%{strongStart}WARNING:%{strongEnd} Environment variable %{environment_variable} does not exist or is not pointing to a valid directory. %{icon_link}" msgstr "" @@ -3450,6 +3459,9 @@ msgstr "" msgid "AdminSettings|Enable Registration Features" msgstr "" +msgid "AdminSettings|Enable Sentry for Rails and Browser JavaScript" +msgstr "" + msgid "AdminSettings|Enable Service Ping" msgstr "" @@ -3492,6 +3504,9 @@ msgstr "" msgid "AdminSettings|Git abuse rate limit" msgstr "" +msgid "AdminSettings|GitLab uses the %{bold_start}Rails%{bold_end} and %{bold_start}Browser JavaScript%{bold_end} Sentry SDKs to send events to Sentry. For changes to Rails integration settings to take effect, restart GitLab." +msgstr "" + msgid "AdminSettings|Group runners expiration" msgstr "" @@ -9610,12 +9625,6 @@ msgstr "" msgid "Changes:" msgstr "" -msgid "Changing any setting bellow doesn't require an application restart" -msgstr "" - -msgid "Changing any setting here requires an application restart" -msgstr "" - msgid "Characters left" msgstr "" @@ -17959,9 +17968,6 @@ msgstr "" msgid "Enable SSL verification" msgstr "" -msgid "Enable Sentry error tracking" -msgstr "" - msgid "Enable Snowplow tracking" msgstr "" @@ -34963,7 +34969,7 @@ msgstr "" msgid "Pipeline|We are currently unable to fetch pipeline data" msgstr "" -msgid "Pipeline|You’re about to stop pipeline #%{pipelineId}." +msgid "Pipeline|You're about to stop pipeline #%{pipelineId}." msgstr "" msgid "Pipeline|for" diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index cfd6bbf3094..e4e1772f08e 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Database schema', feature_category: :database do approvers: %w[target_id user_id], analytics_cycle_analytics_aggregations: %w[last_full_issues_id last_full_merge_requests_id last_incremental_issues_id last_full_run_issues_id last_full_run_merge_requests_id last_incremental_merge_requests_id last_consistency_check_issues_stage_event_hash_id last_consistency_check_issues_issuable_id last_consistency_check_merge_requests_stage_event_hash_id last_consistency_check_merge_requests_issuable_id], analytics_cycle_analytics_merge_request_stage_events: %w[author_id group_id merge_request_id milestone_id project_id stage_event_hash_id state_id], - analytics_cycle_analytics_issue_stage_events: %w[author_id group_id issue_id milestone_id project_id stage_event_hash_id state_id], + analytics_cycle_analytics_issue_stage_events: %w[author_id group_id issue_id milestone_id project_id stage_event_hash_id state_id sprint_id], audit_events: %w[author_id entity_id target_id], award_emoji: %w[awardable_id user_id], aws_roles: %w[role_external_id], diff --git a/spec/frontend/ci/common/pipelines_table_spec.js b/spec/frontend/ci/common/pipelines_table_spec.js index 26dd1a2fcc5..1ddeb901e59 100644 --- a/spec/frontend/ci/common/pipelines_table_spec.js +++ b/spec/frontend/ci/common/pipelines_table_spec.js @@ -1,9 +1,7 @@ -import '~/commons'; import { GlTableLite } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; import fixture from 'test_fixtures/pipelines/pipelines.json'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; import LegacyPipelineMiniGraph from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph.vue'; import PipelineFailedJobsWidget from '~/ci/pipelines_page/components/failure_widget/pipeline_failed_jobs_widget.vue'; import PipelineOperations from '~/ci/pipelines_page/components/pipeline_operations.vue'; @@ -20,14 +18,12 @@ import { import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; -jest.mock('~/ci/event_hub'); - describe('Pipelines Table', () => { - let pipeline; let wrapper; let trackingSpy; const defaultProvide = { + fullPath: '/my-project/', glFeatures: {}, withFailedJobsDetails: false, }; @@ -39,32 +35,31 @@ describe('Pipelines Table', () => { withFailedJobsDetails: true, }; + const { pipelines } = fixture; + const defaultProps = { - pipelines: [], + pipelines, viewType: 'root', pipelineKeyOption: PipelineKeyOptions[0], }; - const createMockPipeline = () => { - // Clone fixture as it could be modified by tests - const { pipelines } = JSON.parse(JSON.stringify(fixture)); - return pipelines.find((p) => p.user !== null && p.commit !== null); - }; - - const createComponent = (props = {}, provide = {}) => { - wrapper = extendedWrapper( - mount(PipelinesTable, { - propsData: { - ...defaultProps, - ...props, - }, - provide: { - ...defaultProvide, - ...provide, - }, - stubs: ['PipelineFailedJobsWidget'], - }), - ); + const [firstPipeline] = pipelines; + + const createComponent = ({ props = {}, provide = {}, stubs = {} } = {}) => { + wrapper = mountExtended(PipelinesTable, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + ...defaultProvide, + ...provide, + }, + stubs: { + PipelineOperations: true, + ...stubs, + }, + }); }; const findGlTableLite = () => wrapper.findComponent(GlTableLite); @@ -84,13 +79,9 @@ describe('Pipelines Table', () => { const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button'); const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button'); - beforeEach(() => { - pipeline = createMockPipeline(); - }); - describe('Pipelines Table', () => { beforeEach(() => { - createComponent({ pipelines: [pipeline], viewType: 'root' }); + createComponent({ props: { viewType: 'root' } }); }); it('displays table', () => { @@ -105,7 +96,7 @@ describe('Pipelines Table', () => { }); it('should display a table row', () => { - expect(findTableRows()).toHaveLength(1); + expect(findTableRows()).toHaveLength(pipelines.length); }); describe('status cell', () => { @@ -120,7 +111,7 @@ describe('Pipelines Table', () => { }); it('should display the pipeline id', () => { - expect(findPipelineInfo().text()).toContain(`#${pipeline.id}`); + expect(findPipelineInfo().text()).toContain(`#${firstPipeline.id}`); }); }); @@ -130,24 +121,33 @@ describe('Pipelines Table', () => { }); it('should render the right number of stages', () => { - const stagesLength = pipeline.details.stages.length; - expect(findLegacyPipelineMiniGraph().props('stages').length).toBe(stagesLength); + const stagesLength = firstPipeline.details.stages.length; + expect(findLegacyPipelineMiniGraph().props('stages')).toHaveLength(stagesLength); }); it('should render the latest downstream pipelines only', () => { // component receives two downstream pipelines. one of them is already outdated // because we retried the trigger job, so the mini pipeline graph will only // render the newly created downstream pipeline instead - expect(pipeline.triggered).toHaveLength(2); + expect(firstPipeline.triggered).toHaveLength(2); expect(findLegacyPipelineMiniGraph().props('downstreamPipelines')).toHaveLength(1); }); describe('when pipeline does not have stages', () => { beforeEach(() => { - pipeline = createMockPipeline(); - pipeline.details.stages = []; - - createComponent({ pipelines: [pipeline] }); + createComponent({ + props: { + pipelines: [ + { + ...firstPipeline, + details: { + ...firstPipeline.details, + stages: [], + }, + }, + ], + }, + }); }); it('stages are not rendered', () => { @@ -163,6 +163,10 @@ describe('Pipelines Table', () => { }); describe('operations cell', () => { + beforeEach(() => { + createComponent({ stubs: { PipelineOperations } }); + }); + it('should render pipeline operations', () => { expect(findActions().exists()).toBe(true); }); @@ -186,11 +190,11 @@ describe('Pipelines Table', () => { describe('row', () => { describe('when the FF is disabled', () => { beforeEach(() => { - createComponent({ pipelines: [pipeline] }); + createComponent(); }); it('does not render', () => { - expect(findTableRows()).toHaveLength(1); + expect(findTableRows()).toHaveLength(pipelines.length); expect(findPipelineFailureWidget().exists()).toBe(false); }); }); @@ -198,20 +202,21 @@ describe('Pipelines Table', () => { describe('when the FF is enabled', () => { describe('and `withFailedJobsDetails` value is provided', () => { beforeEach(() => { - createComponent({ pipelines: [pipeline] }, provideWithDetails); + createComponent({ provide: provideWithDetails }); }); it('renders', () => { - expect(findTableRows()).toHaveLength(2); + // We have 2 rows per pipeline with the widget + expect(findTableRows()).toHaveLength(pipelines.length * 2); expect(findPipelineFailureWidget().exists()).toBe(true); }); it('passes the expected props', () => { expect(findPipelineFailureWidget().props()).toStrictEqual({ - failedJobsCount: pipeline.failed_builds.length, - isPipelineActive: pipeline.active, - pipelineIid: pipeline.iid, - pipelinePath: pipeline.path, + failedJobsCount: firstPipeline.failed_builds.length, + isPipelineActive: firstPipeline.active, + pipelineIid: firstPipeline.iid, + pipelinePath: firstPipeline.path, // Make sure the forward slash was removed projectPath: 'frontend-fixtures/pipelines-project', }); @@ -220,14 +225,13 @@ describe('Pipelines Table', () => { describe('and `withFailedJobsDetails` value is not provided', () => { beforeEach(() => { - createComponent( - { pipelines: [pipeline] }, - { glFeatures: { ciJobFailuresInMr: true } }, - ); + createComponent({ + provide: { glFeatures: { ciJobFailuresInMr: true } }, + }); }); it('does not render', () => { - expect(findTableRows()).toHaveLength(1); + expect(findTableRows()).toHaveLength(pipelines.length); expect(findPipelineFailureWidget().exists()).toBe(false); }); }); @@ -235,35 +239,55 @@ describe('Pipelines Table', () => { }); }); - describe('tracking', () => { + describe('events', () => { beforeEach(() => { - trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + createComponent(); }); - afterEach(() => { - unmockTracking(); + describe('when confirming to cancel a pipeline', () => { + beforeEach(async () => { + await findActions().vm.$emit('cancel-pipeline', firstPipeline); + }); + + it('emits the `cancel-pipeline` event', () => { + expect(wrapper.emitted('cancel-pipeline')).toEqual([[firstPipeline]]); + }); }); - it('tracks status badge click', () => { - findCiBadgeLink().vm.$emit('ciStatusBadgeClick'); + describe('when retrying a pipeline', () => { + beforeEach(() => { + findActions().vm.$emit('retry-pipeline', firstPipeline); + }); - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', { - label: TRACKING_CATEGORIES.table, + it('emits the `retry-pipeline` event', () => { + expect(wrapper.emitted('retry-pipeline')).toEqual([[firstPipeline]]); }); }); - it('tracks retry pipeline button click', () => { - findRetryBtn().vm.$emit('click'); + describe('when refreshing pipelines', () => { + beforeEach(() => { + findActions().vm.$emit('refresh-pipelines-table'); + }); - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', { - label: TRACKING_CATEGORIES.table, + it('emits the `refresh-pipelines-table` event', () => { + expect(wrapper.emitted('refresh-pipelines-table')).toEqual([[]]); }); }); + }); - it('tracks cancel pipeline button click', () => { - findCancelBtn().vm.$emit('click'); + describe('tracking', () => { + beforeEach(() => { + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }); + + afterEach(() => { + unmockTracking(); + }); - expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', { + it('tracks status badge click', () => { + findCiBadgeLink().vm.$emit('ciStatusBadgeClick'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', { label: TRACKING_CATEGORIES.table, }); }); diff --git a/spec/frontend/ci/pipelines_page/components/pipeline_operations_spec.js b/spec/frontend/ci/pipelines_page/components/pipeline_operations_spec.js index d2eab64b317..6205a37e291 100644 --- a/spec/frontend/ci/pipelines_page/components/pipeline_operations_spec.js +++ b/spec/frontend/ci/pipelines_page/components/pipeline_operations_spec.js @@ -1,10 +1,13 @@ +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import PipelinesManualActions from '~/ci/pipelines_page/components/pipelines_manual_actions.vue'; import PipelineMultiActions from '~/ci/pipelines_page/components/pipeline_multi_actions.vue'; import PipelineOperations from '~/ci/pipelines_page/components/pipeline_operations.vue'; -import eventHub from '~/ci/event_hub'; +import PipelineStopModal from '~/ci/pipelines_page/components/pipeline_stop_modal.vue'; +import { TRACKING_CATEGORIES } from '~/ci/constants'; describe('Pipeline operations', () => { + let trackingSpy; let wrapper; const defaultProps = { @@ -36,6 +39,7 @@ describe('Pipeline operations', () => { const findMultiActions = () => wrapper.findComponent(PipelineMultiActions); const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button'); const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button'); + const findPipelineStopModal = () => wrapper.findComponent(PipelineStopModal); it('should display pipeline manual actions', () => { createComponent(); @@ -49,28 +53,71 @@ describe('Pipeline operations', () => { expect(findMultiActions().exists()).toBe(true); }); + it('does not show the confirmation modal', () => { + createComponent(); + + expect(findPipelineStopModal().props().showConfirmationModal).toBe(false); + }); + + describe('when cancelling a pipeline', () => { + beforeEach(async () => { + createComponent(); + await findCancelBtn().vm.$emit('click'); + }); + + it('should show a confirmation modal', () => { + expect(findPipelineStopModal().props().showConfirmationModal).toBe(true); + }); + + it('should emit cancel-pipeline event when confirming', async () => { + await findPipelineStopModal().vm.$emit('submit'); + + expect(wrapper.emitted('cancel-pipeline')).toEqual([[defaultProps.pipeline]]); + expect(findPipelineStopModal().props().showConfirmationModal).toBe(false); + }); + + it('should hide the modal when closing', async () => { + await findPipelineStopModal().vm.$emit('close-modal'); + + expect(findPipelineStopModal().props().showConfirmationModal).toBe(false); + }); + }); + describe('events', () => { beforeEach(() => { createComponent(); - - jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); }); it('should emit retryPipeline event', () => { findRetryBtn().vm.$emit('click'); - expect(eventHub.$emit).toHaveBeenCalledWith( - 'retryPipeline', - defaultProps.pipeline.retry_path, - ); + expect(wrapper.emitted('retry-pipeline')).toEqual([[defaultProps.pipeline]]); + }); + }); + + describe('tracking', () => { + beforeEach(() => { + createComponent(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }); + + afterEach(() => { + unmockTracking(); + }); + + it('tracks retry pipeline button click', () => { + findRetryBtn().vm.$emit('click'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', { + label: TRACKING_CATEGORIES.table, + }); }); - it('should emit openConfirmationModal event', () => { + it('tracks cancel pipeline button click', () => { findCancelBtn().vm.$emit('click'); - expect(eventHub.$emit).toHaveBeenCalledWith('openConfirmationModal', { - pipeline: defaultProps.pipeline, - endpoint: defaultProps.pipeline.cancel_path, + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', { + label: TRACKING_CATEGORIES.table, }); }); }); diff --git a/spec/frontend/ci/pipelines_page/components/pipeline_stop_modal_spec.js b/spec/frontend/ci/pipelines_page/components/pipeline_stop_modal_spec.js index 4d78a923542..1e276840c07 100644 --- a/spec/frontend/ci/pipelines_page/components/pipeline_stop_modal_spec.js +++ b/spec/frontend/ci/pipelines_page/components/pipeline_stop_modal_spec.js @@ -1,15 +1,17 @@ import { shallowMount } from '@vue/test-utils'; -import { GlSprintf } from '@gitlab/ui'; +import { GlModal, GlSprintf } from '@gitlab/ui'; import { mockPipelineHeader } from 'jest/ci/pipeline_details/mock_data'; import PipelineStopModal from '~/ci/pipelines_page/components/pipeline_stop_modal.vue'; describe('PipelineStopModal', () => { let wrapper; - const createComponent = () => { + const createComponent = ({ props = {} } = {}) => { wrapper = shallowMount(PipelineStopModal, { propsData: { pipeline: mockPipelineHeader, + showConfirmationModal: false, + ...props, }, stubs: { GlSprintf, @@ -17,11 +19,43 @@ describe('PipelineStopModal', () => { }); }; + const findModal = () => wrapper.findComponent(GlModal); + beforeEach(() => { createComponent(); }); - it('should render "stop pipeline" warning', () => { - expect(wrapper.text()).toMatch(`You’re about to stop pipeline #${mockPipelineHeader.id}.`); + describe('when `showConfirmationModal` is false', () => { + it('passes the visiblity value to the modal', () => { + expect(findModal().props().visible).toBe(false); + }); + }); + + describe('when `showConfirmationModal` is true', () => { + beforeEach(() => { + createComponent({ props: { showConfirmationModal: true } }); + }); + + it('passes the visiblity value to the modal', () => { + expect(findModal().props().visible).toBe(true); + }); + + it('renders "stop pipeline" warning', () => { + expect(wrapper.text()).toMatch(`You're about to stop pipeline #${mockPipelineHeader.id}.`); + }); + }); + + describe('events', () => { + beforeEach(() => { + createComponent({ props: { showConfirmationModal: true } }); + }); + + it('emits the close-modal event when the visiblity changes', async () => { + expect(wrapper.emitted('close-modal')).toBeUndefined(); + + await findModal().vm.$emit('change', false); + + expect(wrapper.emitted('close-modal')).toEqual([[]]); + }); }); }); diff --git a/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js b/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js index 4af292e3588..d58b139dae3 100644 --- a/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js +++ b/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js @@ -1,13 +1,13 @@ import { GlLoadingIcon, GlModal, GlTableLite } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; import fixture from 'test_fixtures/pipelines/pipelines.json'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { stubComponent } from 'helpers/stub_component'; import waitForPromises from 'helpers/wait_for_promises'; import Api from '~/api'; -import LegacyPipelinesTableWraper from '~/commit/pipelines/legacy_pipelines_table_wrapper.vue'; +import LegacyPipelinesTableWrapper from '~/commit/pipelines/legacy_pipelines_table_wrapper.vue'; +import PipelinesTable from '~/ci/common/pipelines_table.vue'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_INTERNAL_SERVER_ERROR, @@ -39,27 +39,26 @@ describe('Pipelines table in Commits and Merge requests', () => { const findTableRows = () => wrapper.findAllByTestId('pipeline-table-row'); const findModal = () => wrapper.findComponent(GlModal); const findMrPipelinesDocsLink = () => wrapper.findByTestId('mr-pipelines-docs-link'); - - const createComponent = ({ props = {} } = {}) => { - wrapper = extendedWrapper( - mount(LegacyPipelinesTableWraper, { - propsData: { - endpoint: 'endpoint.json', - emptyStateSvgPath: 'foo', - errorStateSvgPath: 'foo', - ...props, - }, - mocks: { - $toast, - }, - stubs: { - GlModal: stubComponent(GlModal, { - template: '<div />', - methods: { show: showMock }, - }), - }, - }), - ); + const findPipelinesTable = () => wrapper.findComponent(PipelinesTable); + + const createComponent = ({ props = {}, mountFn = mountExtended } = {}) => { + wrapper = mountFn(LegacyPipelinesTableWrapper, { + propsData: { + endpoint: 'endpoint.json', + emptyStateSvgPath: 'foo', + errorStateSvgPath: 'foo', + ...props, + }, + mocks: { + $toast, + }, + stubs: { + GlModal: stubComponent(GlModal, { + template: '<div />', + methods: { show: showMock }, + }), + }, + }); }; beforeEach(() => { @@ -116,7 +115,6 @@ describe('Pipelines table in Commits and Merge requests', () => { it('should make an API request when using pagination', async () => { expect(mock.history.get).toHaveLength(1); - expect(mock.history.get[0].params.page).toBe('1'); wrapper.find('.next-page-item').trigger('click'); @@ -359,4 +357,53 @@ describe('Pipelines table in Commits and Merge requests', () => { ); }); }); + + describe('events', () => { + beforeEach(async () => { + mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipeline]); + + createComponent({ mountFn: shallowMountExtended }); + + await waitForPromises(); + }); + + describe('When cancelling a pipeline', () => { + it('sends the cancel action', async () => { + expect(mock.history.post).toHaveLength(0); + + findPipelinesTable().vm.$emit('cancel-pipeline', pipeline); + + await waitForPromises(); + + expect(mock.history.post).toHaveLength(1); + expect(mock.history.post[0].url).toContain('cancel.json'); + }); + }); + + describe('When retrying a pipeline', () => { + it('sends the retry action', async () => { + expect(mock.history.post).toHaveLength(0); + + findPipelinesTable().vm.$emit('retry-pipeline', pipeline); + + await waitForPromises(); + + expect(mock.history.post).toHaveLength(1); + expect(mock.history.post[0].url).toContain('retry.json'); + }); + }); + + describe('When refreshing a pipeline', () => { + it('calls the pipelines endpoint again', async () => { + expect(mock.history.get).toHaveLength(1); + + findPipelinesTable().vm.$emit('refresh-pipelines-table'); + + await waitForPromises(); + + expect(mock.history.get).toHaveLength(2); + expect(mock.history.get[1].url).toContain('endpoint.json'); + }); + }); + }); }); |