Commit b15f4a3f authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '276949-pipeline-restructure-7' into 'master'

Pipeline Graph Structural Update: Polling

See merge request gitlab-org/gitlab!49825
parents 21781af2 7843602e
......@@ -69,9 +69,6 @@ export default {
},
},
methods: {
handleError(errorType) {
this.$emit('error', errorType);
},
setJob(jobName) {
this.hoveredJobName = jobName;
},
......@@ -97,7 +94,7 @@ export default {
:linked-pipelines="upstreamPipelines"
:column-title="__('Upstream')"
:type="$options.pipelineTypeConstants.UPSTREAM"
@error="handleError"
@error="emit('error', errorType)"
/>
</template>
<template #main>
......@@ -109,6 +106,7 @@ export default {
:action="stage.status.action"
:job-hovered="hoveredJobName"
:pipeline-expanded="pipelineExpanded"
@refreshPipelineGraph="$emit('refreshPipelineGraph')"
/>
</template>
<template #downstream>
......@@ -119,7 +117,7 @@ export default {
:type="$options.pipelineTypeConstants.DOWNSTREAM"
@downstreamHovered="setJob"
@pipelineExpandToggle="togglePipelineExpanded"
@error="handleError"
@error="emit('error', errorType)"
/>
</template>
</linked-graph-wrapper>
......
......@@ -4,7 +4,7 @@ import { __ } from '~/locale';
import { DEFAULT, LOAD_FAILURE } from '../../constants';
import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query.graphql';
import PipelineGraph from './graph_component.vue';
import { unwrapPipelineData } from './utils';
import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
export default {
name: 'PipelineGraphWrapper',
......@@ -35,6 +35,7 @@ export default {
apollo: {
pipeline: {
query: getPipelineDetails,
pollInterval: 10000,
variables() {
return {
projectPath: this.pipelineProjectPath,
......@@ -64,11 +65,24 @@ export default {
};
}
},
showLoadingIcon() {
/*
Shows the icon only when the graph is empty, not when it is is
being refetched, for instance, on action completion
*/
return this.$apollo.queries.pipeline.loading && !this.pipeline;
},
},
mounted() {
toggleQueryPollingByVisibility(this.$apollo.queries.pipeline);
},
methods: {
hideAlert() {
this.showAlert = false;
},
refreshPipelineGraph() {
this.$apollo.queries.pipeline.refetch();
},
reportFailure(type) {
this.showAlert = true;
this.failureType = type;
......@@ -81,7 +95,12 @@ export default {
<gl-alert v-if="showAlert" :variant="alert.variant" @dismiss="hideAlert">
{{ alert.text }}
</gl-alert>
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph v-if="pipeline" :pipeline="pipeline" @error="reportFailure" />
<gl-loading-icon v-if="showLoadingIcon" class="gl-mx-auto gl-my-4" size="lg" />
<pipeline-graph
v-if="pipeline"
:pipeline="pipeline"
@error="reportFailure"
@refreshPipelineGraph="refreshPipelineGraph"
/>
</div>
</template>
......@@ -3,7 +3,7 @@ import getPipelineDetails from '../../graphql/queries/get_pipeline_details.query
import LinkedPipeline from './linked_pipeline.vue';
import { LOAD_FAILURE } from '../../constants';
import { UPSTREAM } from './constants';
import { unwrapPipelineData } from './utils';
import { unwrapPipelineData, toggleQueryPollingByVisibility } from './utils';
export default {
components: {
......@@ -67,6 +67,7 @@ export default {
this.$apollo.addSmartQuery('currentPipeline', {
query: getPipelineDetails,
pollInterval: 10000,
variables() {
return {
projectPath,
......@@ -83,6 +84,8 @@ export default {
this.$emit('error', LOAD_FAILURE);
},
});
toggleQueryPollingByVisibility(this.$apollo.queries.currentPipeline);
},
isExpanded(id) {
return Boolean(this.currentPipeline?.id && id === this.currentPipeline.id);
......
......@@ -79,6 +79,7 @@ export default {
:tooltip-text="action.title"
:link="action.path"
class="js-stage-action stage-action rounded"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/>
</div>
</template>
......@@ -96,6 +97,7 @@ export default {
:job-hovered="jobHovered"
:pipeline-expanded="pipelineExpanded"
css-class-job-name="gl-build-content"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/>
<job-group-dropdown v-else :group="group" />
</div>
......
import Visibility from 'visibilityjs';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { unwrapStagesWithNeeds } from '../unwrapping_utils';
......@@ -40,4 +41,17 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
};
};
export { unwrapPipelineData };
const toggleQueryPollingByVisibility = (queryRef, interval = 10000) => {
const stopStartQuery = query => {
if (!Visibility.hidden()) {
query.startPolling(interval);
} else {
query.stopPolling();
}
};
stopStartQuery(queryRef);
Visibility.change(stopStartQuery.bind(null, queryRef));
};
export { unwrapPipelineData, toggleQueryPollingByVisibility };
......@@ -7,7 +7,12 @@ import { GRAPHQL } from './components/graph/constants';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
defaultClient: createDefaultClient(
{},
{
batchMax: 2,
},
),
});
const createPipelinesDetailApp = (selector, pipelineProjectPath, pipelineIid) => {
......
......@@ -44,6 +44,18 @@ describe('graph component', () => {
it('renders the main columns in the graph', () => {
expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length);
});
describe('when column requests a refresh', () => {
beforeEach(() => {
findStageColumns()
.at(0)
.vm.$emit('refreshPipelineGraph');
});
it('refreshPipelineGraph is emitted', () => {
expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
});
describe('when linked pipelines are not present', () => {
......
......@@ -108,4 +108,17 @@ describe('Pipeline graph wrapper', () => {
expect(getGraph().exists()).toBe(false);
});
});
describe('when refresh action is emitted', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.spyOn(wrapper.vm.$apollo.queries.pipeline, 'refetch');
await wrapper.vm.$nextTick();
getGraph().vm.$emit('refreshPipelineGraph');
});
it('calls refetch', () => {
expect(wrapper.vm.$apollo.queries.pipeline.refetch).toHaveBeenCalled();
});
});
});
import { mount, shallowMount } from '@vue/test-utils';
import ActionComponent from '~/pipelines/components/graph/action_component.vue';
import JobItem from '~/pipelines/components/graph/job_item.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
const mockJob = {
......@@ -37,6 +38,7 @@ describe('stage column component', () => {
const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]');
const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]');
const findJobItem = () => wrapper.find(JobItem);
const findActionComponent = () => wrapper.find(ActionComponent);
const createComponent = ({ method = shallowMount, props = {} } = {}) => {
......@@ -67,6 +69,28 @@ describe('stage column component', () => {
});
});
describe('when job notifies action is complete', () => {
beforeEach(() => {
createComponent({
method: mount,
props: {
groups: [
{
title: 'Fish',
size: 1,
jobs: [mockJob],
},
],
},
});
findJobItem().vm.$emit('pipelineActionRequestComplete');
});
it('emits refreshPipelineGraph', () => {
expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
describe('job', () => {
beforeEach(() => {
createComponent({
......
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