Commit 6aa4cb5d authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch '33112-label-based-ca-stage-fixtures' into 'master'

Adding label based stage to FE fixtures

Closes #33112

See merge request gitlab-org/gitlab!22459
parents 5cc1340c 2fd86551
......@@ -8,12 +8,11 @@ module Analytics
expose :type
expose :can_be_start_event?, as: :can_be_start_event
expose :allowed_end_events
expose :label_based?, as: :label_based
private
def type
'simple'
object.label_based? ? 'label' : 'simple'
end
def can_be_start_event?
......
{
"type": "object",
"required": ["name", "identifier", "type", "can_be_start_event", "allowed_end_events", "label_based"],
"required": ["name", "identifier", "type", "can_be_start_event", "allowed_end_events"],
"properties": {
"name": {
"type": "string"
},
"identifier": {
"type": "string"
"type": "string"
},
"type": {
"type": "string" ,
"enum": ["simple"]
"type": "string",
"enum": ["simple", "label"]
},
"can_be_start_event": {
"type": "boolean"
},
"label_based": {
"type": "boolean"
},
"allowed_end_events": {
"type": "array",
"items": {
......
......@@ -9,6 +9,7 @@ exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true display
exports[`CustomStageForm Empty form Start event with events does not select events with canBeStartEvent=false for the start events dropdown 1`] = `
"<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__123\\">
<option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"merge_request_created\\">Merge request created</option>
<option value=\\"merge_request_first_deployed_to_production\\">Merge request first deployed to production</option>
......@@ -20,34 +21,18 @@ exports[`CustomStageForm Empty form Start event with events does not select even
<option value=\\"issue_closed\\">Issue closed</option>
<option value=\\"issue_first_added_to_board\\">Issue first added to a board</option>
<option value=\\"issue_first_associated_with_milestone\\">Issue first associated with a milestone</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"issue_label_added\\">Issue label was added</option>
<option value=\\"issue_label_removed\\">Issue label was removed</option>
<option value=\\"merge_request_closed\\">Merge request closed</option>
<option value=\\"merge_request_label_added\\">Merge Request label was added</option>
<option value=\\"merge_request_label_removed\\">Merge Request label was removed</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"merge_request_created\\">Merge request created</option>
<option value=\\"merge_request_first_deployed_to_production\\">Merge request first deployed to production</option>
<option value=\\"merge_request_last_build_finished\\">Merge request last build finish time</option>
<option value=\\"merge_request_last_build_started\\">Merge request last build start time</option>
<option value=\\"merge_request_merged\\">Merge request merged</option>
<option value=\\"issue_closed\\">Issue closed</option>
<option value=\\"issue_first_added_to_board\\">Issue first added to a board</option>
<option value=\\"issue_first_associated_with_milestone\\">Issue first associated with a milestone</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"issue_label_added\\">Issue label was added</option>
<option value=\\"issue_label_removed\\">Issue label was removed</option>
<option value=\\"merge_request_closed\\">Merge request closed</option>
<option value=\\"merge_request_label_added\\">Merge Request label was added</option>
<option value=\\"merge_request_label_removed\\">Merge Request label was removed</option>
<option value=\\"issue_created\\">Issue created</option>
</select>"
`;
exports[`CustomStageForm Empty form Start event with events selects events with canBeStartEvent=true for the start events dropdown 1`] = `
"<select name=\\"custom-stage-start-event\\" required=\\"required\\" aria-required=\\"true\\" class=\\"gl-form-select custom-select\\" id=\\"__BVID__95\\">
<option value=\\"\\">Select start event</option>
<option value=\\"issue_created\\">Issue created</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"merge_request_created\\">Merge request created</option>
<option value=\\"merge_request_first_deployed_to_production\\">Merge request first deployed to production</option>
......@@ -59,28 +44,11 @@ exports[`CustomStageForm Empty form Start event with events selects events with
<option value=\\"issue_closed\\">Issue closed</option>
<option value=\\"issue_first_added_to_board\\">Issue first added to a board</option>
<option value=\\"issue_first_associated_with_milestone\\">Issue first associated with a milestone</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"issue_label_added\\">Issue label was added</option>
<option value=\\"issue_label_removed\\">Issue label was removed</option>
<option value=\\"merge_request_closed\\">Merge request closed</option>
<option value=\\"merge_request_label_added\\">Merge Request label was added</option>
<option value=\\"merge_request_label_removed\\">Merge Request label was removed</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"merge_request_created\\">Merge request created</option>
<option value=\\"merge_request_first_deployed_to_production\\">Merge request first deployed to production</option>
<option value=\\"merge_request_last_build_finished\\">Merge request last build finish time</option>
<option value=\\"merge_request_last_build_started\\">Merge request last build start time</option>
<option value=\\"merge_request_merged\\">Merge request merged</option>
<option value=\\"issue_closed\\">Issue closed</option>
<option value=\\"issue_first_added_to_board\\">Issue first added to a board</option>
<option value=\\"issue_first_associated_with_milestone\\">Issue first associated with a milestone</option>
<option value=\\"issue_first_mentioned_in_commit\\">Issue first mentioned in a commit</option>
<option value=\\"issue_label_added\\">Issue label was added</option>
<option value=\\"issue_label_removed\\">Issue label was removed</option>
<option value=\\"merge_request_closed\\">Merge request closed</option>
<option value=\\"merge_request_label_added\\">Merge Request label was added</option>
<option value=\\"merge_request_label_removed\\">Merge Request label was removed</option>
<option value=\\"issue_created\\">Issue created</option>
</select>"
`;
......
......@@ -161,8 +161,9 @@ describe('CustomStageForm', () => {
});
describe('Stop event', () => {
const index = 2;
const currAllowed = startEvents[index].allowedEndEvents;
const startEventArrayIndex = 2;
const startEventDropdownIndex = 1;
const currAllowed = startEvents[startEventArrayIndex].allowedEndEvents;
beforeEach(() => {
wrapper = createComponent({}, false);
......@@ -173,7 +174,7 @@ describe('CustomStageForm', () => {
});
it('clears notification when a start event is selected', done => {
selectDropdownOption(wrapper, sel.startEvent, 1);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
Vue.nextTick(() => {
expect(wrapper.text()).not.toContain('Please select a start event first');
done();
......@@ -184,7 +185,7 @@ describe('CustomStageForm', () => {
const el = wrapper.find(sel.endEvent);
expect(el.attributes('disabled')).toEqual('disabled');
selectDropdownOption(wrapper, sel.startEvent, 1);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
Vue.nextTick(() => {
expect(el.attributes('disabled')).toBeUndefined();
done();
......@@ -193,11 +194,10 @@ describe('CustomStageForm', () => {
it('will update the list of stop events when a start event is changed', done => {
let stopOptions = wrapper.find(sel.endEvent).findAll('option');
const selectedStartEventIndex = 1;
const selectedStartEvent = startEvents[selectedStartEventIndex];
const selectedStartEvent = startEvents[startEventDropdownIndex];
expect(stopOptions.length).toEqual(1);
selectDropdownOption(wrapper, sel.startEvent, selectedStartEventIndex);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
......@@ -214,7 +214,7 @@ describe('CustomStageForm', () => {
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, index);
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
......@@ -232,7 +232,7 @@ describe('CustomStageForm', () => {
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, index);
selectDropdownOption(wrapper, sel.startEvent, startEventArrayIndex + 1);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.endEvent);
......@@ -386,13 +386,14 @@ describe('CustomStageForm', () => {
});
describe('with all fields set', () => {
const startEventIndex = 2;
const startEventDropdownIndex = 2;
const startEventArrayIndex = startEventDropdownIndex - 1;
const stopEventIndex = 1;
beforeEach(() => {
wrapper = createComponent({}, false);
selectDropdownOption(wrapper, sel.startEvent, startEventIndex);
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
return Vue.nextTick()
.then(() => {
......@@ -424,9 +425,8 @@ describe('CustomStageForm', () => {
});
it(`${STAGE_ACTIONS.CREATE} event receives the latest data`, () => {
const startEv = startEvents[startEventIndex];
const startEv = startEvents[startEventArrayIndex];
const selectedStopEvent = getDropdownOption(wrapper, sel.endEvent, stopEventIndex);
let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined();
......
import { uniq } from 'underscore';
import { TEST_HOST } from 'helpers/test_constants';
import { getJSONFixture } from 'helpers/fixtures';
import mutations from 'ee/analytics/cycle_analytics/store/mutations';
......@@ -97,31 +98,25 @@ export const medians = stageMedians;
const { events: rawCustomStageEvents } = customizableStagesAndEvents;
const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
export const customStageLabelEvents = camelCasedStageEvents.filter(ev => ev.type === 'label');
export const customStageStartEvents = camelCasedStageEvents.filter(ev => ev.canBeStartEvent);
// find get all the possible stop events
// get all the possible stop events
const allowedEndEventIds = new Set(customStageStartEvents.flatMap(e => e.allowedEndEvents));
export const customStageStopEvents = camelCasedStageEvents.filter(ev =>
allowedEndEventIds.has(ev.identifier),
);
// TODO: the shim below should be removed once we have label events seeding
// https://gitlab.com/gitlab-org/gitlab/issues/33112
export const labelStartEvent = { ...customStageStartEvents[0], type: 'label' };
const firstAllowedStopEvent = labelStartEvent.allowedEndEvents[0];
// We need to enusre that the stop event can be applied to the start event
export const labelStopEvent = {
...customStageStopEvents.find(ev => ev.identifier === firstAllowedStopEvent),
type: 'label',
};
export const customStageEvents = uniq(
[...customStageStartEvents, ...customStageStopEvents],
false,
ev => ev.identifier,
);
export const customStageEvents = [
...customStageStartEvents.filter(ev => ev.identifier !== labelStartEvent.identifier),
...customStageStopEvents.filter(ev => ev.identifier !== labelStopEvent.identifier),
labelStartEvent,
labelStopEvent,
];
export const labelStartEvent = customStageLabelEvents[0];
export const labelStopEvent = customStageLabelEvents.find(
ev => ev.identifier === labelStartEvent.allowedEndEvents[0],
);
const dateRange = getDatesInRange(startDate, endDate, toYmd);
......
......@@ -20,8 +20,8 @@ import {
import { toYmd } from 'ee/analytics/shared/utils';
import {
customStageEvents as events,
customStageLabelEvents as labelEvents,
labelStartEvent,
labelStopEvent,
customStageStartEvents as startEvents,
transformedDurationData,
transformedDurationMedianData,
......@@ -35,7 +35,7 @@ import {
transformedTasksByTypeData,
} from './mock_data';
const labelEvents = [labelStartEvent, labelStopEvent].map(i => i.identifier);
const labelEventIds = labelEvents.map(ev => ev.identifier);
describe('Cycle analytics utils', () => {
describe('isStartEvent', () => {
......@@ -54,13 +54,14 @@ describe('Cycle analytics utils', () => {
describe('isLabelEvent', () => {
it('will return true if the given event identifier is in the labelEvents array', () => {
expect(isLabelEvent(labelEvents, labelStartEvent.identifier)).toEqual(true);
expect(isLabelEvent(labelEventIds, labelStartEvent.identifier)).toEqual(true);
});
it('will return false if the given event identifier is not in the labelEvents array', () => {
[startEvents[1].identifier, null, undefined, ''].forEach(ev => {
expect(isLabelEvent(labelEvents, ev)).toEqual(false);
expect(isLabelEvent(labelEventIds, ev)).toEqual(false);
});
expect(isLabelEvent(labelEvents)).toEqual(false);
expect(isLabelEvent(labelEventIds)).toEqual(false);
});
});
......@@ -89,8 +90,7 @@ describe('Cycle analytics utils', () => {
describe('getLabelEventsIdentifiers', () => {
it('will return an array of identifiers for the label events', () => {
const res = getLabelEventsIdentifiers(events);
expect(res.length).toEqual(labelEvents.length);
expect(res).toEqual(labelEvents);
expect(res).toEqual(labelEventIds);
});
it('will return an empty array when there are no matches', () => {
......@@ -115,7 +115,7 @@ describe('Cycle analytics utils', () => {
describe('eventsByIdentifier', () => {
it('will return the events with an identifier in the provided array', () => {
expect(eventsByIdentifier(events, labelEvents)).toEqual([labelStartEvent, labelStopEvent]);
expect(eventsByIdentifier(events, labelEventIds)).toEqual(labelEvents);
});
it('will return an empty array if there are no matching events', () => {
......
......@@ -16,6 +16,8 @@ describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
let(:issue_2) { create(:issue, project: project, created_at: 4.days.ago) }
let(:issue_3) { create(:issue, project: project, created_at: 3.days.ago) }
let(:label) { create(:group_label, name: 'in-code-review', group: group) }
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
let(:mr_1) { create(:merge_request, source_project: project, allow_broken: true, created_at: 20.days.ago) }
let(:mr_2) { create(:merge_request, source_project: project, allow_broken: true, created_at: 19.days.ago) }
......@@ -29,6 +31,17 @@ describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
let(:build_2) { create(:ci_build, :success, pipeline: pipeline_2, author: user) }
let(:build_3) { create(:ci_build, :success, pipeline: pipeline_3, author: user) }
let(:label_based_stage) do
create(:cycle_analytics_group_stage, {
name: 'label-based-stage',
parent: group,
start_event_identifier: :issue_label_added,
start_event_label_id: label.id,
end_event_identifier: :issue_label_removed,
end_event_label_id: label.id
})
end
def prepare_cycle_analytics_data
group.add_maintainer(user)
project.add_maintainer(user)
......@@ -81,6 +94,32 @@ describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
deploy_master(user, project, environment: 'staging')
end
def create_label_based_cycle_analytics_stage
label_based_stage
issue = create(:issue, project: project, created_at: 20.days.ago, author: user)
Timecop.travel(5.days.ago) do
Issues::UpdateService.new(
project,
user,
label_ids: [label.id]
).execute(issue)
end
Timecop.travel(2.days.ago) do
Issues::UpdateService.new(
project,
user,
label_ids: []
).execute(issue)
end
end
around do |example|
Timecop.freeze { example.run }
end
before(:all) do
clean_frontend_fixtures('analytics/')
clean_frontend_fixtures('cycle_analytics/')
......@@ -145,6 +184,8 @@ describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
group.cycle_analytics_stages.build(params).save!
end
create_label_based_cycle_analytics_stage
prepare_cycle_analytics_data
create_deployment
......@@ -174,6 +215,18 @@ describe 'Analytics (JavaScript fixtures)', :sidekiq_inline do
expect(response).to be_successful
end
end
it "analytics/cycle_analytics/stages/label-based-stage/records.json" do
get(:records, params: params.merge({ id: label_based_stage.id }), format: :json)
expect(response).to be_successful
end
it "analytics/cycle_analytics/stages/label-based-stage/median.json" do
get(:median, params: params.merge({ id: label_based_stage.id }), format: :json)
expect(response).to be_successful
end
end
describe Analytics::CycleAnalytics::SummaryController, type: :controller do
......
# frozen_string_literal: true
require 'spec_helper'
describe Analytics::CycleAnalytics::EventEntity do
describe '#type' do
it 'returns `simple` for non-label based events' do
event = Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated
expect(described_class.new(event).as_json[:type]).to eq('simple')
end
it 'returns `label` for label based events' do
event = Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLabelAdded
expect(described_class.new(event).as_json[:type]).to eq('label')
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