Commit f862ca32 authored by Frédéric Caplette's avatar Frédéric Caplette Committed by Mark Florian

Migrate pipeline tabs to Vue behing a disabled flag

In the current effort to migrate components to respect
Pajamas guideline, we are migrating all the tabs components.
This commit migrate the pipeline tabs behind disabled flag because
each content of the tabs are not adjusted to the new Vue
layout.
parent 47b9390a
<script>
import { GlTabs, GlTab } from '@gitlab/ui';
import { __ } from '~/locale';
import PipelineGraphWrapper from './graph/graph_component_wrapper.vue';
import Dag from './dag/dag.vue';
import JobsApp from './jobs/jobs_app.vue';
import TestReports from './test_reports/test_reports.vue';
export default {
i18n: {
tabs: {
failedJobsTitle: __('Failed Jobs'),
jobsTitle: __('Jobs'),
needsTitle: __('Needs'),
pipelineTitle: __('Pipeline'),
testsTitle: __('Tests'),
},
},
components: {
Dag,
GlTab,
GlTabs,
JobsApp,
FailedJobsApp: JobsApp,
PipelineGraphWrapper,
TestReports,
},
};
</script>
<template>
<gl-tabs>
<gl-tab :title="$options.i18n.tabs.pipelineTitle" data-testid="pipeline-tab">
<pipeline-graph-wrapper />
</gl-tab>
<gl-tab :title="$options.i18n.tabs.needsTitle" data-testid="dag-tab">
<dag />
</gl-tab>
<gl-tab :title="$options.i18n.tabs.jobsTitle" data-testid="jobs-tab">
<jobs-app />
</gl-tab>
<gl-tab :title="$options.i18n.tabs.failedJobsTitle" data-testid="failed-jobs-tab">
<failed-jobs-app />
</gl-tab>
<gl-tab :title="$options.i18n.tabs.testsTitle" data-testid="tests-tab">
<test-reports />
</gl-tab>
<slot></slot>
</gl-tabs>
</template>
......@@ -13,6 +13,7 @@ const SELECTORS = {
PIPELINE_GRAPH: '#js-pipeline-graph-vue',
PIPELINE_HEADER: '#js-pipeline-header-vue',
PIPELINE_NOTIFICATION: '#js-pipeline-notification',
PIPELINE_TABS: '#js-pipeline-tabs',
PIPELINE_TESTS: '#js-pipeline-tests-detail',
PIPELINE_JOBS: '#js-pipeline-jobs-vue',
};
......@@ -28,22 +29,6 @@ export default async function initPipelineDetailsBundle() {
});
}
try {
createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset);
} catch {
createFlash({
message: __('An error occurred while loading the pipeline.'),
});
}
try {
createPipelineHeaderApp(SELECTORS.PIPELINE_HEADER, apolloProvider, dataset.graphqlResourceEtag);
} catch {
createFlash({
message: __('An error occurred while loading a section of this page.'),
});
}
try {
createPipelineNotificationApp(SELECTORS.PIPELINE_NOTIFICATION, apolloProvider);
} catch {
......@@ -52,27 +37,47 @@ export default async function initPipelineDetailsBundle() {
});
}
try {
createDagApp(apolloProvider);
} catch {
createFlash({
message: __('An error occurred while loading the Needs tab.'),
});
}
if (gon.features?.pipelineTabsVue) {
const { createPipelineTabs } = await import('./pipeline_tabs');
try {
createTestDetails(SELECTORS.PIPELINE_TESTS);
} catch {
createFlash({
message: __('An error occurred while loading the Test Reports tab.'),
});
}
try {
createPipelineTabs(SELECTORS.PIPELINE_TABS, apolloProvider);
} catch {
createFlash({
message: __('An error occurred while loading a section of this page.'),
});
}
} else {
try {
createPipelinesDetailApp(SELECTORS.PIPELINE_GRAPH, apolloProvider, dataset);
} catch {
createFlash({
message: __('An error occurred while loading the pipeline.'),
});
}
try {
createPipelineJobsApp(SELECTORS.PIPELINE_JOBS);
} catch {
createFlash({
message: __('An error occurred while loading the Jobs tab.'),
});
try {
createDagApp(apolloProvider);
} catch {
createFlash({
message: __('An error occurred while loading the Needs tab.'),
});
}
try {
createTestDetails(SELECTORS.PIPELINE_TESTS);
} catch {
createFlash({
message: __('An error occurred while loading the Test Reports tab.'),
});
}
try {
createPipelineJobsApp(SELECTORS.PIPELINE_JOBS);
} catch {
createFlash({
message: __('An error occurred while loading the Jobs tab.'),
});
}
}
}
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import PipelineTabs from 'ee_else_ce/pipelines/components/pipeline_tabs.vue';
import { reportToSentry } from './utils';
Vue.use(VueApollo);
const createPipelineTabs = (selector, apolloProvider) => {
const el = document.querySelector(selector);
if (!el) return;
const { dataset } = document.querySelector(selector);
const {
canGenerateCodequalityReports,
codequalityReportDownloadPath,
downloadablePathForReportType,
exposeSecurityDashboard,
exposeLicenseScanningData,
} = dataset;
// eslint-disable-next-line no-new
new Vue({
el: selector,
components: {
PipelineTabs,
},
apolloProvider,
provide: {
canGenerateCodequalityReports: JSON.parse(canGenerateCodequalityReports),
codequalityReportDownloadPath,
downloadablePathForReportType,
exposeSecurityDashboard: JSON.parse(exposeSecurityDashboard),
exposeLicenseScanningData: JSON.parse(exposeLicenseScanningData),
},
errorCaptured(err, _vm, info) {
reportToSentry('pipeline_tabs', `error: ${err}, info: ${info}`);
},
render(createElement) {
return createElement(PipelineTabs);
},
});
};
export { createPipelineTabs };
......@@ -17,6 +17,10 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action :ensure_pipeline, only: [:show, :downloadable_artifacts]
before_action do
push_frontend_feature_flag(:pipeline_tabs_vue, @project, default_enabled: :yaml)
end
# Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? }
......
# frozen_string_literal: true
module Projects
module PipelineHelper
def js_pipeline_tabs_data(project, pipeline)
{
can_generate_codequality_reports: pipeline.can_generate_codequality_reports?.to_json,
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_project_path: project.full_path
}
end
end
end
Projects::PipelineHelper.prepend_mod
......@@ -27,6 +27,8 @@
= s_('You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}').html_safe % { gitlab_ci_yml: '.gitlab-ci.yml', lint_link_start: lint_link_start, lint_link_end: '</a>'.html_safe }
#js-pipeline-notification{ data: { deprecated_keywords_doc_path: help_page_path('ci/yaml/index.md', anchor: 'deprecated-keywords'), full_path: @project.full_path, pipeline_iid: @pipeline.iid } }
= render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
- if Feature.enabled?(:pipeline_tabs_vue, @project, default_enabled: :yaml)
#js-pipeline-tabs{ data: js_pipeline_tabs_data(@project, @pipeline) }
- else
= render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
.js-pipeline-details-vue{ data: { metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: @project.namespace, project_id: @project, format: :json), pipeline_project_path: @project.full_path, pipeline_iid: @pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@pipeline) } }
---
name: pipeline_tabs_vue
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80401
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353118
milestone: '14.10'
type: development
group: group::pipeline authoring
default_enabled: false
<script>
import { GlTab } from '@gitlab/ui';
import BasePipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '~/locale';
import CodequalityReportApp from 'ee/codequality_report/codequality_report.vue';
import CodequalityReportAppGraphql from 'ee/codequality_report/codequality_report_graphql.vue';
import LicenseComplianceApp from 'ee/license_compliance/components/app.vue';
import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline/pipeline_security_dashboard.vue';
export default {
i18n: {
tabs: {
securityTitle: __('Security'),
licenseTitle: __('Licenses'),
codeQualityTitle: __('Code Quality'),
},
},
components: {
BasePipelineTabs,
CodequalityReportApp,
CodequalityReportAppGraphql,
GlTab,
LicenseComplianceApp,
PipelineSecurityDashboard,
},
mixins: [glFeatureFlagMixin()],
inject: [
'canGenerateCodequalityReports',
'codequalityReportDownloadPath',
'exposeSecurityDashboard',
'exposeLicenseScanningData',
],
computed: {
isGraphqlCodeQuality() {
return this.glFeatures.graphqlCodeQualityFullReport;
},
showCodeQualityTab() {
return Boolean(this.codequalityReportDownloadPath || this.canGenerateCodequalityReports);
},
showLicenseTab() {
return Boolean(this.exposeLicenseScanningData);
},
showSecurityTab() {
return Boolean(this.exposeSecurityDashboard);
},
},
};
</script>
<template>
<base-pipeline-tabs>
<gl-tab
v-if="showSecurityTab"
:title="$options.i18n.tabs.securityTitle"
data-testid="security-tab"
>
<pipeline-security-dashboard />
</gl-tab>
<gl-tab
v-if="showLicenseTab"
:title="$options.i18n.tabs.licenseTitle"
data-testid="license-tab"
>
<license-compliance-app />
</gl-tab>
<gl-tab
v-if="showCodeQualityTab"
:title="$options.i18n.tabs.codeQualityTitle"
data-testid="code-quality-tab"
data-track-action="click_button"
data-track-label="get_codequality_report"
>
<codequality-report-app-graphql v-if="isGraphqlCodeQuality" />
<codequality-report-app v-else />
</gl-tab>
</base-pipeline-tabs>
</template>
# frozen_string_literal: true
module EE
module Projects
module PipelineHelper
extend ::Gitlab::Utils::Override
override :js_pipeline_tabs_data
def js_pipeline_tabs_data(project, pipeline)
super.merge(
codequality_report_download_path: codequality_report_download_path(project, pipeline),
expose_license_scanning_data: pipeline.expose_license_scanning_data?.to_json,
expose_security_dashboard: pipeline.expose_security_dashboard?.to_json
)
end
def codequality_report_download_path(project, pipeline)
return unless project.licensed_feature_available?(:full_codequality_report)
pipeline.downloadable_path_for_report_type(:codequality)
end
end
end
end
......@@ -14,6 +14,10 @@ RSpec.describe Projects::PipelinesController do
end
describe 'GET security' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
context 'with a sast artifact' do
before do
create(:ee_ci_build, :sast, pipeline: pipeline)
......@@ -44,6 +48,10 @@ RSpec.describe Projects::PipelinesController do
end
context 'without sast artifact' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
context 'with feature enabled' do
before do
stub_licensed_features(sast: true)
......@@ -69,6 +77,10 @@ RSpec.describe Projects::PipelinesController do
end
describe 'GET licenses' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
let(:licenses_with_html) {get :licenses, format: :html, params: { namespace_id: project.namespace, project_id: project, id: pipeline }}
let(:licenses_with_json) {get :licenses, format: :json, params: { namespace_id: project.namespace, project_id: project, id: pipeline }}
let!(:mit_license) { create(:software_license, :mit) }
......
......@@ -14,6 +14,10 @@ RSpec.describe 'Pipeline', :js do
end
describe 'GET /:project/-/pipelines/:id' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
let(:pipeline) { create(:ci_pipeline, :with_job, project: project, ref: 'master', sha: project.commit.id, user: user) }
subject { visit project_pipeline_path(project, pipeline) }
......@@ -133,6 +137,7 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
before do
stub_feature_flags(pipeline_tabs_vue: false)
stub_licensed_features(sast: true, security_dashboard: true)
stub_feature_flags(pipeline_security_dashboard_graphql: false)
end
......@@ -170,6 +175,7 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
before do
stub_feature_flags(pipeline_tabs_vue: false)
stub_licensed_features(license_scanning: true)
end
......@@ -205,6 +211,10 @@ RSpec.describe 'Pipeline', :js do
end
describe 'GET /:project/-/pipelines/:id/codequality_report', :aggregate_failures do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
shared_examples_for 'full codequality report' do
context 'when licensed' do
before do
......
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import BasePipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
import PipelineTabs from 'ee/pipelines/components/pipeline_tabs.vue';
import CodequalityReportApp from 'ee/codequality_report/codequality_report.vue';
import CodequalityReportAppGraphql from 'ee/codequality_report/codequality_report_graphql.vue';
import LicenseComplianceApp from 'ee/license_compliance/components/app.vue';
import PipelineSecurityDashboard from 'ee/security_dashboard/components/pipeline/pipeline_security_dashboard.vue';
describe('The Pipeline Tabs', () => {
let wrapper;
const findCodeQualityTab = () => wrapper.findByTestId('code-quality-tab');
const findDagTab = () => wrapper.findByTestId('dag-tab');
const findFailedJobsTab = () => wrapper.findByTestId('failed-jobs-tab');
const findJobsTab = () => wrapper.findByTestId('jobs-tab');
const findLicenseTab = () => wrapper.findByTestId('license-tab');
const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
const findSecurityTab = () => wrapper.findByTestId('security-tab');
const findTestsTab = () => wrapper.findByTestId('tests-tab');
const findCodeQualityApp = () => wrapper.findComponent(CodequalityReportApp);
const findCodeQualityAppGraphql = () => wrapper.findComponent(CodequalityReportAppGraphql);
const findLicenseApp = () => wrapper.findComponent(LicenseComplianceApp);
const findSecurityApp = () => wrapper.findComponent(PipelineSecurityDashboard);
const defaultProvide = {
canGenerateCodequalityReports: false,
codequalityReportDownloadPath: '',
exposeSecurityDashboard: false,
exposeLicenseScanningData: false,
};
const createComponent = ({ propsData = {}, provide = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(PipelineTabs, {
propsData,
provide: {
...defaultProvide,
...provide,
},
stubs: {
BasePipelineTabs,
Dag: { template: '<div id="dag"/>' },
JobsApp: { template: '<div class="jobs" />' },
PipelineGraph: { template: '<div id="graph" />' },
TestReports: { template: '<div id="tests" />' },
},
}),
);
};
afterEach(() => {
wrapper.destroy();
});
// The failed jobs MUST be removed from here and tested individually once
// the logic for the tab is implemented.
describe('CE Tabs', () => {
it.each`
tabName | tabComponent
${'Pipeline'} | ${findPipelineTab}
${'Dag'} | ${findDagTab}
${'Failed Jobs'} | ${findFailedJobsTab}
${'Jobs'} | ${findJobsTab}
${'Tests'} | ${findTestsTab}
`('shows $tabName tab', ({ tabComponent }) => {
createComponent();
expect(tabComponent().exists()).toBe(true);
});
});
describe('EE Tabs', () => {
describe('visibility', () => {
it.each`
tabName | tabComponent | appComponent | provideKey | isVisible | text
${'Security'} | ${findSecurityTab} | ${findSecurityApp} | ${'exposeSecurityDashboard'} | ${true} | ${'shows'}
${'Security'} | ${findSecurityTab} | ${findSecurityApp} | ${'exposeSecurityDashboard'} | ${false} | ${'hides'}
${'License'} | ${findLicenseTab} | ${findLicenseApp} | ${'exposeLicenseScanningData'} | ${true} | ${'shows'}
${'License'} | ${findLicenseTab} | ${findLicenseApp} | ${'exposeLicenseScanningData'} | ${false} | ${'hides'}
`(
'$text $tabName and its associated component when $provideKey is $provideKey ',
({ tabComponent, appComponent, provideKey, isVisible }) => {
createComponent({
provide: { [provideKey]: isVisible },
});
expect(tabComponent().exists()).toBe(isVisible);
expect(appComponent().exists()).toBe(isVisible);
},
);
});
describe('code quality visibility', () => {
describe('feature flags', () => {
describe('with `graphqlCodeQualityFullReport` enabled', () => {
beforeEach(() => {
createComponent({
provide: {
canGenerateCodequalityReports: true,
glFeatures: {
graphqlCodeQualityFullReport: true,
},
},
});
});
it('shows the graphql code quality report app', () => {
expect(findCodeQualityAppGraphql().exists()).toBe(true);
expect(findCodeQualityApp().exists()).toBe(false);
});
});
describe('with `graphqlCodeQualityFullReport` disabled', () => {
beforeEach(() => {
createComponent({
provide: {
canGenerateCodequalityReports: true,
glFeatures: {
graphqlCodeQualityFullReport: false,
},
},
});
});
it('shows the default code quality report app', () => {
expect(findCodeQualityAppGraphql().exists()).toBe(false);
expect(findCodeQualityApp().exists()).toBe(true);
});
});
});
it.each`
provideValue | isVisible | codequalityReportDownloadPath | text
${true} | ${true} | ${''} | ${'shows'}
${false} | ${false} | ${''} | ${'hides'}
${false} | ${true} | ${'/path'} | ${'shows'}
${true} | ${true} | ${'/path'} | ${'shows'}
`(
'$text Code Quality and its associated component when canGenerateCodequalityReports is $provideValue and codequalityReportDownloadPath is $codequalityReportDownloadPath',
({ provideValue, isVisible, codequalityReportDownloadPath }) => {
createComponent({
provide: { canGenerateCodequalityReports: provideValue, codequalityReportDownloadPath },
});
expect(findCodeQualityTab().exists()).toBe(isVisible);
expect(findCodeQualityApp().exists()).toBe(isVisible);
},
);
});
});
});
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::PipelineHelper do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:raw_pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user)}
describe '#js_pipeline_tabs_data' do
subject(:pipeline_tabs_data) { helper.js_pipeline_tabs_data(project, pipeline) }
it 'returns pipeline tabs data' do
expect(pipeline_tabs_data).to eq({
can_generate_codequality_reports: pipeline.can_generate_codequality_reports?.to_json,
codequality_report_download_path: helper.codequality_report_download_path(project, pipeline),
expose_license_scanning_data: pipeline.expose_license_scanning_data?.to_json,
expose_security_dashboard: pipeline.expose_security_dashboard?.to_json,
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_project_path: project.full_path
})
end
end
describe 'codequality_report_download_path' do
before do
project.add_developer(user)
end
subject(:codequality_report_path) { helper.codequality_report_download_path(project, pipeline) }
describe 'when `full_codequality_report` feature is not available' do
before do
stub_licensed_features(full_codequality_report: false)
end
it 'returns nil' do
is_expected.to be(nil)
end
end
describe 'when `full_code_quality_report` feature is available' do
before do
stub_licensed_features(full_codequality_report: true)
end
describe 'and there is no artefact for codequality' do
it 'returns nil for `codequality`' do
is_expected.to be(nil)
end
end
describe 'and there is an artefact for codequality' do
before do
create(:ci_build, :codequality_report, pipeline: raw_pipeline)
end
it 'returns the downloadable path for `codequality`' do
is_expected.not_to be(nil)
is_expected.to eq(pipeline.downloadable_path_for_report_type(:codequality))
end
end
end
end
end
......@@ -10,6 +10,7 @@ RSpec.describe 'Commits' do
before do
sign_in(user)
stub_ci_pipeline_to_return_yaml_file
stub_feature_flags(pipeline_tabs_vue: false)
end
let(:creator) { create(:user, developer_projects: [project]) }
......@@ -93,6 +94,7 @@ RSpec.describe 'Commits' do
context 'Download artifacts', :js do
before do
stub_feature_flags(pipeline_tabs_vue: false)
create(:ci_job_artifact, :archive, file: artifacts_file, job: build)
end
......@@ -122,6 +124,7 @@ RSpec.describe 'Commits' do
context "when logged as reporter", :js do
before do
stub_feature_flags(pipeline_tabs_vue: false)
project.add_reporter(user)
create(:ci_job_artifact, :archive, file: artifacts_file, job: build)
visit builds_project_pipeline_path(project, pipeline)
......
......@@ -15,6 +15,7 @@ RSpec.describe 'Pipeline', :js do
before do
sign_in(user)
project.add_role(user, role)
stub_feature_flags(pipeline_tabs_vue: false)
end
shared_context 'pipeline builds' do
......@@ -356,6 +357,7 @@ RSpec.describe 'Pipeline', :js do
context 'page tabs' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
visit_pipeline
end
......@@ -388,6 +390,7 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, :with_test_reports, :with_report_results, project: project) }
before do
stub_feature_flags(pipeline_tabs_vue: false)
visit_pipeline
wait_for_requests
end
......@@ -924,6 +927,7 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
before do
stub_feature_flags(pipeline_tabs_vue: false)
visit builds_project_pipeline_path(project, pipeline)
end
......@@ -944,6 +948,10 @@ RSpec.describe 'Pipeline', :js do
end
context 'page tabs' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
it 'shows Pipeline, Jobs and DAG tabs with link' do
expect(page).to have_link('Pipeline')
expect(page).to have_link('Jobs')
......@@ -1014,6 +1022,10 @@ RSpec.describe 'Pipeline', :js do
end
describe 'GET /:project/-/pipelines/:id/failures' do
before do
stub_feature_flags(pipeline_tabs_vue: false)
end
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: '1234') }
let(:pipeline_failures_page) { failures_project_pipeline_path(project, pipeline) }
let!(:failed_build) { create(:ci_build, :failed, pipeline: pipeline) }
......@@ -1139,6 +1151,7 @@ RSpec.describe 'Pipeline', :js do
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
before do
stub_feature_flags(pipeline_tabs_vue: false)
visit dag_project_pipeline_path(project, pipeline)
end
......
......@@ -623,6 +623,7 @@ RSpec.describe 'Pipelines', :js do
create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3, ref: 'master')
stub_feature_flags(pipeline_tabs_vue: false)
visit project_pipeline_path(project, pipeline)
wait_for_requests
end
......
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import PipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import Dag from '~/pipelines/components/dag/dag.vue';
import JobsApp from '~/pipelines/components/jobs/jobs_app.vue';
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
describe('The Pipeline Tabs', () => {
let wrapper;
const findDagTab = () => wrapper.findByTestId('dag-tab');
const findFailedJobsTab = () => wrapper.findByTestId('failed-jobs-tab');
const findJobsTab = () => wrapper.findByTestId('jobs-tab');
const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
const findTestsTab = () => wrapper.findByTestId('tests-tab');
const findDagApp = () => wrapper.findComponent(Dag);
const findFailedJobsApp = () => wrapper.findComponent(JobsApp);
const findJobsApp = () => wrapper.findComponent(JobsApp);
const findPipelineApp = () => wrapper.findComponent(PipelineGraphWrapper);
const findTestsApp = () => wrapper.findComponent(TestReports);
const createComponent = (propsData = {}) => {
wrapper = extendedWrapper(
shallowMount(PipelineTabs, {
propsData,
stubs: {
Dag: { template: '<div id="dag"/>' },
JobsApp: { template: '<div class="jobs" />' },
PipelineGraph: { template: '<div id="graph" />' },
TestReports: { template: '<div id="tests" />' },
},
}),
);
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
// The failed jobs MUST be removed from here and tested individually once
// the logic for the tab is implemented.
describe('Tabs', () => {
it.each`
tabName | tabComponent | appComponent
${'Pipeline'} | ${findPipelineTab} | ${findPipelineApp}
${'Dag'} | ${findDagTab} | ${findDagApp}
${'Jobs'} | ${findJobsTab} | ${findJobsApp}
${'Failed Jobs'} | ${findFailedJobsTab} | ${findFailedJobsApp}
${'Tests'} | ${findTestsTab} | ${findTestsApp}
`('shows $tabName tab and its associated component', ({ appComponent, tabComponent }) => {
expect(tabComponent().exists()).toBe(true);
expect(appComponent().exists()).toBe(true);
});
});
});
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::PipelineHelper do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:raw_pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user)}
describe '#js_pipeline_tabs_data' do
subject(:pipeline_tabs_data) { helper.js_pipeline_tabs_data(project, pipeline) }
it 'returns pipeline tabs data' do
expect(pipeline_tabs_data).to include({
can_generate_codequality_reports: pipeline.can_generate_codequality_reports?.to_json,
graphql_resource_etag: graphql_etag_pipeline_path(pipeline),
metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json),
pipeline_project_path: project.full_path
})
end
end
end
......@@ -13,6 +13,7 @@ RSpec.describe 'projects/pipelines/show' do
before do
assign(:project, project)
assign(:pipeline, presented_pipeline)
stub_feature_flags(pipeline_tabs_vue: false)
end
context 'when pipeline has errors' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment