Commit e2ff9db4 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Phil Hughes

Move tasks by type chart into folder

Moves the filters and charts components for
the tasks by type chart into a separate
component folder

Moves type of work into a separate component
parent 2ca22554
......@@ -10,7 +10,7 @@ import { LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants';
import DateRange from '../../shared/components/daterange.vue';
import StageTable from './stage_table.vue';
import DurationChart from './duration_chart.vue';
import TasksByTypeChart from './tasks_by_type_chart.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue';
import UrlSyncMixin from '../../shared/mixins/url_sync_mixin';
import { toYmd } from '../../shared/utils';
import RecentActivityCard from './recent_activity_card.vue';
......@@ -25,7 +25,7 @@ export default {
GroupsDropdownFilter,
ProjectsDropdownFilter,
StageTable,
TasksByTypeChart,
TypeOfWorkCharts,
RecentActivityCard,
},
mixins: [glFeatureFlagsMixin(), UrlSyncMixin],
......@@ -53,6 +53,7 @@ export default {
'isLoading',
'isLoadingStage',
'isLoadingTasksByTypeChart',
'isLoadingTasksByTypeChartTopLabels',
'isEmptyStage',
'isSavingCustomStage',
'isCreatingCustomStage',
......@@ -93,11 +94,13 @@ export default {
shouldDisplayDurationChart() {
return this.featureFlags.hasDurationChart && !this.hasNoAccessError && !this.isLoading;
},
shouldDisplayTasksByTypeChart() {
shouldDisplayTypeOfWorkCharts() {
return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError;
},
isTasksByTypeChartLoaded() {
return !this.isLoading && !this.isLoadingTasksByTypeChart;
isLoadingTypeOfWork() {
return (
this.isLoading || this.isLoadingTasksByTypeChartTopLabels || this.isLoadingTasksByTypeChart
);
},
hasDateRangeSet() {
return this.startDate && this.endDate;
......@@ -310,21 +313,14 @@ export default {
/>
</div>
</div>
<div v-if="shouldDisplayDurationChart" class="mt-3">
<duration-chart :stages="activeStages" />
</div>
<template v-if="shouldDisplayTasksByTypeChart">
<div class="js-tasks-by-type-chart">
<div v-if="isTasksByTypeChartLoaded">
<tasks-by-type-chart
:chart-data="tasksByTypeChartData"
:filters="selectedTasksByTypeFilters"
@updateFilter="setTasksByTypeFilters"
/>
</div>
<gl-loading-icon v-else size="md" class="my-4 py-4" />
</div>
</template>
<duration-chart v-if="shouldDisplayDurationChart" class="mt-3" :stages="activeStages" />
<type-of-work-charts
v-if="shouldDisplayTypeOfWorkCharts"
:is-loading="isLoadingTypeOfWork"
:tasks-by-type-chart-data="tasksByTypeChartData"
:selected-tasks-by-type-filters="selectedTasksByTypeFilters"
@updateFilter="setTasksByTypeFilters"
/>
</div>
</div>
</template>
<script>
import { GlStackedColumnChart } from '@gitlab/ui/dist/charts';
export default {
name: 'TasksByTypeChart',
components: {
GlStackedColumnChart,
},
props: {
data: {
type: Array,
required: true,
},
groupBy: {
type: Array,
required: true,
},
seriesNames: {
type: Array,
required: true,
},
},
computed: {
hasData() {
return Boolean(this.data.length);
},
},
};
</script>
<template>
<gl-stacked-column-chart
v-if="hasData"
:data="data"
:group-by="groupBy"
x-axis-type="category"
y-axis-type="value"
:x-axis-title="__('Date')"
:y-axis-title="s__('CycleAnalytics|Number of tasks')"
:series-names="seriesNames"
/>
<div v-else class="bs-callout bs-callout-info">
<p>{{ __('There is no data available. Please change your selection.') }}</p>
</div>
</template>
......@@ -2,14 +2,14 @@
import { GlDropdownDivider, GlSegmentedControl, GlIcon } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import createFlash from '~/flash';
import { removeFlash } from '../utils';
import { removeFlash } from '../../utils';
import {
TASKS_BY_TYPE_FILTERS,
TASKS_BY_TYPE_SUBJECT_ISSUE,
TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS,
TASKS_BY_TYPE_MAX_LABELS,
} from '../constants';
import LabelsSelector from './labels_selector.vue';
} from '../../constants';
import LabelsSelector from '../labels_selector.vue';
export default {
name: 'TasksByTypeFilters',
......
<script>
import { GlStackedColumnChart } from '@gitlab/ui/dist/charts';
import { GlLoadingIcon } from '@gitlab/ui';
import TasksByTypeChart from './tasks_by_type/tasks_by_type_chart.vue';
import TasksByTypeFilters from './tasks_by_type/tasks_by_type_filters.vue';
import { s__, sprintf } from '~/locale';
import { formattedDate } from '../../shared/utils';
import { TASKS_BY_TYPE_SUBJECT_ISSUE } from '../constants';
import TasksByTypeFilters from './tasks_by_type_filters.vue';
export default {
name: 'TasksByTypeChart',
components: {
GlStackedColumnChart,
TasksByTypeFilters,
},
name: 'TypeOfWorkCharts',
components: { GlLoadingIcon, TasksByTypeChart, TasksByTypeFilters },
props: {
filters: {
isLoading: {
type: Boolean,
required: true,
},
tasksByTypeChartData: {
type: Object,
required: true,
},
chartData: {
selectedTasksByTypeFilters: {
type: Object,
required: true,
},
},
computed: {
hasData() {
return Boolean(this.chartData?.data?.length);
},
summaryDescription() {
const {
startDate,
endDate,
selectedProjectIds,
selectedGroup: { name: groupName },
} = this.filters;
} = this.selectedTasksByTypeFilters;
const selectedProjectCount = selectedProjectIds.length;
const str =
......@@ -51,7 +50,7 @@ export default {
},
selectedSubjectFilter() {
const {
filters: { subject },
selectedTasksByTypeFilters: { subject },
} = this;
return subject || TASKS_BY_TYPE_SUBJECT_ISSUE;
},
......@@ -59,29 +58,21 @@ export default {
};
</script>
<template>
<div class="row">
<div class="col-12">
<div class="js-tasks-by-type-chart row">
<gl-loading-icon v-if="isLoading" size="md" class="col-12 my-4 py-4" />
<div v-else class="col-12">
<h3>{{ s__('CycleAnalytics|Type of work') }}</h3>
<div v-if="hasData">
<p>{{ summaryDescription }}</p>
<tasks-by-type-filters
:selected-label-ids="filters.selectedLabelIds"
:subject-filter="selectedSubjectFilter"
@updateFilter="$emit('updateFilter', $event)"
/>
<gl-stacked-column-chart
:data="chartData.data"
:group-by="chartData.groupBy"
x-axis-type="category"
y-axis-type="value"
:x-axis-title="__('Date')"
:y-axis-title="s__('CycleAnalytics|Number of tasks')"
:series-names="chartData.seriesNames"
/>
</div>
<div v-else class="bs-callout bs-callout-info">
<p>{{ __('There is no data available. Please change your selection.') }}</p>
</div>
<p>{{ summaryDescription }}</p>
<tasks-by-type-filters
:selected-label-ids="selectedTasksByTypeFilters.selectedLabelIds"
:subject-filter="selectedSubjectFilter"
@updateFilter="$emit('updateFilter', $event)"
/>
<tasks-by-type-chart
:data="tasksByTypeChartData.data"
:group-by="tasksByTypeChartData.groupBy"
:series-names="tasksByTypeChartData.seriesNames"
/>
</div>
</div>
</template>
......@@ -295,18 +295,12 @@ export const createCustomStage = ({ dispatch, state }, data) => {
});
};
export const receiveTasksByTypeDataSuccess = ({ commit }, data) => {
commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, data);
};
export const receiveTasksByTypeDataError = ({ commit }, error) => {
commit(types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR, error);
createFlash(__('There was an error fetching data for the tasks by type chart'));
};
export const requestTasksByTypeData = ({ commit }) => commit(types.REQUEST_TASKS_BY_TYPE_DATA);
export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
export const fetchTasksByTypeData = ({ dispatch, commit, state, getters }) => {
const {
currentGroupPath,
cycleAnalyticsRequestParams: { created_after, created_before, project_ids },
......@@ -316,6 +310,9 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
tasksByType: { subject, selectedLabelIds },
} = state;
// ensure we clear any chart data currently in state
commit(types.REQUEST_TASKS_BY_TYPE_DATA);
// dont request if we have no labels selected...for now
if (selectedLabelIds.length) {
const params = {
......@@ -326,13 +323,11 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
label_ids: selectedLabelIds,
};
dispatch('requestTasksByTypeData');
return Api.cycleAnalyticsTasksByType(currentGroupPath, params)
.then(({ data }) => dispatch('receiveTasksByTypeDataSuccess', data))
.then(({ data }) => commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, data))
.catch(error => dispatch('receiveTasksByTypeDataError', error));
}
return Promise.resolve();
return commit(types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS, []);
};
export const requestUpdateStage = ({ commit }) => commit(types.REQUEST_UPDATE_STAGE);
......@@ -403,7 +398,7 @@ export const removeStage = ({ dispatch, state }, stageId) => {
export const setTasksByTypeFilters = ({ dispatch, commit }, data) => {
commit(types.SET_TASKS_BY_TYPE_FILTERS, data);
dispatch('fetchTasksByTypeData');
dispatch('fetchTopRankedGroupLabels');
};
export const initializeCycleAnalyticsSuccess = ({ commit }) =>
......
......@@ -50,6 +50,7 @@ export default {
state.isLoadingStage = false;
},
[types.REQUEST_TOP_RANKED_GROUP_LABELS](state) {
state.isLoadingTasksByTypeChartTopLabels = true;
state.topRankedLabels = [];
state.tasksByType = {
...state.tasksByType,
......@@ -58,6 +59,7 @@ export default {
},
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS](state, data = []) {
const { tasksByType } = state;
state.isLoadingTasksByTypeChartTopLabels = false;
state.topRankedLabels = data.map(convertObjectPropsToCamelCase);
state.tasksByType = {
...tasksByType,
......@@ -66,6 +68,7 @@ export default {
},
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR](state) {
const { tasksByType } = state;
state.isLoadingTasksByTypeChartTopLabels = false;
state.topRankedLabels = [];
state.tasksByType = {
...tasksByType,
......@@ -130,7 +133,7 @@ export default {
[types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR](state) {
state.isLoadingTasksByTypeChart = false;
},
[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, data) {
[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, data = []) {
state.isLoadingTasksByTypeChart = false;
state.tasksByType = {
...state.tasksByType,
......
......@@ -9,6 +9,7 @@ export default () => ({
isLoading: false,
isLoadingStage: false,
isLoadingTasksByTypeChart: false,
isLoadingTasksByTypeChartTopLabels: false,
isEmptyStage: false,
errorCode: null,
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TasksByTypeChart no data available should render the no data available message 1`] = `
"<div class=\\"row\\">
<div class=\\"col-12\\">
<h3>Type of work</h3>
<div class=\\"bs-callout bs-callout-info\\">
<p>There is no data available. Please change your selection.</p>
</div>
</div>
</div>"
`;
exports[`TasksByTypeChart with data available should render the loading chart 1`] = `
"<div class=\\"row\\">
<div class=\\"col-12\\">
<h3>Type of work</h3>
<div>
<p>Showing data for group 'Gitlab Org' from Dec 11, 2019 to Jan 10, 2020</p>
<tasks-by-type-filters-stub selectedlabelids=\\"1,2,3\\" maxlabels=\\"15\\" subjectfilter=\\"Issue\\"></tasks-by-type-filters-stub>
<gl-stacked-column-chart-stub data=\\"0,1,2,5,2,3,2,4,1\\" option=\\"[object Object]\\" presentation=\\"stacked\\" groupby=\\"Group 1,Group 2,Group 3\\" xaxistype=\\"category\\" xaxistitle=\\"Date\\" yaxistitle=\\"Number of tasks\\" seriesnames=\\"Cool label,Normal label\\" legendaveragetext=\\"Avg\\" legendmaxtext=\\"Max\\" y-axis-type=\\"value\\"></gl-stacked-column-chart-stub>
</div>
</div>
</div>"
`;
......@@ -14,7 +14,7 @@ import 'bootstrap';
import '~/gl_dropdown';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import Daterange from 'ee/analytics/shared/components/daterange.vue';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type_chart.vue';
import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status';
import * as commonUtils from '~/lib/utils/common_utils';
......@@ -132,8 +132,8 @@ describe('Cycle Analytics component', () => {
expect(wrapper.find(DurationChart).exists()).toBe(flag);
};
const displaysTasksByType = flag => {
expect(wrapper.find(TasksByTypeChart).exists()).toBe(flag);
const displaysTypeOfWork = flag => {
expect(wrapper.find(TypeOfWorkCharts).exists()).toBe(flag);
};
beforeEach(() => {
......@@ -343,7 +343,7 @@ describe('Cycle Analytics component', () => {
});
it('does not display the tasks by type chart', () => {
displaysTasksByType(false);
displaysTypeOfWork(false);
});
it('does not display the duration chart', () => {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TasksByTypeChart no data available should render the no data available message 1`] = `
"<div class=\\"bs-callout bs-callout-info\\">
<p>There is no data available. Please change your selection.</p>
</div>"
`;
exports[`TasksByTypeChart with data available should render the loading chart 1`] = `"<gl-stacked-column-chart-stub data=\\"0,1,2,5,2,3,2,4,1\\" option=\\"[object Object]\\" presentation=\\"stacked\\" groupby=\\"Group 1,Group 2,Group 3\\" xaxistype=\\"category\\" xaxistitle=\\"Date\\" yaxistitle=\\"Number of tasks\\" seriesnames=\\"Cool label,Normal label\\" legendaveragetext=\\"Avg\\" legendmaxtext=\\"Max\\" y-axis-type=\\"value\\"></gl-stacked-column-chart-stub>"`;
import { mount, shallowMount } from '@vue/test-utils';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type_chart.vue';
import { TASKS_BY_TYPE_SUBJECT_ISSUE } from 'ee/analytics/cycle_analytics/constants';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type/tasks_by_type_chart.vue';
import { tasksByTypeData } from '../../mock_data';
const seriesNames = ['Cool label', 'Normal label'];
const data = [[0, 1, 2], [5, 2, 3], [2, 4, 1]];
const groupBy = ['Group 1', 'Group 2', 'Group 3'];
const filters = {
selectedGroup: {
id: 22,
name: 'Gitlab Org',
fullName: 'Gitlab Org',
fullPath: 'gitlab-org',
},
selectedProjectIds: [],
startDate: new Date('2019-12-11'),
endDate: new Date('2020-01-10'),
subject: TASKS_BY_TYPE_SUBJECT_ISSUE,
selectedLabelIds: [1, 2, 3],
};
const { groupBy, data, seriesNames } = tasksByTypeData;
function createComponent({ props = {}, shallow = true, stubs = {} }) {
const fn = shallow ? shallowMount : mount;
return fn(TasksByTypeChart, {
propsData: {
filters,
chartData: {
groupBy,
data,
seriesNames,
},
groupBy,
data,
seriesNames,
...props,
},
stubs: {
......@@ -60,11 +42,9 @@ describe('TasksByTypeChart', () => {
beforeEach(() => {
wrapper = createComponent({
props: {
chartData: {
groupBy: [],
data: [],
seriesNames: [],
},
groupBy: [],
data: [],
seriesNames: [],
},
});
});
......
......@@ -3,7 +3,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import { GlDropdownItem, GlSegmentedControl } from '@gitlab/ui';
import TasksByTypeFilters from 'ee/analytics/cycle_analytics/components/tasks_by_type_filters.vue';
import TasksByTypeFilters from 'ee/analytics/cycle_analytics/components/tasks_by_type/tasks_by_type_filters.vue';
import LabelsSelector from 'ee/analytics/cycle_analytics/components/labels_selector.vue';
import {
TASKS_BY_TYPE_SUBJECT_ISSUE,
......@@ -11,8 +11,8 @@ import {
TASKS_BY_TYPE_FILTERS,
} from 'ee/analytics/cycle_analytics/constants';
import waitForPromises from 'helpers/wait_for_promises';
import { shouldFlashAMessage } from '../helpers';
import { groupLabels } from '../mock_data';
import { shouldFlashAMessage } from '../../helpers';
import { groupLabels } from '../../mock_data';
import createStore from 'ee/analytics/cycle_analytics/store';
import * as getters from 'ee/analytics/cycle_analytics/store/getters';
......
import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type/tasks_by_type_chart.vue';
import TasksByTypeFilters from 'ee/analytics/cycle_analytics/components/tasks_by_type/tasks_by_type_filters.vue';
import { tasksByTypeData, taskByTypeFilters } from '../mock_data';
import {
TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
TASKS_BY_TYPE_FILTERS,
} from 'ee/analytics/cycle_analytics/constants';
describe('TypeOfWorkCharts', () => {
function createComponent({ props = {}, stubs = {} } = {}) {
return shallowMount(TypeOfWorkCharts, {
propsData: {
isLoading: false,
tasksByTypeChartData: tasksByTypeData,
selectedTasksByTypeFilters: taskByTypeFilters,
...props,
},
stubs: {
TasksByTypeChart: false,
TasksByTypeFilters: false,
...stubs,
},
});
}
let wrapper = null;
const findSubjectFilters = _wrapper => _wrapper.find(TasksByTypeFilters);
const findTasksByTypeChart = _wrapper => _wrapper.find(TasksByTypeChart);
const findLoader = _wrapper => _wrapper.find(GlLoadingIcon);
afterEach(() => {
wrapper.destroy();
});
describe('with data', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('renders the task by type chart', () => {
expect(findTasksByTypeChart(wrapper).exists()).toBe(true);
});
it('does not render the loading icon', () => {
expect(findLoader(wrapper).exists()).toBe(false);
});
});
describe('when a filter is selected', () => {
const payload = {
filter: TASKS_BY_TYPE_FILTERS.SUBJECT,
value: TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
};
beforeEach(() => {
wrapper = createComponent();
findSubjectFilters(wrapper).vm.$emit('updateFilter', payload);
return wrapper.vm.$nextTick();
});
it('emits the `updateFilter` event', () => {
expect(wrapper.emitted('updateFilter')).toBeDefined();
expect(wrapper.emitted('updateFilter')[0]).toEqual([payload]);
});
});
describe('while loading', () => {
beforeEach(() => {
wrapper = createComponent({ props: { isLoading: true } });
});
it('renders loading icon', () => {
expect(findLoader(wrapper).exists()).toBe(true);
});
});
});
......@@ -3,7 +3,10 @@ import { TEST_HOST } from 'helpers/test_constants';
import { getJSONFixture } from 'helpers/fixtures';
import mutations from 'ee/analytics/cycle_analytics/store/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/mutation_types';
import { DEFAULT_DAYS_IN_PAST } from 'ee/analytics/cycle_analytics/constants';
import {
DEFAULT_DAYS_IN_PAST,
TASKS_BY_TYPE_SUBJECT_ISSUE,
} from 'ee/analytics/cycle_analytics/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getDateInPast, getDatesInRange } from '~/lib/utils/datetime_utility';
import { toYmd } from 'ee/analytics/shared/utils';
......@@ -142,7 +145,7 @@ export const customStageFormErrors = convertObjectPropsToCamelCase(rawCustomStag
const dateRange = getDatesInRange(startDate, endDate, toYmd);
export const tasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_type.json').map(
export const rawTasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_type.json').map(
labelData => {
// add data points for our mock date range
const maxValue = 10;
......@@ -154,7 +157,27 @@ export const tasksByTypeData = getJSONFixture('analytics/type_of_work/tasks_by_t
},
);
export const transformedTasksByTypeData = transformRawTasksByTypeData(tasksByTypeData);
export const transformedTasksByTypeData = transformRawTasksByTypeData(rawTasksByTypeData);
export const tasksByTypeData = {
seriesNames: ['Cool label', 'Normal label'],
data: [[0, 1, 2], [5, 2, 3], [2, 4, 1]],
groupBy: ['Group 1', 'Group 2', 'Group 3'],
};
export const taskByTypeFilters = {
selectedGroup: {
id: 22,
name: 'Gitlab Org',
fullName: 'Gitlab Org',
fullPath: 'gitlab-org',
},
selectedProjectIds: [],
startDate: new Date('2019-12-11'),
endDate: new Date('2020-01-10'),
subject: TASKS_BY_TYPE_SUBJECT_ISSUE,
selectedLabelIds: [1, 2, 3],
};
export const rawDurationData = [
{
......
......@@ -849,7 +849,7 @@ describe('Cycle analytics actions', () => {
const filter = TASKS_BY_TYPE_FILTERS.SUBJECT;
const value = 'issue';
it(`commits the ${types.SET_TASKS_BY_TYPE_FILTERS} mutation and dispatches 'fetchTasksByTypeData'`, done => {
it(`commits the ${types.SET_TASKS_BY_TYPE_FILTERS} mutation and dispatches 'fetchTopRankedGroupLabels'`, done => {
testAction(
actions.setTasksByTypeFilters,
{ filter, value },
......@@ -862,7 +862,7 @@ describe('Cycle analytics actions', () => {
],
[
{
type: 'fetchTasksByTypeData',
type: 'fetchTopRankedGroupLabels',
},
],
done,
......
......@@ -15,7 +15,7 @@ import {
startDate,
endDate,
customizableStagesAndEvents,
tasksByTypeData,
rawTasksByTypeData,
transformedTasksByTypeData,
selectedProjects,
} from '../mock_data';
......@@ -185,7 +185,7 @@ describe('Cycle analytics mutations', () => {
it('sets tasksByType.data to the raw returned chart data', () => {
state = { tasksByType: { data: null } };
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, tasksByTypeData);
mutations[types.RECEIVE_TASKS_BY_TYPE_DATA_SUCCESS](state, rawTasksByTypeData);
expect(state.tasksByType.data).toEqual(transformedTasksByTypeData);
});
......
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