Commit 6ab56bdf authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'stop-calling-parse-twice' into 'master'

Pipeline Graph: Stop calling parseData twice

See merge request gitlab-org/gitlab!67853
parents 9fffb28d 171202e4
......@@ -39,10 +39,10 @@ export default {
required: false,
default: false,
},
pipelineLayers: {
type: Array,
computedPipelineInfo: {
type: Object,
required: false,
default: () => [],
default: () => ({}),
},
type: {
type: String,
......@@ -81,7 +81,10 @@ export default {
layout() {
return this.isStageView
? this.pipeline.stages
: generateColumnsFromLayersListMemoized(this.pipeline, this.pipelineLayers);
: generateColumnsFromLayersListMemoized(
this.pipeline,
this.computedPipelineInfo.pipelineLayers,
);
},
hasDownstreamPipelines() {
return Boolean(this.pipeline?.downstream?.length > 0);
......@@ -92,6 +95,9 @@ export default {
isStageView() {
return this.viewType === STAGE_VIEW;
},
linksData() {
return this.computedPipelineInfo?.linksData ?? null;
},
metricsConfig() {
return {
path: this.configPaths.metricsPath,
......@@ -188,6 +194,7 @@ export default {
:container-id="containerId"
:container-measurements="measurements"
:highlighted-job="hoveredJobName"
:links-data="linksData"
:metrics-config="metricsConfig"
:show-links="showJobLinks"
:view-type="viewType"
......
......@@ -9,11 +9,11 @@ import { DEFAULT, DRAW_FAILURE, LOAD_FAILURE } from '../../constants';
import DismissPipelineGraphCallout from '../../graphql/mutations/dismiss_pipeline_notification.graphql';
import getPipelineQuery from '../../graphql/queries/get_pipeline_header_data.query.graphql';
import { reportToSentry, reportMessageToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { IID_FAILURE, LAYER_VIEW, STAGE_VIEW, VIEW_TYPE_KEY } from './constants';
import PipelineGraph from './graph_component.vue';
import GraphViewSelector from './graph_view_selector.vue';
import {
calculatePipelineLayersInfo,
getQueryHeaders,
serializeLoadErrors,
toggleQueryPollingByVisibility,
......@@ -51,10 +51,10 @@ export default {
return {
alertType: null,
callouts: [],
computedPipelineInfo: null,
currentViewType: STAGE_VIEW,
canRefetchHeaderPipeline: false,
pipeline: null,
pipelineLayers: null,
showAlert: false,
showLinks: false,
};
......@@ -214,12 +214,16 @@ export default {
reportToSentry(this.$options.name, `error: ${err}, info: ${info}`);
},
methods: {
getPipelineLayers() {
if (this.currentViewType === LAYER_VIEW && !this.pipelineLayers) {
this.pipelineLayers = listByLayers(this.pipeline);
getPipelineInfo() {
if (this.currentViewType === LAYER_VIEW && !this.computedPipelineInfo) {
this.computedPipelineInfo = calculatePipelineLayersInfo(
this.pipeline,
this.$options.name,
this.metricsPath,
);
}
return this.pipelineLayers;
return this.computedPipelineInfo;
},
handleTipDismissal() {
try {
......@@ -288,7 +292,7 @@ export default {
v-if="pipeline"
:config-paths="configPaths"
:pipeline="pipeline"
:pipeline-layers="getPipelineLayers()"
:computed-pipeline-info="getPipelineInfo()"
:show-links="showLinks"
:view-type="graphViewType"
@error="reportFailure"
......
......@@ -2,10 +2,10 @@
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import { LOAD_FAILURE } from '../../constants';
import { reportToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { ONE_COL_WIDTH, UPSTREAM, LAYER_VIEW, STAGE_VIEW } from './constants';
import LinkedPipeline from './linked_pipeline.vue';
import {
calculatePipelineLayersInfo,
getQueryHeaders,
serializeLoadErrors,
toggleQueryPollingByVisibility,
......@@ -138,7 +138,11 @@ export default {
},
getPipelineLayers(id) {
if (this.viewType === LAYER_VIEW && !this.pipelineLayers[id]) {
this.pipelineLayers[id] = listByLayers(this.currentPipeline);
this.pipelineLayers[id] = calculatePipelineLayersInfo(
this.currentPipeline,
this.$options.name,
this.configPaths.metricsPath,
);
}
return this.pipelineLayers[id];
......@@ -223,7 +227,7 @@ export default {
class="d-inline-block gl-mt-n2"
:config-paths="configPaths"
:pipeline="currentPipeline"
:pipeline-layers="getPipelineLayers(pipeline.id)"
:computed-pipeline-info="getPipelineLayers(pipeline.id)"
:show-links="showLinks"
:is-linked-pipeline="true"
:view-type="graphViewType"
......
import {
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
PIPELINES_DETAIL_LINK_DURATION,
PIPELINES_DETAIL_LINKS_TOTAL,
PIPELINES_DETAIL_LINKS_JOB_RATIO,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
import { reportPerformance } from '../graph_shared/api';
export const beginPerfMeasure = () => {
performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });
};
export const finishPerfMeasureAndSend = (numLinks, numGroups, metricsPath) => {
performanceMarkAndMeasure({
mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
measures: [
{
name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
},
],
});
window.requestAnimationFrame(() => {
const duration = window.performance.getEntriesByName(
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
)[0]?.duration;
if (!duration) {
return;
}
const data = {
histograms: [
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
{
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
value: numLinks / numGroups,
},
],
};
reportPerformance(metricsPath, data);
});
};
import { isEmpty } from 'lodash';
import Visibility from 'visibilityjs';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { reportToSentry } from '../../utils';
import { listByLayers } from '../parsing_utils';
import { unwrapStagesWithNeedsAndLookup } from '../unwrapping_utils';
import { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils';
const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
return {
......@@ -10,6 +13,28 @@ const addMulti = (mainPipelineProjectPath, linkedPipeline) => {
};
};
const calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => {
const shouldCollectMetrics = Boolean(metricsPath.length);
if (shouldCollectMetrics) {
beginPerfMeasure();
}
let layers = null;
try {
layers = listByLayers(pipeline);
if (shouldCollectMetrics) {
finishPerfMeasureAndSend(layers.linksData.length, layers.numGroups, metricsPath);
}
} catch (err) {
reportToSentry(componentName, err);
}
return layers;
};
/* eslint-disable @gitlab/require-i18n-strings */
const getQueryHeaders = (etagResource) => {
return {
......@@ -106,6 +131,7 @@ const unwrapPipelineData = (mainPipelineProjectPath, data) => {
const validateConfigPaths = (value) => value.graphqlResourceEtag?.length > 0;
export {
calculatePipelineLayersInfo,
getQueryHeaders,
serializeGqlErr,
serializeLoadErrors,
......
......@@ -13,7 +13,7 @@ export const createUniqueLinkId = (stageName, jobName) => `${stageName}-${jobNam
* @returns {Array} Links that contain all the information about them
*/
export const generateLinksData = ({ links }, containerID, modifier = '') => {
export const generateLinksData = (links, containerID, modifier = '') => {
const containerEl = document.getElementById(containerID);
return links.map((link) => {
......
......@@ -17,8 +17,8 @@ export default {
type: Object,
required: true,
},
parsedData: {
type: Object,
linksData: {
type: Array,
required: true,
},
pipelineId: {
......@@ -95,7 +95,7 @@ export default {
highlightedJobs(jobs) {
this.$emit('highlightedJobsChange', jobs);
},
parsedData() {
linksData() {
this.calculateLinkData();
},
viewType() {
......@@ -112,7 +112,7 @@ export default {
reportToSentry(this.$options.name, `error: ${err}, info: ${info}`);
},
mounted() {
if (!isEmpty(this.parsedData)) {
if (!isEmpty(this.linksData)) {
this.calculateLinkData();
}
},
......@@ -122,7 +122,7 @@ export default {
},
calculateLinkData() {
try {
this.links = generateLinksData(this.parsedData, this.containerId, `-${this.pipelineId}`);
this.links = generateLinksData(this.linksData, this.containerId, `-${this.pipelineId}`);
} catch (err) {
this.$emit('error', { type: DRAW_FAILURE, reportToSentry: false });
reportToSentry(this.$options.name, err);
......
<script>
import { isEmpty } from 'lodash';
import { __ } from '~/locale';
import {
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
PIPELINES_DETAIL_LINK_DURATION,
PIPELINES_DETAIL_LINKS_TOTAL,
PIPELINES_DETAIL_LINKS_JOB_RATIO,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
import { memoize } from 'lodash';
import { reportToSentry } from '../../utils';
import { parseData } from '../parsing_utils';
import { reportPerformance } from './api';
import LinksInner from './links_inner.vue';
const parseForLinksBare = (pipeline) => {
const arrayOfJobs = pipeline.flatMap(({ groups }) => groups);
return parseData(arrayOfJobs).links;
};
const parseForLinks = memoize(parseForLinksBare);
export default {
name: 'LinksLayer',
components: {
......@@ -29,10 +25,10 @@ export default {
type: Array,
required: true,
},
metricsConfig: {
type: Object,
linksData: {
type: Array,
required: false,
default: () => ({}),
default: () => [],
},
showLinks: {
type: Boolean,
......@@ -40,30 +36,16 @@ export default {
default: true,
},
},
data() {
return {
alertDismissed: false,
parsedData: {},
showLinksOverride: false,
};
},
i18n: {
showLinksAnyways: __('Show links anyways'),
tooManyJobs: __(
'This graph has a large number of jobs and showing the links between them may have performance implications.',
),
},
computed: {
containerZero() {
return !this.containerMeasurements.width || !this.containerMeasurements.height;
},
numGroups() {
return this.pipelineData.reduce((acc, { groups }) => {
return acc + Number(groups.length);
}, 0);
},
shouldCollectMetrics() {
return this.metricsConfig.collectMetrics && this.metricsConfig.path;
getLinksData() {
if (this.linksData.length > 0) {
return this.linksData;
}
return parseForLinks(this.pipelineData);
},
showLinkedLayers() {
return this.showLinks && !this.containerZero;
......@@ -72,77 +54,14 @@ export default {
errorCaptured(err, _vm, info) {
reportToSentry(this.$options.name, `error: ${err}, info: ${info}`);
},
mounted() {
if (!isEmpty(this.pipelineData)) {
window.requestAnimationFrame(() => {
this.prepareLinkData();
});
}
},
methods: {
beginPerfMeasure() {
if (this.shouldCollectMetrics) {
performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });
}
},
finishPerfMeasureAndSend(numLinks) {
if (this.shouldCollectMetrics) {
performanceMarkAndMeasure({
mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,
measures: [
{
name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,
},
],
});
}
window.requestAnimationFrame(() => {
const duration = window.performance.getEntriesByName(
PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,
)[0]?.duration;
if (!duration) {
return;
}
const data = {
histograms: [
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
{
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
value: numLinks / this.numGroups,
},
],
};
reportPerformance(this.metricsConfig.path, data);
});
},
prepareLinkData() {
this.beginPerfMeasure();
let numLinks;
try {
const arrayOfJobs = this.pipelineData.flatMap(({ groups }) => groups);
this.parsedData = parseData(arrayOfJobs);
numLinks = this.parsedData.links.length;
} catch (err) {
reportToSentry(this.$options.name, err);
}
this.finishPerfMeasureAndSend(numLinks);
},
},
};
</script>
<template>
<links-inner
v-if="showLinkedLayers"
:container-measurements="containerMeasurements"
:parsed-data="parsedData"
:links-data="getLinksData"
:pipeline-data="pipelineData"
:total-groups="numGroups"
v-bind="$attrs"
v-on="$listeners"
>
......
......@@ -175,7 +175,7 @@ export const listByLayers = ({ stages }) => {
const parsedData = parseData(arrayOfJobs);
const dataWithLayers = createSankey()(parsedData);
return dataWithLayers.nodes.reduce((acc, { layer, name }) => {
const pipelineLayers = dataWithLayers.nodes.reduce((acc, { layer, name }) => {
/* sort groups by layer */
if (!acc[layer]) {
......@@ -186,6 +186,12 @@ export const listByLayers = ({ stages }) => {
return acc;
}, []);
return {
linksData: parsedData.links,
numGroups: arrayOfJobs.length,
pipelineLayers,
};
};
export const generateColumnsFromLayersListBare = ({ stages, stagesLookup }, pipelineLayers) => {
......
......@@ -30587,9 +30587,6 @@ msgstr ""
msgid "Show latest version"
msgstr ""
msgid "Show links anyways"
msgstr ""
msgid "Show list"
msgstr ""
......@@ -33930,9 +33927,6 @@ msgstr ""
msgid "This field is required."
msgstr ""
msgid "This graph has a large number of jobs and showing the links between them may have performance implications."
msgstr ""
msgid "This group"
msgstr ""
......
......@@ -4,8 +4,8 @@ import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import JobItem from '~/pipelines/components/graph/job_item.vue';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import { calculatePipelineLayersInfo } from '~/pipelines/components/graph/utils';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import { listByLayers } from '~/pipelines/components/parsing_utils';
import {
generateResponse,
mockPipelineResponse,
......@@ -150,7 +150,7 @@ describe('graph component', () => {
},
props: {
viewType: LAYER_VIEW,
pipelineLayers: listByLayers(defaultProps.pipeline),
computedPipelineInfo: calculatePipelineLayersInfo(defaultProps.pipeline, 'layer', ''),
},
});
});
......
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
import axios from '~/lib/utils/axios_utils';
import {
PIPELINES_DETAIL_LINK_DURATION,
PIPELINES_DETAIL_LINKS_TOTAL,
PIPELINES_DETAIL_LINKS_JOB_RATIO,
} from '~/performance/constants';
import * as perfUtils from '~/performance/utils';
import {
IID_FAILURE,
LAYER_VIEW,
......@@ -16,9 +24,11 @@ import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import * as Api from '~/pipelines/components/graph_shared/api';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
import * as sentryUtils from '~/pipelines/utils';
import { mockRunningPipelineHeaderData } from '../mock_data';
import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
......@@ -480,4 +490,112 @@ describe('Pipeline graph wrapper', () => {
});
});
});
describe('performance metrics', () => {
const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json';
let markAndMeasure;
let reportToSentry;
let reportPerformance;
let mock;
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb());
markAndMeasure = jest.spyOn(perfUtils, 'performanceMarkAndMeasure');
reportToSentry = jest.spyOn(sentryUtils, 'reportToSentry');
reportPerformance = jest.spyOn(Api, 'reportPerformance');
});
describe('with no metrics path', () => {
beforeEach(async () => {
createComponentWithApollo();
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('is not called', () => {
expect(markAndMeasure).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
});
});
describe('with metrics path', () => {
const duration = 875;
const numLinks = 7;
const totalGroups = 8;
const metricsData = {
histograms: [
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
{
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
value: numLinks / totalGroups,
},
],
};
describe('when no duration is obtained', () => {
beforeEach(async () => {
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
return [];
});
createComponentWithApollo({
provide: {
metricsPath,
glFeatures: {
pipelineGraphLayersView: true,
},
},
data: {
currentViewType: LAYER_VIEW,
},
});
jest.runOnlyPendingTimers();
await wrapper.vm.$nextTick();
});
it('attempts to collect metrics', () => {
expect(markAndMeasure).toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
});
});
describe('with duration and no error', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onPost(metricsPath).reply(200, {});
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
return [{ duration }];
});
createComponentWithApollo({
provide: {
metricsPath,
glFeatures: {
pipelineGraphLayersView: true,
},
},
data: {
currentViewType: LAYER_VIEW,
},
});
});
afterEach(() => {
mock.restore();
});
it('it calls reportPerformance with expected arguments', () => {
expect(markAndMeasure).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
expect(reportToSentry).not.toHaveBeenCalled();
});
});
});
});
});
......@@ -31,7 +31,7 @@ describe('Links Inner component', () => {
propsData: {
...defaultProps,
...props,
parsedData: parseData(currentPipelineData.flatMap(({ groups }) => groups)),
linksData: parseData(currentPipelineData.flatMap(({ groups }) => groups)).links,
},
});
};
......
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import {
PIPELINES_DETAIL_LINK_DURATION,
PIPELINES_DETAIL_LINKS_TOTAL,
PIPELINES_DETAIL_LINKS_JOB_RATIO,
} from '~/performance/constants';
import * as perfUtils from '~/performance/utils';
import * as Api from '~/pipelines/components/graph_shared/api';
import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as sentryUtils from '~/pipelines/utils';
import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
describe('links layer component', () => {
......@@ -94,139 +84,4 @@ describe('links layer component', () => {
expect(findLinksInner().exists()).toBe(false);
});
});
describe('performance metrics', () => {
const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json';
let markAndMeasure;
let reportToSentry;
let reportPerformance;
let mock;
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb());
markAndMeasure = jest.spyOn(perfUtils, 'performanceMarkAndMeasure');
reportToSentry = jest.spyOn(sentryUtils, 'reportToSentry');
reportPerformance = jest.spyOn(Api, 'reportPerformance');
});
describe('with no metrics config object', () => {
beforeEach(() => {
createComponent();
});
it('is not called', () => {
expect(markAndMeasure).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
});
});
describe('with metrics config set to false', () => {
beforeEach(() => {
createComponent({
props: {
metricsConfig: {
collectMetrics: false,
metricsPath: '/path/to/metrics',
},
},
});
});
it('is not called', () => {
expect(markAndMeasure).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
});
});
describe('with no metrics path', () => {
beforeEach(() => {
createComponent({
props: {
metricsConfig: {
collectMetrics: true,
metricsPath: '',
},
},
});
});
it('is not called', () => {
expect(markAndMeasure).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
});
});
describe('with metrics path and collect set to true', () => {
const duration = 875;
const numLinks = 7;
const totalGroups = 8;
const metricsData = {
histograms: [
{ name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
{ name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
{
name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
value: numLinks / totalGroups,
},
],
};
describe('when no duration is obtained', () => {
beforeEach(() => {
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
return [];
});
createComponent({
props: {
metricsConfig: {
collectMetrics: true,
path: metricsPath,
},
},
});
});
it('attempts to collect metrics', () => {
expect(markAndMeasure).toHaveBeenCalled();
expect(reportPerformance).not.toHaveBeenCalled();
expect(reportToSentry).not.toHaveBeenCalled();
});
});
describe('with duration and no error', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onPost(metricsPath).reply(200, {});
jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
return [{ duration }];
});
createComponent({
props: {
metricsConfig: {
collectMetrics: true,
path: metricsPath,
},
},
});
});
afterEach(() => {
mock.restore();
});
it('it calls reportPerformance with expected arguments', () => {
expect(markAndMeasure).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
expect(reportToSentry).not.toHaveBeenCalled();
});
});
});
});
});
......@@ -120,8 +120,8 @@ describe('DAG visualization parsing utilities', () => {
describe('generateColumnsFromLayersList', () => {
const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo');
const layers = listByLayers(pipeline);
const columns = generateColumnsFromLayersListBare(pipeline, layers);
const { pipelineLayers } = listByLayers(pipeline);
const columns = generateColumnsFromLayersListBare(pipeline, pipelineLayers);
it('returns stage-like objects with default name, id, and status', () => {
columns.forEach((col, idx) => {
......@@ -136,7 +136,7 @@ describe('DAG visualization parsing utilities', () => {
it('creates groups that match the list created in listByLayers', () => {
columns.forEach((col, idx) => {
const groupNames = col.groups.map(({ name }) => name);
expect(groupNames).toEqual(layers[idx]);
expect(groupNames).toEqual(pipelineLayers[idx]);
});
});
......
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