Skip to content

Commit

Permalink
fix: Workflow graph component supports updating (#1180)
Browse files Browse the repository at this point in the history
  • Loading branch information
minghay authored Oct 1, 2024
1 parent 5fb793a commit 8ae4477
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 13 deletions.
63 changes: 52 additions & 11 deletions app/components/pipeline/workflow/graph/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ import {
getElementSizes,
getGraphSvg,
getMaximumJobNameLength,
getNodeWidth
getNodeWidth,
icon
} from 'screwdriver-ui/utils/pipeline/graph/d3-graph-util';
import { nodeCanShowTooltip } from 'screwdriver-ui/utils/pipeline/graph/tooltip';

export default class PipelineWorkflowGraphComponent extends Component {
@action
draw(element) {
const data = decorateGraph({
decoratedGraph;

constructor() {
super(...arguments);

this.getDecoratedGraph();
}

getDecoratedGraph() {
this.decoratedGraph = decorateGraph({
inputGraph: this.args.workflowGraph,
builds: this.args.builds,
jobs: this.args.jobs.map(job => {
Expand All @@ -29,11 +37,14 @@ export default class PipelineWorkflowGraphComponent extends Component {
prNum: this.args.event.prNum,
stages: this.args.stages
});
}

const isSkippedEvent = isSkipped(this.args.event, this.args.builds);
@action
draw(element) {
const isSkippedEvent = isSkipped(this.args.event);
const elementSizes = getElementSizes();
const maximumJobNameLength = getMaximumJobNameLength(
data,
this.decoratedGraph,
this.args.displayJobNameLength
);
const nodeWidth = getNodeWidth(elementSizes, maximumJobNameLength);
Expand All @@ -56,7 +67,7 @@ export default class PipelineWorkflowGraphComponent extends Component {
// Add the SVG element
const svg = getGraphSvg(
element,
data,
this.decoratedGraph,
elementSizes,
maximumJobNameLength,
onClickGraph
Expand All @@ -67,7 +78,7 @@ export default class PipelineWorkflowGraphComponent extends Component {
this.args.stages.length > 0
? addStages(
svg,
data,
this.decoratedGraph,
elementSizes,
nodeWidth,
onClickStageMenu,
Expand All @@ -78,7 +89,7 @@ export default class PipelineWorkflowGraphComponent extends Component {
// edges
addEdges(
svg,
data,
this.decoratedGraph,
elementSizes,
nodeWidth,
isSkippedEvent,
Expand All @@ -88,7 +99,7 @@ export default class PipelineWorkflowGraphComponent extends Component {
// Jobs Icons
addJobIcons(
svg,
data,
this.decoratedGraph,
elementSizes,
nodeWidth,
verticalDisplacements,
Expand All @@ -98,10 +109,40 @@ export default class PipelineWorkflowGraphComponent extends Component {

addJobNames(
svg,
data,
this.decoratedGraph,
elementSizes,
maximumJobNameLength,
verticalDisplacements
);
}

@action
redraw(element) {
if (
this.decoratedGraph.nodes.length !== this.args.workflowGraph.nodes.length
) {
this.getDecoratedGraph();
element.replaceChildren();
this.draw(element);

return;
}

this.getDecoratedGraph();
this.decoratedGraph.nodes.forEach(node => {
const n = element.querySelector(`g.graph-node[data-job="${node.name}"]`);

if (n) {
const txt = n.querySelector('text');

txt.firstChild.textContent = icon(node.status);
n.setAttribute(
'class',
`graph-node${
node.status ? ` build-${node.status.toLowerCase()}` : ''
}`
);
}
});
}
}
5 changes: 4 additions & 1 deletion app/components/pipeline/workflow/graph/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
<div id="workflow-graph" {{did-insert this.draw}} />
<div id="workflow-graph"
{{did-insert this.draw}}
{{did-update this.redraw @workflowGraph @builds}}
/>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'screwdriver-ui/tests/helpers';
import { render } from '@ember/test-helpers';
import { render, rerender } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Component | pipeline/workflow/graph', function (hooks) {
Expand Down Expand Up @@ -252,4 +252,100 @@ module('Integration | Component | pipeline/workflow/graph', function (hooks) {

assert.equal(this.element.querySelector('svg').children.length, 8);
});

test('it rerenders graph when workflowGraph changes', async function (assert) {
const workflowGraph = {
nodes: [{ name: '~commit' }, { name: 'main' }],
edges: [{ src: '~commit', dest: 'main' }]
};
const workflowGraphWithDownstreamTriggers = {
nodes: [
{ name: '~commit' },
{ name: 'main' },
{ name: 'sd-main-triggers', status: 'DOWNSTREAM_TRIGGER' }
],
edges: [
{ src: '~commit', dest: 'main' },
{ src: 'main', dest: 'sd-main-triggers' }
]
};
const event = { startFrom: '~commit' };
const jobs = [{ id: 1 }];
const builds = [{ id: 1, jobId: 1, status: 'SUCCESS' }];
const stages = [];
const displayJobNameLength = 20;

this.setProperties({
workflowGraph,
event,
jobs,
builds,
stages,
displayJobNameLength
});
await render(
hbs`<Pipeline::Workflow::Graph
@workflowGraph={{this.workflowGraph}}
@event={{this.event}}
@jobs={{this.jobs}}
@builds={{this.builds}}
@stages={{this.stages}}
@chainPr={{false}}
@displayJobNameLength={{this.displayJobNameLength}}
/>`
);

assert.equal(this.element.querySelector('svg').children.length, 5);

this.setProperties({ workflowGraph: workflowGraphWithDownstreamTriggers });
await rerender();

assert.equal(this.element.querySelector('svg').children.length, 8);
});

test('it rerenders graph when builds update', async function (assert) {
const workflowGraph = {
nodes: [{ name: '~commit' }, { name: 'main', id: 123 }],
edges: [{ src: '~commit', dest: 'main' }]
};
const event = { startFrom: '~commit' };
const jobs = [{ id: 123 }];
const builds = [{ id: 1, jobId: 123, status: 'RUNNING' }];
const stages = [];
const displayJobNameLength = 20;

this.setProperties({
workflowGraph,
event,
jobs,
builds,
stages,
displayJobNameLength
});
await render(
hbs`<Pipeline::Workflow::Graph
@workflowGraph={{this.workflowGraph}}
@event={{this.event}}
@jobs={{this.jobs}}
@builds={{this.builds}}
@stages={{this.stages}}
@chainPr={{false}}
@displayJobNameLength={{this.displayJobNameLength}}
/>`
);

assert.dom('svg [data-job=main]').hasClass('build-running');

this.setProperties({
builds: [{ id: 1, jobId: 123, status: 'SUCCESS' }],
workflowGraph,
event,
jobs,
stages,
displayJobNameLength
});
await rerender();

assert.dom('svg [data-job=main]').hasClass('build-success');
});
});

0 comments on commit 8ae4477

Please sign in to comment.