Commit 7be12de8 authored by Kushal Pandya's avatar Kushal Pandya

Remove `performance_roadmap` ff and legacy code

Changelog: changed
EE: true
parent 28e47ff8
...@@ -9,7 +9,6 @@ module Types ...@@ -9,7 +9,6 @@ module Types
DEFAULT_COMPLEXITY = 1 DEFAULT_COMPLEXITY = 1
attr_reader :deprecation, :doc_reference attr_reader :deprecation, :doc_reference
attr_writer :max_page_size # Can be removed with :performance_roadmap feature flag: https://gitlab.com/gitlab-org/gitlab/-/issues/337198
def initialize(**kwargs, &block) def initialize(**kwargs, &block)
@calls_gitaly = !!kwargs.delete(:calls_gitaly) @calls_gitaly = !!kwargs.delete(:calls_gitaly)
......
...@@ -182,7 +182,7 @@ export default { ...@@ -182,7 +182,7 @@ export default {
<current-day-indicator :preset-type="presetType" :timeframe-item="timeframeItem" /> <current-day-indicator :preset-type="presetType" :timeframe-item="timeframeItem" />
</span> </span>
</div> </div>
<gl-intersection-observer v-if="glFeatures.performanceRoadmap" @appear="handleScrolledToEnd"> <gl-intersection-observer @appear="handleScrolledToEnd">
<div <div
v-if="epicsFetchForNextPageInProgress" v-if="epicsFetchForNextPageInProgress"
class="gl-text-center gl-py-3" class="gl-text-center gl-py-3"
......
...@@ -3,15 +3,12 @@ import { GlLoadingIcon, GlAlert } from '@gitlab/ui'; ...@@ -3,15 +3,12 @@ import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { import {
EXTEND_AS,
EPICS_LIMIT_DISMISSED_COOKIE_NAME, EPICS_LIMIT_DISMISSED_COOKIE_NAME,
EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT, EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT,
DATE_RANGES, DATE_RANGES,
} from '../constants'; } from '../constants';
import eventHub from '../event_hub';
import EpicsListEmpty from './epics_list_empty.vue'; import EpicsListEmpty from './epics_list_empty.vue';
import RoadmapFilters from './roadmap_filters.vue'; import RoadmapFilters from './roadmap_filters.vue';
import RoadmapShell from './roadmap_shell.vue'; import RoadmapShell from './roadmap_shell.vue';
...@@ -31,7 +28,6 @@ export default { ...@@ -31,7 +28,6 @@ export default {
RoadmapFilters, RoadmapFilters,
RoadmapShell, RoadmapShell,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
timeframeRangeType: { timeframeRangeType: {
type: String, type: String,
...@@ -59,14 +55,11 @@ export default { ...@@ -59,14 +55,11 @@ export default {
'epics', 'epics',
'milestones', 'milestones',
'timeframe', 'timeframe',
'extendedTimeframe',
'epicsFetchInProgress', 'epicsFetchInProgress',
'epicsFetchForTimeframeInProgress',
'epicsFetchResultEmpty', 'epicsFetchResultEmpty',
'epicsFetchFailure', 'epicsFetchFailure',
'isChildEpics', 'isChildEpics',
'hasFiltersApplied', 'hasFiltersApplied',
'milestonesFetchFailure',
'filterParams', 'filterParams',
]), ]),
showFilteredSearchbar() { showFilteredSearchbar() {
...@@ -91,62 +84,7 @@ export default { ...@@ -91,62 +84,7 @@ export default {
this.fetchMilestones(); this.fetchMilestones();
}, },
methods: { methods: {
...mapActions([ ...mapActions(['fetchEpics', 'fetchMilestones']),
'fetchEpics',
'fetchEpicsForTimeframe',
'extendTimeframe',
'refreshEpicDates',
'fetchMilestones',
'refreshMilestoneDates',
]),
/**
* Once timeline is expanded (either with prepend or append)
* We need performing following actions;
*
* 1. Reset start and end edges of the timeline for
* infinite scrolling to continue further.
* 2. Re-render timeline bars to account for
* updated timeframe.
* 3. In case of prepending timeframe,
* reset scroll-position (due to DOM prepend).
*/
processExtendedTimeline({ extendAs = EXTEND_AS.PREPEND, roadmapTimelineEl, itemsCount = 0 }) {
// Re-render timeline bars with updated timeline
eventHub.$emit('refreshTimeline', {
todayBarReady: extendAs === EXTEND_AS.PREPEND,
});
if (extendAs === EXTEND_AS.PREPEND) {
// When DOM is prepended with elements
// we compensate the scrolling for added elements' width
roadmapTimelineEl.parentElement.scrollBy(
roadmapTimelineEl.querySelector('.timeline-header-item').clientWidth * itemsCount,
0,
);
}
},
handleScrollToExtend({ el: roadmapTimelineEl, extendAs = EXTEND_AS.PREPEND }) {
this.extendTimeframe({ extendAs });
this.refreshEpicDates();
this.refreshMilestoneDates();
this.$nextTick(() => {
this.fetchEpicsForTimeframe({
timeframe: this.extendedTimeframe,
})
.then(() => {
this.$nextTick(() => {
// Re-render timeline bars with updated timeline
this.processExtendedTimeline({
itemsCount: this.extendedTimeframe ? this.extendedTimeframe.length : 0,
extendAs,
roadmapTimelineEl,
});
});
})
.catch(() => {});
});
},
dismissTooManyEpicsWarning() { dismissTooManyEpicsWarning() {
Cookies.set(EPICS_LIMIT_DISMISSED_COOKIE_NAME, 'true', { Cookies.set(EPICS_LIMIT_DISMISSED_COOKIE_NAME, 'true', {
expires: EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT, expires: EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT,
...@@ -193,8 +131,6 @@ export default { ...@@ -193,8 +131,6 @@ export default {
:timeframe="timeframe" :timeframe="timeframe"
:current-group-id="currentGroupId" :current-group-id="currentGroupId"
:has-filters-applied="hasFiltersApplied" :has-filters-applied="hasFiltersApplied"
@onScrollToStart="handleScrollToExtend"
@onScrollToEnd="handleScrollToExtend"
/> />
</div> </div>
</div> </div>
......
<script> <script>
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { isInViewport } from '~/lib/utils/common_utils';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { EXTEND_AS } from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import epicsListSection from './epics_list_section.vue'; import epicsListSection from './epics_list_section.vue';
...@@ -16,7 +13,6 @@ export default { ...@@ -16,7 +13,6 @@ export default {
milestonesListSection, milestonesListSection,
roadmapTimelineSection, roadmapTimelineSection,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
presetType: { presetType: {
type: String, type: String,
...@@ -43,56 +39,16 @@ export default { ...@@ -43,56 +39,16 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
timeframeStartOffset: 0,
};
},
computed: { computed: {
...mapState(['defaultInnerHeight']), ...mapState(['defaultInnerHeight']),
displayMilestones() { displayMilestones() {
return Boolean(this.milestones.length); return Boolean(this.milestones.length);
}, },
}, },
mounted() {
this.$nextTick(() => {
// We're guarding this as in tests, `roadmapTimeline`
// is not ready when this line is executed.
if (this.$refs.roadmapTimeline) {
this.timeframeStartOffset = this.$refs.roadmapTimeline.$el
.querySelector('.timeline-header-item')
.querySelector('.item-sublabel .sublabel-value:first-child')
.getBoundingClientRect().left;
}
});
},
methods: { methods: {
handleScroll() { handleScroll() {
const { scrollTop, scrollLeft, clientHeight, scrollHeight } = this.$el; const { scrollTop, scrollLeft, clientHeight, scrollHeight } = this.$el;
if (!this.glFeatures.roadmapDaterangeFilter) {
const timelineEdgeStartEl = this.$refs.roadmapTimeline.$el
.querySelector('.timeline-header-item')
.querySelector('.item-sublabel .sublabel-value:first-child');
const timelineEdgeEndEl = this.$refs.roadmapTimeline.$el
.querySelector('.timeline-header-item:last-child')
.querySelector('.item-sublabel .sublabel-value:last-child');
// If timeline was scrolled to start
if (isInViewport(timelineEdgeStartEl, { left: this.timeframeStartOffset })) {
this.$emit('onScrollToStart', {
el: this.$refs.roadmapTimeline.$el,
extendAs: EXTEND_AS.PREPEND,
});
} else if (isInViewport(timelineEdgeEndEl)) {
// If timeline was scrolled to end
this.$emit('onScrollToEnd', {
el: this.$refs.roadmapTimeline.$el,
extendAs: EXTEND_AS.APPEND,
});
}
}
eventHub.$emit('epicsListScrolled', { scrollTop, scrollLeft, clientHeight, scrollHeight }); eventHub.$emit('epicsListScrolled', { scrollTop, scrollLeft, clientHeight, scrollHeight });
}, },
}, },
......
...@@ -81,4 +81,4 @@ export const EPICS_LIMIT_DISMISSED_COOKIE_NAME = 'epics_limit_warning_dismissed' ...@@ -81,4 +81,4 @@ export const EPICS_LIMIT_DISMISSED_COOKIE_NAME = 'epics_limit_warning_dismissed'
export const EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT = 365; export const EPICS_LIMIT_DISMISSED_COOKIE_TIMEOUT = 365;
export const ROADMAP_PAGE_SIZE = gon.features?.performanceRoadmap ? 50 : gon.roadmap_epics_limit; export const ROADMAP_PAGE_SIZE = 50;
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { EXTEND_AS, ROADMAP_PAGE_SIZE } from '../constants'; import { ROADMAP_PAGE_SIZE } from '../constants';
import epicChildEpics from '../queries/epicChildEpics.query.graphql'; import epicChildEpics from '../queries/epicChildEpics.query.graphql';
import groupEpics from '../queries/groupEpics.query.graphql'; import groupEpics from '../queries/groupEpics.query.graphql';
import groupMilestones from '../queries/groupMilestones.query.graphql'; import groupMilestones from '../queries/groupMilestones.query.graphql';
import * as epicUtils from '../utils/epic_utils'; import * as epicUtils from '../utils/epic_utils';
import * as roadmapItemUtils from '../utils/roadmap_item_utils'; import * as roadmapItemUtils from '../utils/roadmap_item_utils';
import { import { getEpicsTimeframeRange, sortEpics } from '../utils/roadmap_utils';
getEpicsTimeframeRange,
sortEpics,
extendTimeframeForPreset,
} from '../utils/roadmap_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
...@@ -185,44 +181,6 @@ export const fetchEpics = ({ state, commit, dispatch }, { endCursor } = {}) => { ...@@ -185,44 +181,6 @@ export const fetchEpics = ({ state, commit, dispatch }, { endCursor } = {}) => {
.catch(() => dispatch('receiveEpicsFailure')); .catch(() => dispatch('receiveEpicsFailure'));
}; };
export const fetchEpicsForTimeframe = ({ state, commit, dispatch }, { timeframe }) => {
commit(types.REQUEST_EPICS_FOR_TIMEFRAME);
return fetchGroupEpics(state, { timeframe })
.then(({ rawEpics, pageInfo }) => {
dispatch('receiveEpicsSuccess', {
rawEpics,
pageInfo,
newEpic: true,
timeframeExtended: true,
});
})
.catch(() => dispatch('receiveEpicsFailure'));
};
/**
* Adds more EpicItemTimeline cells to the start or end of the roadmap.
*
* @param extendAs An EXTEND_AS enum value
*/
export const extendTimeframe = ({ commit, state }, { extendAs }) => {
const isExtendTypePrepend = extendAs === EXTEND_AS.PREPEND;
const { presetType, timeframe } = state;
const timeframeToExtend = extendTimeframeForPreset({
extendAs,
presetType,
initialDate: isExtendTypePrepend
? roadmapItemUtils.timeframeStartDate(presetType, timeframe)
: roadmapItemUtils.timeframeEndDate(presetType, timeframe),
});
if (isExtendTypePrepend) {
commit(types.PREPEND_TIMEFRAME, timeframeToExtend);
} else {
commit(types.APPEND_TIMEFRAME, timeframeToExtend);
}
};
export const initItemChildrenFlags = ({ commit }, data) => export const initItemChildrenFlags = ({ commit }, data) =>
commit(types.INIT_EPIC_CHILDREN_FLAGS, data); commit(types.INIT_EPIC_CHILDREN_FLAGS, data);
...@@ -256,34 +214,6 @@ export const toggleEpic = ({ state, dispatch }, { parentItem }) => { ...@@ -256,34 +214,6 @@ export const toggleEpic = ({ state, dispatch }, { parentItem }) => {
} }
}; };
/**
* For epics that have no start or end date, this function updates their start and end dates
* so that the epic bars get longer to appear infinitely scrolling.
*/
export const refreshEpicDates = ({ commit, state }) => {
const { presetType, timeframe } = state;
const epics = state.epics.map((epic) => {
// Update child epic dates too
if (epic.children?.edges?.length > 0) {
epic.children.edges.map((childEpic) =>
roadmapItemUtils.processRoadmapItemDates(
childEpic,
roadmapItemUtils.timeframeStartDate(presetType, timeframe),
roadmapItemUtils.timeframeEndDate(presetType, timeframe),
),
);
}
return roadmapItemUtils.processRoadmapItemDates(
epic,
roadmapItemUtils.timeframeStartDate(presetType, timeframe),
roadmapItemUtils.timeframeEndDate(presetType, timeframe),
);
});
commit(types.SET_EPICS, epics);
};
export const fetchGroupMilestones = ( export const fetchGroupMilestones = (
{ fullPath, presetType, filterParams, timeframe }, { fullPath, presetType, filterParams, timeframe },
defaultTimeframe, defaultTimeframe,
......
...@@ -8,7 +8,6 @@ module Groups ...@@ -8,7 +8,6 @@ module Groups
before_action :check_epics_available! before_action :check_epics_available!
before_action :persist_roadmap_layout, only: [:show] before_action :persist_roadmap_layout, only: [:show]
before_action do before_action do
push_frontend_feature_flag(:performance_roadmap, @group, default_enabled: :yaml)
push_frontend_feature_flag(:roadmap_daterange_filter, @group, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:roadmap_daterange_filter, @group, type: :development, default_enabled: :yaml)
end end
......
# frozen_string_literal: true
module SetsMaxPageSize
extend ActiveSupport::Concern
DEPRECATED_MAX_PAGE_SIZE = 1000
# We no longer need 1000 page size after epics roadmap pagination feature is released,
# after :performance_roadmap flag rollout we can safely use default max page size(100)
# for epics, child epics and child issues without breaking current roadmaps.
#
# When removing :performance_roadmap flag delete this file and remove its method call and
# the fields using the resolver will keep using default max page size.
# Flag rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/337198
private
def set_temp_limit_for(actor)
max_page_size =
if Feature.enabled?(:performance_roadmap, actor, default_enabled: :yaml)
context.schema.default_max_page_size
else
DEPRECATED_MAX_PAGE_SIZE
end
field.max_page_size = max_page_size # rubocop: disable Graphql/Descriptions
end
end
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
module Resolvers module Resolvers
class EpicIssuesResolver < BaseResolver class EpicIssuesResolver < BaseResolver
include CachingArrayResolver include CachingArrayResolver
include SetsMaxPageSize
type Types::EpicIssueType.connection_type, null: true type Types::EpicIssueType.connection_type, null: true
...@@ -22,8 +21,6 @@ module Resolvers ...@@ -22,8 +21,6 @@ module Resolvers
end end
def query_for(id) def query_for(id)
set_temp_limit_for(epic.group) # Can be removed with :performance_roadmap feature flag: https://gitlab.com/gitlab-org/gitlab/-/issues/337198
::Epic.related_issues(ids: id) ::Epic.related_issues(ids: id)
end end
......
...@@ -5,7 +5,6 @@ module Resolvers ...@@ -5,7 +5,6 @@ module Resolvers
include TimeFrameArguments include TimeFrameArguments
include SearchArguments include SearchArguments
include LooksAhead include LooksAhead
include SetsMaxPageSize
argument :iid, GraphQL::Types::ID, argument :iid, GraphQL::Types::ID,
required: false, required: false,
...@@ -102,8 +101,6 @@ module Resolvers ...@@ -102,8 +101,6 @@ module Resolvers
end end
def find_epics(args) def find_epics(args)
set_temp_limit_for(group) # Can be removed with :performance_roadmap feature flag: https://gitlab.com/gitlab-org/gitlab/-/issues/337198
apply_lookahead(EpicsFinder.new(context[:current_user], args).execute) apply_lookahead(EpicsFinder.new(context[:current_user], args).execute)
end end
......
---
name: performance_roadmap
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65652
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337198
milestone: '14.2'
type: development
group: group::product planning
default_enabled: true
...@@ -29,7 +29,6 @@ RSpec.describe 'group epic roadmap', :js do ...@@ -29,7 +29,6 @@ RSpec.describe 'group epic roadmap', :js do
before do before do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
stub_feature_flags(unfiltered_epic_aggregates: false) stub_feature_flags(unfiltered_epic_aggregates: false)
stub_feature_flags(performance_roadmap: false)
stub_feature_flags(roadmap_daterange_filter: false) stub_feature_flags(roadmap_daterange_filter: false)
sign_in(user) sign_in(user)
......
...@@ -59,7 +59,6 @@ const createComponent = ({ ...@@ -59,7 +59,6 @@ const createComponent = ({
currentGroupId = mockGroupId, currentGroupId = mockGroupId,
presetType = PRESET_TYPES.MONTHS, presetType = PRESET_TYPES.MONTHS,
hasFiltersApplied = false, hasFiltersApplied = false,
performanceRoadmap = false,
} = {}) => { } = {}) => {
return shallowMountExtended(EpicsListSection, { return shallowMountExtended(EpicsListSection, {
localVue, localVue,
...@@ -75,11 +74,6 @@ const createComponent = ({ ...@@ -75,11 +74,6 @@ const createComponent = ({
currentGroupId, currentGroupId,
hasFiltersApplied, hasFiltersApplied,
}, },
provide: {
glFeatures: {
performanceRoadmap,
},
},
}); });
}; };
...@@ -87,7 +81,6 @@ describe('EpicsListSectionComponent', () => { ...@@ -87,7 +81,6 @@ describe('EpicsListSectionComponent', () => {
let wrapper; let wrapper;
beforeEach(() => { beforeEach(() => {
gon.features = { performanceRoadmap: false };
wrapper = createComponent(); wrapper = createComponent();
}); });
...@@ -249,6 +242,8 @@ describe('EpicsListSectionComponent', () => { ...@@ -249,6 +242,8 @@ describe('EpicsListSectionComponent', () => {
}); });
describe('template', () => { describe('template', () => {
const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
it('renders component container element with class `epics-list-section`', () => { it('renders component container element with class `epics-list-section`', () => {
expect(wrapper.classes('epics-list-section')).toBe(true); expect(wrapper.classes('epics-list-section')).toBe(true);
}); });
...@@ -263,22 +258,6 @@ describe('EpicsListSectionComponent', () => { ...@@ -263,22 +258,6 @@ describe('EpicsListSectionComponent', () => {
expect(wrapper.find('.epics-list-item-empty').exists()).toBe(true); expect(wrapper.find('.epics-list-item-empty').exists()).toBe(true);
}); });
it('renders bottom shadow element when `showBottomShadow` prop is true', () => {
wrapper.setData({
showBottomShadow: true,
});
expect(wrapper.find('.epic-scroll-bottom-shadow').exists()).toBe(true);
});
describe('when `performanceRoadmap` feature flag is enabled', () => {
const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
beforeEach(() => {
gon.features = { performanceRoadmap: true };
wrapper = createComponent({ performanceRoadmap: true });
});
it('renders gl-intersection-observer component', () => { it('renders gl-intersection-observer component', () => {
expect(findIntersectionObserver().exists()).toBe(true); expect(findIntersectionObserver().exists()).toBe(true);
}); });
...@@ -301,6 +280,13 @@ describe('EpicsListSectionComponent', () => { ...@@ -301,6 +280,13 @@ describe('EpicsListSectionComponent', () => {
expect(wrapper.findByTestId('next-page-loading').text()).toContain('Loading epics'); expect(wrapper.findByTestId('next-page-loading').text()).toContain('Loading epics');
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
}); });
it('renders bottom shadow element when `showBottomShadow` prop is true', () => {
wrapper.setData({
showBottomShadow: true,
});
expect(wrapper.find('.epic-scroll-bottom-shadow').exists()).toBe(true);
}); });
}); });
......
import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import EpicsListEmpty from 'ee/roadmap/components/epics_list_empty.vue'; import EpicsListEmpty from 'ee/roadmap/components/epics_list_empty.vue';
import RoadmapApp from 'ee/roadmap/components/roadmap_app.vue'; import RoadmapApp from 'ee/roadmap/components/roadmap_app.vue';
import RoadmapFilters from 'ee/roadmap/components/roadmap_filters.vue'; import RoadmapFilters from 'ee/roadmap/components/roadmap_filters.vue';
import RoadmapShell from 'ee/roadmap/components/roadmap_shell.vue'; import RoadmapShell from 'ee/roadmap/components/roadmap_shell.vue';
import { PRESET_TYPES, EXTEND_AS } from 'ee/roadmap/constants'; import { PRESET_TYPES } from 'ee/roadmap/constants';
import eventHub from 'ee/roadmap/event_hub';
import createStore from 'ee/roadmap/store'; import createStore from 'ee/roadmap/store';
import * as types from 'ee/roadmap/store/mutation_types'; import * as types from 'ee/roadmap/store/mutation_types';
import { getTimeframeForMonthsView } from 'ee/roadmap/utils/roadmap_utils'; import { getTimeframeForMonthsView } from 'ee/roadmap/utils/roadmap_utils';
...@@ -20,7 +18,6 @@ import { ...@@ -20,7 +18,6 @@ import {
mockSortedBy, mockSortedBy,
mockSvgPath, mockSvgPath,
mockTimeframeInitialDate, mockTimeframeInitialDate,
rawEpics,
} from 'ee_jest/roadmap/mock_data'; } from 'ee_jest/roadmap/mock_data';
describe('RoadmapApp', () => { describe('RoadmapApp', () => {
...@@ -155,71 +152,6 @@ describe('RoadmapApp', () => { ...@@ -155,71 +152,6 @@ describe('RoadmapApp', () => {
}); });
}); });
describe('extending the roadmap timeline', () => {
let roadmapTimelineEl;
beforeEach(() => {
store.dispatch('receiveEpicsSuccess', { rawEpics: rawEpics.slice(0, 2) });
wrapper = createComponent(mount);
roadmapTimelineEl = wrapper.find('.roadmap-timeline-section').element;
});
it('updates timeline by extending timeframe from the start when called with extendType as `prepend`', () => {
jest.spyOn(eventHub, '$emit').mockImplementation();
wrapper.vm.processExtendedTimeline({
extendType: EXTEND_AS.PREPEND,
roadmapTimelineEl,
itemsCount: 0,
});
expect(eventHub.$emit).toHaveBeenCalledWith('refreshTimeline', expect.any(Object));
expect(roadmapTimelineEl.parentElement.scrollBy).toHaveBeenCalled();
});
it('updates timeline by extending timeframe from the end when called with extendType as `append`', () => {
jest.spyOn(eventHub, '$emit').mockImplementation();
wrapper.vm.processExtendedTimeline({
extendType: EXTEND_AS.APPEND,
roadmapTimelineEl,
itemsCount: 0,
});
expect(eventHub.$emit).toHaveBeenCalledWith('refreshTimeline', expect.any(Object));
});
it('updates the store and refreshes roadmap with extended timeline based on provided extendType', () => {
jest.spyOn(wrapper.vm, 'extendTimeframe').mockImplementation();
jest.spyOn(wrapper.vm, 'refreshEpicDates').mockImplementation();
jest.spyOn(wrapper.vm, 'refreshMilestoneDates').mockImplementation();
jest.spyOn(wrapper.vm, 'fetchEpicsForTimeframe').mockResolvedValue();
const extendType = EXTEND_AS.PREPEND;
wrapper.vm.handleScrollToExtend({ el: roadmapTimelineEl, extendAs: extendType });
expect(wrapper.vm.extendTimeframe).toHaveBeenCalledWith({ extendAs: extendType });
expect(wrapper.vm.refreshEpicDates).toHaveBeenCalled();
expect(wrapper.vm.refreshMilestoneDates).toHaveBeenCalled();
});
it('calls `fetchEpicsForTimeframe` with extended timeframe array', async () => {
jest.spyOn(wrapper.vm, 'fetchEpicsForTimeframe').mockResolvedValue();
const extendType = EXTEND_AS.PREPEND;
wrapper.vm.handleScrollToExtend({ el: roadmapTimelineEl, extendAs: extendType });
await nextTick();
expect(wrapper.vm.fetchEpicsForTimeframe).toHaveBeenCalledWith(
expect.objectContaining({
timeframe: wrapper.vm.extendedTimeframe,
}),
);
});
});
describe('roadmap epics limit warning', () => { describe('roadmap epics limit warning', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
......
...@@ -79,12 +79,6 @@ describe('RoadmapShell', () => { ...@@ -79,12 +79,6 @@ describe('RoadmapShell', () => {
store = null; store = null;
}); });
describe('data', () => {
it('returns default data props', () => {
expect(wrapper.vm.timeframeStartOffset).toBe(0);
});
});
describe('methods', () => { describe('methods', () => {
beforeEach(() => { beforeEach(() => {
document.body.innerHTML += document.body.innerHTML +=
......
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { PRESET_TYPES, EXTEND_AS } from 'ee/roadmap/constants'; import { PRESET_TYPES } from 'ee/roadmap/constants';
import groupMilestones from 'ee/roadmap/queries/groupMilestones.query.graphql'; import groupMilestones from 'ee/roadmap/queries/groupMilestones.query.graphql';
import * as actions from 'ee/roadmap/store/actions'; import * as actions from 'ee/roadmap/store/actions';
import * as types from 'ee/roadmap/store/mutation_types'; import * as types from 'ee/roadmap/store/mutation_types';
...@@ -14,9 +14,7 @@ import { ...@@ -14,9 +14,7 @@ import {
mockGroupId, mockGroupId,
basePath, basePath,
mockTimeframeInitialDate, mockTimeframeInitialDate,
mockTimeframeMonthsPrepend,
mockTimeframeMonthsAppend, mockTimeframeMonthsAppend,
rawEpics,
mockRawEpic, mockRawEpic,
mockRawEpic2, mockRawEpic2,
mockFormattedEpic, mockFormattedEpic,
...@@ -219,104 +217,6 @@ describe('Roadmap Vuex Actions', () => { ...@@ -219,104 +217,6 @@ describe('Roadmap Vuex Actions', () => {
}); });
}); });
describe('fetchEpicsForTimeframe', () => {
describe('success', () => {
it('should perform REQUEST_EPICS_FOR_TIMEFRAME mutation and dispatch receiveEpicsSuccess action when request is successful', () => {
jest.spyOn(epicUtils.gqClient, 'query').mockReturnValue(
Promise.resolve({
data: mockGroupEpicsQueryResponse.data,
}),
);
return testAction(
actions.fetchEpicsForTimeframe,
{ timeframe: mockTimeframeMonths },
state,
[
{
type: types.REQUEST_EPICS_FOR_TIMEFRAME,
},
],
[
{
type: 'receiveEpicsSuccess',
payload: {
rawEpics: mockGroupEpics,
pageInfo: mockPageInfo,
newEpic: true,
timeframeExtended: true,
},
},
],
);
});
});
describe('failure', () => {
it('should perform REQUEST_EPICS_FOR_TIMEFRAME mutation and dispatch requestEpicsFailure action when request fails', () => {
jest.spyOn(epicUtils.gqClient, 'query').mockRejectedValue();
return testAction(
actions.fetchEpicsForTimeframe,
{ timeframe: mockTimeframeMonths },
state,
[
{
type: types.REQUEST_EPICS_FOR_TIMEFRAME,
},
],
[
{
type: 'receiveEpicsFailure',
},
],
);
});
});
});
describe('extendTimeframe', () => {
it('should prepend to timeframe when called with extend type prepend', () => {
return testAction(
actions.extendTimeframe,
{ extendAs: EXTEND_AS.PREPEND },
state,
[{ type: types.PREPEND_TIMEFRAME, payload: mockTimeframeMonthsPrepend }],
[],
);
});
it('should append to timeframe when called with extend type append', () => {
return testAction(
actions.extendTimeframe,
{ extendAs: EXTEND_AS.APPEND },
state,
[{ type: types.APPEND_TIMEFRAME, payload: mockTimeframeMonthsAppend }],
[],
);
});
});
describe('refreshEpicDates', () => {
it('should update epics after refreshing epic dates to match with updated timeframe', () => {
const epics = rawEpics.map((epic) =>
roadmapItemUtils.formatRoadmapItemDetails(
epic,
state.timeframeStartDate,
state.timeframeEndDate,
),
);
return testAction(
actions.refreshEpicDates,
{},
{ ...state, timeframe: mockTimeframeMonths.concat(mockTimeframeMonthsAppend), epics },
[{ type: types.SET_EPICS, payload: epics }],
[],
);
});
});
describe('requestChildrenEpics', () => { describe('requestChildrenEpics', () => {
const parentItemId = '41'; const parentItemId = '41';
it('should set `itemChildrenFetchInProgress` in childrenFlags for parentItem to true', () => { it('should set `itemChildrenFetchInProgress` in childrenFlags for parentItem to true', () => {
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting epic issues information' do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:project) { create(:project, group: group) }
before_all do
group.add_maintainer(user)
end
before do
stub_licensed_features(epics: true)
end
context 'when toggling performance_roadmap feature flag' do
let_it_be(:epic) { create(:epic, group: group, state: 'opened') }
let_it_be(:issue1) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project) }
let(:query) do
epic_issues_query(epic)
end
before_all do
create(:epic_issue, epic: epic, issue: issue1)
create(:epic_issue, epic: epic, issue: issue2)
end
def epic_issues_query(epic)
epic_issues_fragment = <<~EPIC_ISSUE
issues {
nodes {
id
}
}
EPIC_ISSUE
graphql_query_for(
:group,
{ 'fullPath' => epic.group.full_path },
query_graphql_field(
'epic', { iid: epic.iid },
epic_issues_fragment
)
)
end
def epic_issues_count
graphql_data_at(:group, :epic, :issues, :nodes).count
end
it 'returns epics with max page based on feature flag status' do
stub_const('SetsMaxPageSize::DEPRECATED_MAX_PAGE_SIZE', 1)
post_graphql(epic_issues_query(epic), current_user: user)
expect(epic_issues_count).to eq(2)
stub_feature_flags(performance_roadmap: false)
post_graphql(epic_issues_query(epic), current_user: user)
expect(epic_issues_count).to eq(1)
stub_feature_flags(performance_roadmap: true)
post_graphql(epic_issues_query(epic), current_user: user)
expect(epic_issues_count).to eq(2)
end
end
end
...@@ -13,30 +13,6 @@ RSpec.describe 'getting epics information' do ...@@ -13,30 +13,6 @@ RSpec.describe 'getting epics information' do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
end end
context 'when performance_roadmap flag is disabled' do
let_it_be(:epic1) { create(:epic, group: group, state: 'opened') }
let_it_be(:epic2) { create(:epic, group: group, state: 'opened') }
def epics_count
graphql_data_at(:group, :epics, :nodes).count
end
it 'returns epics within timeframe' do
stub_const('SetsMaxPageSize::DEPRECATED_MAX_PAGE_SIZE', 1)
post_graphql(epics_query_by_hash(group), current_user: user)
expect(epics_count).to eq(2)
stub_feature_flags(performance_roadmap: false)
post_graphql(epics_query_by_hash(group), current_user: user)
expect(epics_count).to eq(1)
stub_feature_flags(performance_roadmap: true)
post_graphql(epics_query_by_hash(group), current_user: user)
expect(epics_count).to eq(2)
end
end
describe 'query for epics which start with an iid' do describe 'query for epics which start with an iid' do
let_it_be(:epic1) { create(:epic, group: group, iid: 11) } let_it_be(:epic1) { create(:epic, group: group, iid: 11) }
let_it_be(:epic2) { create(:epic, group: group, iid: 22) } let_it_be(:epic2) { create(:epic, group: group, iid: 22) }
......
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