Commit f6233515 authored by Savas Vedova's avatar Savas Vedova

Merge branch '328422-vsa-stage-id-mismatch-in-specs' into 'master'

VSA Stage ID mismatch in specs [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!66358
parents 949caf37 18e04efc
......@@ -17,13 +17,14 @@ import {
rawStageMedians,
createdBefore,
createdAfter,
deepCamelCase,
} from 'jest/cycle_analytics/mock_data';
import {
PAGINATION_TYPE,
PAGINATION_SORT_DIRECTION_DESC,
PAGINATION_SORT_FIELD_END_EVENT,
} from '~/cycle_analytics/constants';
import { transformStagesForPathNavigation } from '~/cycle_analytics/utils';
import { transformStagesForPathNavigation, formatMedianValues } from '~/cycle_analytics/utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
......@@ -105,8 +106,6 @@ export const stagingStage = getStageByTitle(dummyState.stages, 'staging');
export const allowedStages = [issueStage, planStage, codeStage];
const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
const stageFixtures = defaultStages.reduce((acc, stage) => {
const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
return {
......@@ -115,43 +114,29 @@ const stageFixtures = defaultStages.reduce((acc, stage) => {
};
}, {});
export const stageMedians = rawStageMedians.reduce(
(acc, { id, value }) => ({
...acc,
[id]: value,
const getStageId = (name) => {
const { id } = getStageByTitle(dummyState.stages, name);
return id;
};
export const stageMediansWithNumericIds = formatMedianValues(
rawStageMedians.map(({ id: name, ...rest }) => {
const id = getStageId(name);
return { ...rest, name, id };
}),
{},
);
export const stageMediansWithNumericIds = rawStageMedians.reduce((acc, { id, value }) => {
const { id: stageId } = getStageByTitle(dummyState.stages, id);
return {
...acc,
[stageId]: value,
};
}, {});
export const rawStageCounts = defaultStages.map((id) => ({
id,
...getJSONFixture(fixtureEndpoints.stageCount(id)),
}));
// Once https://gitlab.com/gitlab-org/gitlab/-/issues/328422 is fixed
// we should be able to use the rawStageCounts for building
// the stage counts mock data
/*
export const stageCounts = rawStageCounts.reduce(
(acc, { id, value }) => ({
export const stageCounts = rawStageCounts.reduce((acc, { id: name, count }) => {
const id = getStageId(name);
return {
...acc,
[id]: value,
}),
{},
);
*/
export const stageCounts = rawStageMedians.reduce((acc, { id, value }) => {
const { id: stageId } = getStageByTitle(dummyState.stages, id);
return { ...acc, [stageId]: value };
[id]: count,
};
}, {});
export const issueEvents = deepCamelCase(stageFixtures.issue);
......@@ -172,7 +157,7 @@ export const rawCustomStage = {
end_event_identifier: 'issue_first_added_to_board',
};
export const medians = stageMedians;
export const medians = stageMediansWithNumericIds;
export const rawCustomStageEvents = customizableStagesAndEvents.events;
export const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
......
......@@ -12,7 +12,7 @@ import {
import {
allowedStages,
issueStage,
stageMedians,
stageMediansWithNumericIds,
stageCounts,
basePaginationResult,
initialPaginationState,
......@@ -212,7 +212,7 @@ describe('Value Stream Analytics getters', () => {
it('returns the transformed data', () => {
state = {
stages: allowedStages,
medians: stageMedians,
medians: stageMediansWithNumericIds,
selectedStage: issueStage,
stageCounts,
};
......
......@@ -8,8 +8,9 @@ import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue';
import { NOT_ENOUGH_DATA_ERROR } from '~/cycle_analytics/constants';
import initState from '~/cycle_analytics/store/state';
import { selectedStage, convertedEvents as selectedStageEvents } from './mock_data';
import { selectedStage, issueEvents } from './mock_data';
const selectedStageEvents = issueEvents.events;
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
......
import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import { DEFAULT_VALUE_STREAM, DEFAULT_DAYS_IN_PAST } from '~/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......@@ -6,11 +7,30 @@ import { getDateInPast } from '~/lib/utils/datetime_utility';
export const createdBefore = new Date(2019, 0, 14);
export const createdAfter = getDateInPast(createdBefore, DEFAULT_DAYS_IN_PAST);
export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep: true });
export const getStageByTitle = (stages, title) =>
stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents: 'projects/analytics/value_stream_analytics/stages',
stageEvents: (stage) => `projects/analytics/value_stream_analytics/events/${stage}`,
};
export const customizableStagesAndEvents = getJSONFixture(
fixtureEndpoints.customizableCycleAnalyticsStagesAndEvents,
);
export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
const stageFixtures = defaultStages.reduce((acc, stage) => {
const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
return {
...acc,
[stage]: events,
};
}, {});
export const summary = [
{ value: '20', title: 'New Issues' },
{ value: null, title: 'Commits' },
......@@ -109,50 +129,13 @@ export const convertedData = {
],
};
export const rawEvents = [
{
title: 'Brockfunc-1617160796',
author: {
id: 275,
name: 'VSM User4',
username: 'vsm-user-4-1617160796',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/6a6f5480ae582ba68982a34169420747?s=80&d=identicon',
web_url: 'http://gdk.test:3001/vsm-user-4-1617160796',
show_status: false,
path: '/vsm-user-4-1617160796',
},
iid: '16',
total_time: { days: 1, hours: 9 },
created_at: 'about 1 month ago',
url: 'http://gdk.test:3001/vsa-life/ror-project-vsa/-/issues/16',
short_sha: 'some_sha',
commit_url: 'some_commit_url',
},
{
title: 'Subpod-1617160796',
author: {
id: 274,
name: 'VSM User3',
username: 'vsm-user-3-1617160796',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/fde853fc3ab7dc552e649dcb4fcf5f7f?s=80&d=identicon',
web_url: 'http://gdk.test:3001/vsm-user-3-1617160796',
show_status: false,
path: '/vsm-user-3-1617160796',
},
iid: '20',
total_time: { days: 2, hours: 18 },
created_at: 'about 1 month ago',
url: 'http://gdk.test:3001/vsa-life/ror-project-vsa/-/issues/20',
},
];
export const convertedEvents = rawEvents.map((ev) =>
convertObjectPropsToCamelCase(ev, { deep: true }),
);
export const rawIssueEvents = stageFixtures.issue;
export const issueEvents = deepCamelCase(rawIssueEvents);
export const planEvents = deepCamelCase(stageFixtures.plan);
export const reviewEvents = deepCamelCase(stageFixtures.review);
export const codeEvents = deepCamelCase(stageFixtures.code);
export const testEvents = deepCamelCase(stageFixtures.test);
export const stagingEvents = deepCamelCase(stageFixtures.staging);
export const pathNavIssueMetric = 172800;
......@@ -251,228 +234,8 @@ export const selectedProjects = [
},
];
export const rawValueStreamStages = [
{
title: 'Issue',
hidden: false,
legend: '',
description: 'Time before an issue gets scheduled',
id: 'issue',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:13" dir="auto"\u003eIssue created\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
},
{
title: 'Plan',
hidden: false,
legend: '',
description: 'Time before an issue starts implementation',
id: 'plan',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:71" dir="auto"\u003eIssue first associated with a milestone or issue first added to a board\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
},
{
title: 'Code',
hidden: false,
legend: '',
description: 'Time until first merge request',
id: 'code',
custom: false,
start_event_html_description:
'\u003cp data-sourcepos="1:1-1:33" dir="auto"\u003eIssue first mentioned in a commit\u003c/p\u003e',
end_event_html_description:
'\u003cp data-sourcepos="1:1-1:21" dir="auto"\u003eMerge request created\u003c/p\u003e',
},
];
export const rawValueStreamStages = customizableStagesAndEvents.stages;
export const valueStreamStages = rawValueStreamStages.map((s) =>
convertObjectPropsToCamelCase(s, { deep: true }),
);
// Temporary workaronud until we have relevant backend fixtures endpoints
export const testEvents = [
{
name: 'test',
id: 53,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/53',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
{
name: 'test',
id: 54,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/54',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
];
export const stagingEvents = [
{
name: 'test',
id: 83,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/83',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
{
name: 'test',
id: 84,
branch: {
name: 'master',
url: 'http://localhost/group3/project9/-/tree/master',
},
shortSha: 'b83d6e39',
author: {
id: 18,
name: 'John Doe21',
username: 'user12',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/70a85d1042e02066f7451ae831689be0?s=80&d=identicon',
webUrl: 'http://localhost/user12',
showStatus: false,
path: '/user12',
},
date: 'about 1 hour ago',
totalTime: { mins: 2 },
url: 'http://localhost/group3/project9/-/jobs/84',
commitUrl: 'http://localhost/group3/project9/-/commit/b83d6e391c22777fca1ed3012fce84f633d7fed0',
},
];
export const reviewEvents = [
{
title: 'My title 98',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '3',
totalTime: { days: 15 },
createdAt: '20 days ago',
url: 'http://localhost/group3/project9/-/merge_requests/3',
state: 'opened',
},
{
title: 'My title 99',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '4',
totalTime: { days: 9 },
createdAt: '19 days ago',
url: 'http://localhost/group3/project9/-/merge_requests/4',
state: 'opened',
},
];
export const issueEvents = [
{
title: 'My title 24',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '3',
totalTime: { days: 2 },
createdAt: '4 days ago',
url: 'http://localhost/group3/project9/-/issues/3',
},
{
title: 'My title 23',
author: {
id: 17,
name: 'John Doe20',
username: 'user11',
state: 'active',
avatarUrl:
'https://www.gravatar.com/avatar/fb32cf62136a195ec4f40ec6d1cfffdc?s=80&d=identicon',
webUrl: 'http://localhost/user11',
showStatus: false,
path: '/user11',
},
iid: '2',
totalTime: { days: 2 },
createdAt: '5 days ago',
url: 'http://localhost/group3/project9/-/issues/2',
},
];
......@@ -21,16 +21,23 @@ let trackingSpy = null;
const noDataSvgPath = 'path/to/no/data';
const emptyStateTitle = 'Too much data';
const notEnoughDataError = "We don't have enough data to show this stage.";
const [firstIssueEvent] = issueEvents;
const [firstStagingEvent] = stagingEvents;
const [firstTestEvent] = testEvents;
const [firstReviewEvent] = reviewEvents;
const issueEventItems = issueEvents.events;
const stagingEventItems = stagingEvents.events;
const testEventItems = testEvents.events;
const reviewEventItems = reviewEvents.events;
const [firstIssueEvent] = issueEventItems;
const [firstStagingEvent] = stagingEventItems;
const [firstTestEvent] = testEventItems;
const [firstReviewEvent] = reviewEventItems;
const pagination = { page: 1, hasNextPage: true };
const findStageEvents = () => wrapper.findAllByTestId('vsa-stage-event');
const findPagination = () => wrapper.findByTestId('vsa-stage-pagination');
const findTable = () => wrapper.findComponent(GlTable);
const findTableHead = () => wrapper.find('thead');
const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
function createComponent(props = {}, shallow = false) {
const func = shallow ? shallowMount : mount;
......@@ -38,7 +45,7 @@ function createComponent(props = {}, shallow = false) {
func(StageTable, {
propsData: {
isLoading: false,
stageEvents: issueEvents,
stageEvents: issueEventItems,
noDataSvgPath,
selectedStage: issueStage,
pagination,
......@@ -64,10 +71,10 @@ describe('StageTable', () => {
it('will render the correct events', () => {
const evs = findStageEvents();
expect(evs).toHaveLength(issueEvents.length);
expect(evs).toHaveLength(issueEventItems.length);
const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
issueEvents.forEach((ev, index) => {
issueEventItems.forEach((ev, index) => {
expect(titles[index]).toBe(ev.title);
});
});
......@@ -84,10 +91,10 @@ describe('StageTable', () => {
it('will render the correct events', () => {
const evs = findStageEvents();
expect(evs).toHaveLength(issueEvents.length);
expect(evs).toHaveLength(issueEventItems.length);
const titles = evs.wrappers.map((ev) => findStageEventTitle(ev).text());
issueEvents.forEach((ev, index) => {
issueEventItems.forEach((ev, index) => {
expect(titles[index]).toBe(ev.title);
});
});
......@@ -106,19 +113,20 @@ describe('StageTable', () => {
});
it('will set the workflow title to "Issues"', () => {
expect(wrapper.find('thead').text()).toContain('Issues');
expect(findTableHead().text()).toContain('Issues');
});
it('does not render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(false);
expect(findIcon('fork').exists()).toBe(false);
});
it('does not render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(false);
expect(findIcon('commit').exists()).toBe(false);
});
it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 days');
const createdAt = firstIssueEvent.createdAt.replace(' ago', '');
expect(findStageTime().text()).toBe(createdAt);
});
it('will render the author', () => {
......@@ -143,8 +151,8 @@ describe('StageTable', () => {
});
it('will set the workflow title to "Merge requests"', () => {
expect(wrapper.find('thead').text()).toContain('Merge requests');
expect(wrapper.find('thead').text()).not.toContain('Issues');
expect(findTableHead().text()).toContain('Merge requests');
expect(findTableHead().text()).not.toContain('Issues');
});
});
......@@ -157,8 +165,8 @@ describe('StageTable', () => {
});
it('will set the workflow title to "Deployments"', () => {
expect(wrapper.find('thead').text()).toContain('Deployments');
expect(wrapper.find('thead').text()).not.toContain('Issues');
expect(findTableHead().text()).toContain('Deployments');
expect(findTableHead().text()).not.toContain('Issues');
});
it('will not render the event title', () => {
......@@ -166,15 +174,15 @@ describe('StageTable', () => {
});
it('will render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(true);
expect(findIcon('fork').exists()).toBe(true);
});
it('will render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(true);
expect(findIcon('commit').exists()).toBe(true);
});
it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 mins');
expect(findStageTime().text()).toBe('2 mins');
});
it('will render the build shortSha', () => {
......@@ -199,8 +207,8 @@ describe('StageTable', () => {
});
it('will set the workflow title to "Jobs"', () => {
expect(wrapper.find('thead').text()).toContain('Jobs');
expect(wrapper.find('thead').text()).not.toContain('Issues');
expect(findTableHead().text()).toContain('Jobs');
expect(findTableHead().text()).not.toContain('Issues');
});
it('will not render the event title', () => {
......@@ -208,15 +216,15 @@ describe('StageTable', () => {
});
it('will render the fork icon', () => {
expect(wrapper.findByTestId('fork-icon').exists()).toBe(true);
expect(findIcon('fork').exists()).toBe(true);
});
it('will render the branch icon', () => {
expect(wrapper.findByTestId('commit-icon').exists()).toBe(true);
expect(findIcon('commit').exists()).toBe(true);
});
it('will render the total time', () => {
expect(wrapper.findByTestId('vsa-stage-event-time').text()).toBe('2 mins');
expect(findStageTime().text()).toBe('2 mins');
});
it('will render the build shortSha', () => {
......
......@@ -4,8 +4,8 @@ import * as types from '~/cycle_analytics/store/mutation_types';
import mutations from '~/cycle_analytics/store/mutations';
import {
selectedStage,
rawEvents,
convertedEvents,
rawIssueEvents,
issueEvents,
rawData,
convertedData,
selectedValueStream,
......@@ -16,6 +16,8 @@ import {
} from '../mock_data';
let state;
const rawEvents = rawIssueEvents.events;
const convertedEvents = issueEvents.events;
const mockRequestPath = 'fake/request/path';
const mockCreatedAfter = '2020-06-18';
const mockCreatedBefore = '2020-07-18';
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
include JavaScriptFixturesHelpers
let_it_be(:value_stream_id) { 'default' }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
let_it_be(:user) { create(:user, :admin) }
let_it_be(:milestone) { create(:milestone, project: project) }
let(:issue) { create(:issue, project: project, created_at: 4.days.ago) }
let(:issue_1) { create(:issue, project: project, created_at: 5.days.ago) }
let(:issue_2) { create(:issue, project: project, created_at: 4.days.ago, milestone: milestone) }
let(:issue_3) { create(:issue, project: project, created_at: 3.days.ago, milestone: milestone) }
let(:mr_1) { create(:merge_request, source_project: project, allow_broken: true, created_at: 5.days.ago) }
let(:mr_2) { create(:merge_request, source_project: project, allow_broken: true, created_at: 4.days.ago) }
let(:pipeline_1) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr_1.source_branch, sha: mr_1.source_branch_sha, head_pipeline_of: mr_1) }
let(:pipeline_2) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr_2.source_branch, sha: mr_2.source_branch_sha, head_pipeline_of: mr_2) }
let(:build_1) { create(:ci_build, :success, pipeline: pipeline_1, author: user) }
let(:build_2) { create(:ci_build, :success, pipeline: pipeline_2, author: user) }
def prepare_cycle_analytics_data
group.add_maintainer(user)
project.add_maintainer(user)
create_commit_referencing_issue(issue_1)
create_commit_referencing_issue(issue_2)
create_merge_request_closing_issue(user, project, issue_1)
create_merge_request_closing_issue(user, project, issue_2)
merge_merge_requests_closing_issue(user, project, issue_3)
end
def create_deployment
deploy_master(user, project, environment: 'staging')
deploy_master(user, project)
end
def update_metrics
issue_1.metrics.update!(first_added_to_board_at: 3.days.ago, first_mentioned_in_commit_at: 2.days.ago)
issue_2.metrics.update!(first_added_to_board_at: 2.days.ago, first_mentioned_in_commit_at: 1.day.ago)
mr_1.metrics.update!({
merged_at: 5.days.ago,
first_deployed_to_production_at: 1.day.ago,
latest_build_started_at: 5.days.ago,
latest_build_finished_at: 1.day.ago,
pipeline: build_1.pipeline
})
mr_2.metrics.update!({
merged_at: 10.days.ago,
first_deployed_to_production_at: 5.days.ago,
latest_build_started_at: 9.days.ago,
latest_build_finished_at: 7.days.ago,
pipeline: build_2.pipeline
})
end
before(:all) do
clean_frontend_fixtures('projects/analytics/value_stream_analytics/')
end
before do
stub_licensed_features(cycle_analytics_for_groups: true)
prepare_cycle_analytics_data
update_metrics
create_deployment
end
describe Projects::Analytics::CycleAnalytics::StagesController, type: :controller do
render_views
let(:params) { { namespace_id: group, project_id: project, value_stream_id: value_stream_id } }
before do
project.add_developer(user)
sign_in(user)
end
it 'projects/analytics/value_stream_analytics/stages' do
get(:index, params: params, format: :json)
expect(response).to be_successful
end
end
describe Projects::CycleAnalytics::EventsController, type: :controller do
render_views
let(:params) { { namespace_id: group, project_id: project, value_stream_id: value_stream_id } }
before do
project.add_developer(user)
sign_in(user)
end
Gitlab::Analytics::CycleAnalytics::DefaultStages.all.each do |stage|
it "projects/analytics/value_stream_analytics/events/#{stage[:name]}" do
get(stage[:name], params: params, format: :json)
expect(response).to be_successful
end
end
end
end
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