diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
commit | 0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch) | |
tree | 7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /app/assets/javascripts/pipelines/components | |
parent | 72123183a20411a36d607d70b12d57c484394c8e (diff) |
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/pipelines/components')
11 files changed, 160 insertions, 156 deletions
diff --git a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue index 9f76d4cec50..225706265c3 100644 --- a/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue +++ b/app/assets/javascripts/pipelines/components/graph/linked_pipeline.vue @@ -13,7 +13,6 @@ import { __, sprintf } from '~/locale'; import CancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql'; import RetryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql'; import CiStatus from '~/vue_shared/components/ci_icon.vue'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { PIPELINE_GRAPHQL_TYPE } from '../../constants'; import { reportToSentry } from '../../utils'; import { ACTION_FAILURE, DOWNSTREAM, UPSTREAM } from './constants'; @@ -35,7 +34,6 @@ export default { flatLeftBorder: ['gl-rounded-bottom-left-none!', 'gl-rounded-top-left-none!'], flatRightBorder: ['gl-rounded-bottom-right-none!', 'gl-rounded-top-right-none!'], }, - mixins: [glFeatureFlagMixin()], props: { columnTitle: { type: String, @@ -62,11 +60,12 @@ export default { return { hasActionTooltip: false, isActionLoading: false, + isExpandBtnFocus: false, }; }, computed: { action() { - if (this.glFeatures?.downstreamRetryAction && this.isDownstream) { + if (this.isDownstream) { if (this.isCancelable) { return { icon: 'cancel', @@ -89,6 +88,9 @@ export default { ? ['gl-border-r-0!', ...this.$options.styles.flatRightBorder] : ['gl-border-l-0!', ...this.$options.styles.flatLeftBorder]; }, + buttonShadowClass() { + return this.isExpandBtnFocus ? '' : 'gl-shadow-none!'; + }, buttonId() { return `js-linked-pipeline-${this.pipeline.id}`; }, @@ -99,9 +101,12 @@ export default { }, expandedIcon() { if (this.isUpstream) { - return this.expanded ? 'angle-right' : 'angle-left'; + return this.expanded ? 'chevron-lg-right' : 'chevron-lg-left'; } - return this.expanded ? 'angle-left' : 'angle-right'; + return this.expanded ? 'chevron-lg-left' : 'chevron-lg-right'; + }, + expandBtnText() { + return this.expanded ? __('Collapse jobs') : __('Expand jobs'); }, childPipeline() { return this.isDownstream && this.isSameProject; @@ -157,7 +162,7 @@ export default { return Boolean(this.action?.method && this.action?.icon && this.action?.ariaLabel); }, showCardTooltip() { - return !this.hasActionTooltip; + return !this.hasActionTooltip && !this.isExpandBtnFocus; }, sourceJobName() { return this.pipeline.sourceJob?.name ?? ''; @@ -214,6 +219,9 @@ export default { setActionTooltip(flag) { this.hasActionTooltip = flag; }, + setExpandBtnActiveState(flag) { + this.isExpandBtnFocus = flag; + }, }, }; </script> @@ -221,7 +229,7 @@ export default { <template> <div ref="linkedPipeline" - class="gl-h-full gl-display-flex!" + class="gl-h-full gl-display-flex! gl-px-2" :class="flexDirection" data-qa-selector="linked_pipeline_container" @mouseover="onDownstreamHovered" @@ -237,7 +245,11 @@ export default { <div class="gl-display-flex gl-downstream-pipeline-job-width gl-flex-direction-column gl-line-height-normal" > - <span class="gl-text-truncate" data-testid="downstream-title"> + <span + class="gl-text-truncate" + data-testid="downstream-title" + data-qa-selector="downstream_title_content" + > {{ downstreamTitle }} </span> <div class="gl-text-truncate"> @@ -273,12 +285,18 @@ export default { <div class="gl-display-flex"> <gl-button :id="buttonId" - class="gl-border! gl-shadow-none! gl-rounded-lg!" - :class="[`js-pipeline-expand-${pipeline.id}`, buttonBorderClasses]" + v-gl-tooltip + :title="expandBtnText" + class="gl-border! gl-rounded-lg!" + :class="[`js-pipeline-expand-${pipeline.id}`, buttonBorderClasses, buttonShadowClass]" :icon="expandedIcon" - :aria-label="__('Expand pipeline')" + :aria-label="expandBtnText" data-testid="expand-pipeline-button" data-qa-selector="expand_linked_pipeline_button" + @mouseover="setExpandBtnActiveState(true)" + @mouseout="setExpandBtnActiveState(false)" + @focus="setExpandBtnActiveState(true)" + @blur="setExpandBtnActiveState(false)" @click="onClickLinkedPipeline" /> </div> diff --git a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue index 1c646bdf3d6..070c5ee59de 100644 --- a/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue +++ b/app/assets/javascripts/pipelines/components/jobs/failed_jobs_table.vue @@ -56,7 +56,13 @@ export default { </script> <template> - <gl-table-lite :items="failedJobs" :fields="$options.fields" stacked="lg" fixed> + <gl-table-lite + :items="failedJobs" + :fields="$options.fields" + stacked="lg" + fixed + data-testId="tab-failures" + > <template #table-colgroup="{ fields }"> <col v-for="field in fields" :key="field.key" :class="field.columnClass" /> </template> diff --git a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue index b45f3e4f32c..18e9ffa23cf 100644 --- a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue +++ b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue @@ -127,7 +127,7 @@ export default { <jobs-table v-else :jobs="jobs" :table-fields="$options.fields" data-testid="jobs-tab-table" /> <gl-intersection-observer v-if="jobsPageInfo.hasNextPage" @appear="fetchMoreJobs"> - <gl-loading-icon v-if="showLoadingSpinner" size="md" /> + <gl-loading-icon v-if="showLoadingSpinner" size="lg" /> </gl-intersection-observer> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/notification/deprecated_type_keyword_notification.vue b/app/assets/javascripts/pipelines/components/notification/deprecated_type_keyword_notification.vue deleted file mode 100644 index b8f9f84c217..00000000000 --- a/app/assets/javascripts/pipelines/components/notification/deprecated_type_keyword_notification.vue +++ /dev/null @@ -1,102 +0,0 @@ -<script> -import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui'; -import { __ } from '~/locale'; -import getPipelineWarnings from '../../graphql/queries/get_pipeline_warnings.query.graphql'; - -export default { - // eslint-disable-next-line @gitlab/require-i18n-strings - expectedMessage: 'will be removed in', - i18n: { - title: __('Found warning in your .gitlab-ci.yml'), - rootTypesWarning: __( - '%{codeStart}types%{codeEnd} is deprecated and will be removed in 15.0. Use %{codeStart}stages%{codeEnd} instead. %{linkStart}Learn More %{linkEnd}', - ), - typeWarning: __( - '%{codeStart}type%{codeEnd} is deprecated and will be removed in 15.0. Use %{codeStart}stage%{codeEnd} instead. %{linkStart}Learn More %{linkEnd}', - ), - }, - components: { - GlAlert, - GlLink, - GlSprintf, - }, - inject: ['deprecatedKeywordsDocPath', 'fullPath', 'pipelineIid'], - apollo: { - warnings: { - query: getPipelineWarnings, - variables() { - return { - fullPath: this.fullPath, - iid: this.pipelineIid, - }; - }, - update(data) { - return data?.project?.pipeline?.warningMessages || []; - }, - error() { - this.hasError = true; - }, - }, - }, - data() { - return { - warnings: [], - hasError: false, - }; - }, - computed: { - deprecationWarnings() { - return this.warnings.filter(({ content }) => { - return content.includes(this.$options.expectedMessage); - }); - }, - formattedWarnings() { - // The API doesn't have a mechanism currently to return a - // type instead of just the error message. To work around this, - // we check if the deprecation message is found within the warnings - // and show a FE version of that message with the link to the documentation - // and translated. We can have only 2 types of warnings: root types and individual - // type. If the word `root` is present, then we know it's the root type deprecation - // and if not, it's the normal type. This has to be deleted in 15.0. - // Follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/350810 - return this.deprecationWarnings.map(({ content }) => { - if (content.includes('root')) { - return this.$options.i18n.rootTypesWarning; - } - return this.$options.i18n.typeWarning; - }); - }, - hasDeprecationWarning() { - return this.formattedWarnings.length > 0; - }, - showWarning() { - return ( - !this.$apollo.queries.warnings?.loading && !this.hasError && this.hasDeprecationWarning - ); - }, - }, -}; -</script> -<template> - <div> - <gl-alert - v-if="showWarning" - :title="$options.i18n.title" - variant="warning" - :dismissible="false" - > - <ul class="gl-mb-0"> - <li v-for="warning in formattedWarnings" :key="warning"> - <gl-sprintf :message="warning"> - <template #code="{ content }"> - <code> {{ content }}</code> - </template> - <template #link="{ content }"> - <gl-link :href="deprecatedKeywordsDocPath" target="_blank"> {{ content }}</gl-link> - </template> - </gl-sprintf> - </li> - </ul> - </gl-alert> - </div> -</template> diff --git a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue index 66d30c10362..e1745969649 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue @@ -1,9 +1,10 @@ <script> -import { GlTabs, GlTab } from '@gitlab/ui'; +import { GlBadge, GlTabs, GlTab } from '@gitlab/ui'; import { __ } from '~/locale'; import { failedJobsTabName, jobsTabName, needsTabName, testReportTabName } from '../constants'; import PipelineGraphWrapper from './graph/graph_component_wrapper.vue'; import Dag from './dag/dag.vue'; +import FailedJobsApp from './jobs/failed_jobs_app.vue'; import JobsApp from './jobs/jobs_app.vue'; import TestReports from './test_reports/test_reports.vue'; @@ -25,14 +26,20 @@ export default { }, components: { Dag, + GlBadge, GlTab, GlTabs, JobsApp, - FailedJobsApp: JobsApp, + FailedJobsApp, PipelineGraphWrapper, TestReports, }, - inject: ['defaultTabValue'], + inject: ['defaultTabValue', 'failedJobsCount', 'failedJobsSummary', 'totalJobCount'], + computed: { + showFailedJobsTab() { + return this.failedJobsCount > 0; + }, + }, methods: { isActive(tabName) { return tabName === this.defaultTabValue; @@ -54,19 +61,25 @@ export default { > <dag /> </gl-tab> - <gl-tab - :title="$options.i18n.tabs.jobsTitle" - :active="isActive($options.tabNames.jobs)" - data-testid="jobs-tab" - > + <gl-tab :active="isActive($options.tabNames.jobs)" data-testid="jobs-tab" lazy> + <template #title> + <span class="gl-mr-2">{{ $options.i18n.tabs.jobsTitle }}</span> + <gl-badge size="sm" data-testid="builds-counter">{{ totalJobCount }}</gl-badge> + </template> <jobs-app /> </gl-tab> <gl-tab + v-if="showFailedJobsTab" :title="$options.i18n.tabs.failedJobsTitle" :active="isActive($options.tabNames.failures)" data-testid="failed-jobs-tab" + lazy > - <failed-jobs-app /> + <template #title> + <span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span> + <gl-badge size="sm" data-testid="failed-builds-counter">{{ failedJobsCount }}</gl-badge> + </template> + <failed-jobs-app :failed-jobs-summary="failedJobsSummary" /> </gl-tab> <gl-tab :title="$options.i18n.tabs.testsTitle" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue index e35fccf2d7e..05cb2ebb769 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue @@ -36,12 +36,12 @@ export default { }; </script> <template> - <div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle gl-my-1"> + <div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle"> <div v-for="stage in stages" :key="stage.name" :class="stagesClass" - class="stage-container dropdown" + class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container" > <pipeline-stage :stage="stage" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue index 53e21d4ce8b..d7e55d36ff6 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue @@ -21,6 +21,13 @@ import eventHub from '../../event_hub'; import JobItem from './job_item.vue'; export default { + i18n: { + stage: __('Stage:'), + loadingText: __('Loading, please wait.'), + }, + dropdownPopperOpts: { + placement: 'bottom', + }, components: { CiIcon, GlLoadingIcon, @@ -48,20 +55,26 @@ export default { }, data() { return { + isDropdownOpen: false, isLoading: false, dropdownContent: [], + stageName: '', }; }, watch: { updateDropdown() { - if (this.updateDropdown && this.isDropdownOpen() && !this.isLoading) { + if (this.updateDropdown && this.isDropdownOpen && !this.isLoading) { this.fetchJobs(); } }, }, methods: { + onHideDropdown() { + this.isDropdownOpen = false; + }, onShowDropdown() { eventHub.$emit('clickedDropdown'); + this.isDropdownOpen = true; this.isLoading = true; this.fetchJobs(); }, @@ -70,6 +83,7 @@ export default { .get(this.stage.dropdown_path) .then(({ data }) => { this.dropdownContent = data.latest_statuses; + this.stageName = data.name; this.isLoading = false; }) .catch(() => { @@ -81,9 +95,6 @@ export default { }); }); }, - isDropdownOpen() { - return this.$el.classList.contains('show'); - }, pipelineActionRequestComplete() { // close the dropdown in MR widget this.$refs.dropdown.hide(); @@ -107,28 +118,42 @@ export default { variant="link" :aria-label="stageAriaLabel(stage.title)" :lazy="true" - :popper-opts="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ { - placement: 'bottom', - } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" + :popper-opts="$options.dropdownPopperOpts" :toggle-class="['gl-rounded-full!']" menu-class="mini-pipeline-graph-dropdown-menu" + @hide="onHideDropdown" @show="onShowDropdown" > <template #button-content> <ci-icon + is-borderless is-interactive css-classes="gl-rounded-full" + :is-active="isDropdownOpen" :size="24" :status="stage.status" - class="gl-align-items-center gl-display-inline-flex" + class="gl-align-items-center gl-border gl-display-inline-flex gl-z-index-1" /> </template> - <gl-loading-icon v-if="isLoading" size="sm" /> + <div + v-if="isLoading" + class="gl-display-flex gl-justify-content-center gl-p-2" + data-testid="pipeline-stage-loading-state" + > + <gl-loading-icon size="sm" class="gl-mr-3" /> + <p class="gl-mb-0">{{ $options.i18n.loadingText }}</p> + </div> <ul v-else class="js-builds-dropdown-list scrollable-menu" data-testid="mini-pipeline-graph-dropdown-menu-list" > + <div + class="gl-align-items-center gl-border-b gl-display-flex gl-font-weight-bold gl-justify-content-center gl-pb-3" + > + <span class="gl-mr-1">{{ $options.i18n.stage }}</span> + <span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span> + </div> <li v-for="job in dropdownContent" :key="job.id"> <job-item :dropdown-length="dropdownContent.length" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue index 63c492c8bcd..09d588aaafd 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue @@ -137,9 +137,8 @@ export default { class="gl-text-decoration-underline gl-text-blue-600! gl-mr-3" data-testid="pipeline-url-link" data-qa-selector="pipeline_url_link" + >#{{ pipeline[pipelineKey] }}</gl-link > - #{{ pipeline[pipelineKey] }} - </gl-link> <!--Commit row--> <div class="icon-container gl-display-inline-block gl-mr-1"> <gl-icon diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue b/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue index 47e5bb0bde8..76ee6ab613b 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_case_details.vue @@ -19,7 +19,10 @@ export default { }, testCase: { type: Object, - required: true, + required: false, + default: () => { + return {}; + }, }, }, computed: { @@ -49,6 +52,7 @@ export default { }, text: { name: __('Name'), + file: __('File'), duration: __('Execution time'), history: __('History'), trace: __('System output'), @@ -56,7 +60,7 @@ export default { }, modalCloseButton: { text: __('Close'), - attributes: [{ variant: 'info' }], + attributes: [{ variant: 'confirm' }], }, }; </script> @@ -74,11 +78,24 @@ export default { </div> </div> + <div v-if="testCase.file" class="gl-display-flex gl-flex-wrap gl-mx-n4 gl-my-3"> + <strong class="gl-text-right col-sm-3">{{ $options.text.file }}</strong> + <div class="col-sm-9" data-testid="test-case-file"> + <gl-link v-if="testCase.filePath" :href="testCase.filePath"> + {{ testCase.file }} + </gl-link> + <span v-else>{{ testCase.file }}</span> + </div> + </div> + <div class="gl-display-flex gl-flex-wrap gl-mx-n4 gl-my-3"> <strong class="gl-text-right col-sm-3">{{ $options.text.duration }}</strong> - <div class="col-sm-9" data-testid="test-case-duration"> + <div v-if="testCase.formattedTime" class="col-sm-9" data-testid="test-case-duration"> {{ testCase.formattedTime }} </div> + <div v-else-if="testCase.execution_time" class="col-sm-9" data-testid="test-case-duration"> + {{ sprintf('%{value} s', { value: testCase.execution_time }) }} + </div> </div> <div v-if="testCase.recent_failures" class="gl-display-flex gl-flex-wrap gl-mx-n4 gl-my-3"> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue index 9b0e6560c53..1e481d37017 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue @@ -7,11 +7,21 @@ import { GlLink, GlButton, GlPagination, + GlEmptyState, + GlSprintf, } from '@gitlab/ui'; import { mapState, mapGetters, mapActions } from 'vuex'; -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; import TestCaseDetails from './test_case_details.vue'; +export const i18n = { + expiredArtifactsTitle: s__('TestReports|Job artifacts are expired'), + expiredArtifactsDescription: s__( + 'TestReports|Test reports require job artifacts but all artifacts are expired. %{linkStart}Learn more%{linkEnd}', + ), +}; + export default { name: 'TestsSuiteTable', components: { @@ -20,12 +30,19 @@ export default { GlLink, GlButton, GlPagination, + GlEmptyState, + GlSprintf, TestCaseDetails, }, directives: { GlTooltip: GlTooltipDirective, GlModalDirective, }, + inject: { + artifactsExpiredImagePath: { + default: '', + }, + }, props: { heading: { type: String, @@ -44,18 +61,21 @@ export default { ...mapActions(['setPage']), }, wrapSymbols: ['::', '#', '.', '_', '-', '/', '\\'], + i18n, + learnMorePath: helpPagePath('ci/unit_test_reports', { + anchor: 'viewing-unit-test-reports-on-gitlab', + }), }; </script> <template> <div> - <div class="row gl-mt-3"> - <div class="col-12"> - <h4>{{ heading }}</h4> - </div> - </div> - <div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-cases-table"> + <div class="row gl-mt-3"> + <div class="col-12"> + <h4>{{ heading }}</h4> + </div> + </div> <div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray"> <div role="rowheader" class="table-section section-20"> {{ __('Suite') }} @@ -158,16 +178,24 @@ export default { </div> <div v-else> - <p data-testid="no-test-cases"> + <gl-empty-state + v-if="getSuiteArtifactsExpired" + :title="$options.i18n.expiredArtifactsTitle" + :svg-path="artifactsExpiredImagePath" + :svg-height="100" + data-testid="artifacts-expired" + > + <template #description> + <gl-sprintf :message="$options.i18n.expiredArtifactsDescription"> + <template #link="{ content }"> + <gl-link :href="$options.learnMorePath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </template> + </gl-empty-state> + <p v-else data-testid="no-test-cases"> {{ s__('TestReports|There are no test cases to display.') }} </p> - <p v-if="getSuiteArtifactsExpired" data-testid="artifacts-expired"> - {{ - s__( - 'TestReports|Test details are populated by job artifacts. The job artifacts from this pipeline are expired.', - ) - }} - </p> </div> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue index 79b1b6af38b..2f5301715c3 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary.vue @@ -71,7 +71,7 @@ export default { v-if="showBack" size="small" class="gl-mr-3 js-back-button" - icon="angle-left" + icon="chevron-lg-left" :aria-label="__('Go back')" @click="onBackClick" /> |