Skip to content

Commit

Permalink
ADM-913 [frontend]:receive multiple time ranges in metrics page- pipe…
Browse files Browse the repository at this point in the history
…line (#1393)

* ADM-913 fix: remove time for pipeline info

* ADM-913 refactor: extract value

* ADM-913 feat: send several steps request by time ranges

* ADM-913 feat: use allSettled to request api

* ADM-913 test: fix unit test

* ADM-913 test: add unit test

* ADM-913 test: fix test

* ADM-913 fix: fix eslint

* ADM-913 test: add happy path test for steps res

* ADM-913 test: fix test
  • Loading branch information
Leiqiuhong authored Apr 19, 2024
1 parent b9f0ea8 commit 6984484
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 111 deletions.
8 changes: 4 additions & 4 deletions frontend/__tests__/client/MetricsClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('get steps from metrics response', () => {
}),
);

const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token);
const result = await metricsClient.getSteps(params[0], buildId, organizationId, pipelineType, token);

expect(result).toEqual({ response: ['step1'], haveStep: true });
});
Expand All @@ -36,7 +36,7 @@ describe('get steps from metrics response', () => {
);

await expect(async () => {
await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token);
await metricsClient.getSteps(params[0], buildId, organizationId, pipelineType, token);
}).rejects.toThrow(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR);
});

Expand All @@ -48,14 +48,14 @@ describe('get steps from metrics response', () => {
);

await expect(async () => {
await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token);
await metricsClient.getSteps(params[0], buildId, organizationId, pipelineType, token);
}).rejects.toThrow(VERIFY_ERROR_MESSAGE.BAD_REQUEST);
});

it('should show isNoStep True when getSteps response status 204', async () => {
server.use(rest.get(getStepsUrl, (req, res, ctx) => res(ctx.status(HttpStatusCode.NoContent))));

const result = await metricsClient.getSteps(params, buildId, organizationId, pipelineType, token);
const result = await metricsClient.getSteps(params[0], buildId, organizationId, pipelineType, token);

expect(result).toEqual({ branches: [], response: [], haveStep: false, pipelineCrews: [] });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ jest.mock('@src/context/config/configSlice', () => ({
selectStepsParams: jest.fn().mockReturnValue({
buildId: '',
organizationId: '',
params: {
endTime: 1681747200000,
orgName: '',
pipelineName: '',
repository: '',
startTime: 1680537600000,
},
params: [
{
endTime: 1681747200000,
orgName: '',
pipelineName: '',
repository: '',
startTime: 1680537600000,
},
],
pipelineType: 'BuildKite',
token: '',
}),
Expand Down Expand Up @@ -146,7 +148,6 @@ describe('PipelineMetricSelection', () => {
});

it('should show step selection when select organization and pipelineName', async () => {
metricsClient.getSteps = jest.fn().mockImplementation(() => ['steps1', 'steps2']);
const { getByText } = await setup(
{ ...deploymentFrequencySetting, organization: 'mockOrgName', pipelineName: 'mockName' },
false,
Expand All @@ -161,7 +162,7 @@ describe('PipelineMetricSelection', () => {

it('should show error message pop when getSteps failed', async () => {
metricsClient.getSteps = jest.fn().mockImplementation(() => {
throw new Error('error message');
return Promise.reject('error');
});
const { getByText, getByRole, getAllByRole } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] },
Expand All @@ -178,12 +179,14 @@ describe('PipelineMetricSelection', () => {
});

await waitFor(() => {
expect(getByText('Failed to get BuildKite steps: error message')).toBeInTheDocument();
expect(getByText('Failed to get BuildKite steps')).toBeInTheDocument();
});
expect(mockUpdatePipeline).toHaveBeenCalledTimes(2);
});
it('should show no steps warning message when getSteps succeed but get no steps', async () => {
metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false });
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: [], haveStep: false, pipelineCrews: [], branches: [] });
const { getByText, getByRole, getAllByRole } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] },
false,
Expand All @@ -201,7 +204,7 @@ describe('PipelineMetricSelection', () => {
await waitFor(() => {
expect(
getByText(
'There is no step during this period for this pipeline! Please change the search time in the Config page!',
'There is no step during these periods for this pipeline! Please change the search time in the Config page!',
),
).toBeInTheDocument();

Expand All @@ -210,7 +213,9 @@ describe('PipelineMetricSelection', () => {
});

it('should show no steps warning message when getSteps succeed but get no steps and isShowRemoveButton is true', async () => {
metricsClient.getSteps = jest.fn().mockReturnValue({ response: [], haveStep: false });
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: [], haveStep: false, pipelineCrews: [], branches: [] });
const { getByRole, getAllByRole } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] },
true,
Expand All @@ -231,7 +236,9 @@ describe('PipelineMetricSelection', () => {
});

it('should show steps selection when getSteps succeed ', async () => {
metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true });
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: ['steps'], haveStep: true, pipelineCrews: [], branches: [] });
const { getByRole, getByText, getAllByRole } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: [] },
false,
Expand All @@ -257,7 +264,7 @@ describe('PipelineMetricSelection', () => {
it('should show branches selection when getSteps succeed ', async () => {
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'] });
.mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'], pipelineCrews: [] });
const { getByRole, getByText } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: '', branches: ['branch1', 'branch2'] },
false,
Expand All @@ -279,7 +286,7 @@ describe('PipelineMetricSelection', () => {
it('should show not show branches when deployment setting has branches given branches does not match pipeline ', async () => {
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'] });
.mockReturnValue({ response: ['steps'], haveStep: true, branches: ['branch1', 'branch2'], pipelineCrews: [] });
const { getByRole, queryByRole, getByText } = await setup(
{ id: 0, organization: 'mockOrgName3', pipelineName: 'mockName3', step: '', branches: ['branch6', 'branch7'] },
false,
Expand All @@ -299,7 +306,9 @@ describe('PipelineMetricSelection', () => {
});

it('should show duplicated message given duplicated id', async () => {
metricsClient.getSteps = jest.fn().mockReturnValue({ response: ['steps'], haveStep: true });
metricsClient.getSteps = jest
.fn()
.mockReturnValue({ response: ['steps'], haveStep: true, pipelineCrews: [], branches: [] });
const { getByText } = await setup(
{ id: 0, organization: 'mockOrgName', pipelineName: 'mockName', step: 'step1', branches: [] },
false,
Expand Down
8 changes: 8 additions & 0 deletions frontend/__tests__/context/metricsSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import saveMetricsSettingReducer, {
selectReworkTimesSettings,
selectShouldGetBoardConfig,
selectShouldGetPipelineConfig,
selectShouldRetryPipelineConfig,
selectStepWarningMessage,
selectTreatFlagCardAsBlock,
setCycleTimeSettingsType,
Expand All @@ -34,6 +35,7 @@ import saveMetricsSettingReducer, {
updateReworkTimesSettings,
updateShouldGetBoardConfig,
updateShouldGetPipelineConfig,
updateShouldRetryPipelineConfig,
updateTreatFlagCardAsBlock,
} from '@src/context/Metrics/metricsSlice';
import {
Expand All @@ -49,6 +51,7 @@ import { store } from '@src/store';
const initState = {
shouldGetBoardConfig: true,
shouldGetPipeLineConfig: true,
shouldRetryPipelineConfig: false,
jiraColumns: [],
targetFields: [],
users: [],
Expand Down Expand Up @@ -590,6 +593,11 @@ describe('saveMetricsSetting reducer', () => {
expect(savedPipelineCrews.pipelineCrews).toBe(crews);
});

it('should update ShouldRetryPipelineConfig', async () => {
store.dispatch(updateShouldRetryPipelineConfig(true));
expect(selectShouldRetryPipelineConfig(store.getState())).toEqual(true);
});

it('should set cycle time setting type', () => {
const setCycleTimeSettingsTypeResult = saveMetricsSettingReducer(
initState,
Expand Down
32 changes: 18 additions & 14 deletions frontend/__tests__/context/pipelineToolSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,15 @@ describe('pipelineTool reducer', () => {
expect(selectStepsParams(store.getState(), 'mockOrgName', 'mockName')).toEqual({
buildId: 'mockId',
organizationId: 'mockOrgId',
params: {
endTime: dayjs(MOCK_DATE_RANGE[0].endDate).endOf('date').valueOf(),
orgName: 'mockOrgName',
pipelineName: 'mockName',
repository: 'mockRepository',
startTime: dayjs(MOCK_DATE_RANGE[0].startDate).startOf('date').valueOf(),
},
params: [
{
endTime: dayjs(MOCK_DATE_RANGE[0].endDate).endOf('date').valueOf(),
orgName: 'mockOrgName',
pipelineName: 'mockName',
repository: 'mockRepository',
startTime: dayjs(MOCK_DATE_RANGE[0].startDate).startOf('date').valueOf(),
},
],
pipelineType: 'BuildKite',
token: '',
});
Expand All @@ -254,13 +256,15 @@ describe('pipelineTool reducer', () => {
expect(selectStepsParams(store.getState(), '', '')).toEqual({
buildId: '',
organizationId: '',
params: {
endTime: dayjs(MOCK_DATE_RANGE[0].endDate).endOf('date').valueOf(),
orgName: '',
pipelineName: '',
repository: '',
startTime: dayjs(MOCK_DATE_RANGE[0].startDate).startOf('date').valueOf(),
},
params: [
{
endTime: dayjs(MOCK_DATE_RANGE[0].endDate).endOf('date').valueOf(),
orgName: '',
pipelineName: '',
repository: '',
startTime: dayjs(MOCK_DATE_RANGE[0].startDate).startOf('date').valueOf(),
},
],
pipelineType: 'BuildKite',
token: '',
});
Expand Down
24 changes: 17 additions & 7 deletions frontend/__tests__/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SortType } from '@src/containers/ConfigStep/DateRangePicker/DateRangePi
import { CSVReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request';
import { ReportResponseDTO } from '@src/clients/report/dto/response';
import { SOURCE_CONTROL_TYPES } from '@src/constants/resources';
import { IStepsParams } from '@src/clients/MetricsClient';
import { METRIC_TYPES } from '@src/constants/commons';

export const PROJECT_NAME = 'Heartbeat';
Expand Down Expand Up @@ -368,13 +369,22 @@ export enum PIPELINE_SETTING_TYPES {
export const CONFIRM_DIALOG_DESCRIPTION = 'All the filled data will be cleared. Continue to Home page?';

export const MOCK_GET_STEPS_PARAMS = {
params: {
pipelineName: 'mock pipeline name',
repository: 'mock repository',
orgName: 'mock orgName',
startTime: 1212112121212,
endTime: 1313131313131,
},
params: [
{
pipelineName: 'mock pipeline name',
repository: 'mock repository',
orgName: 'mock orgName',
startTime: 1212112121212,
endTime: 1313131313131,
},
{
pipelineName: 'mock pipeline name',
repository: 'mock repository',
orgName: 'mock orgName',
startTime: 1212112121214,
endTime: 1313131313134,
},
] as IStepsParams[],
buildId: 'mockBuildId',
organizationId: 'mockOrganizationId',
pipelineType: 'BuildKite',
Expand Down
86 changes: 76 additions & 10 deletions frontend/__tests__/hooks/useGetMetricsStepsEffect.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,78 @@
import { ERROR_MESSAGE_TIME_DURATION, MOCK_GET_STEPS_PARAMS } from '../fixtures';
import { useGetMetricsStepsEffect } from '@src/hooks/useGetMetricsStepsEffect';
import { InternalServerError } from '@src/errors/InternalServerError';
import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources';
import { metricsClient } from '@src/clients/MetricsClient';
import { act, renderHook } from '@testing-library/react';
import { HttpStatusCode } from 'axios';
import { setupStore } from '@test/utils/setupStoreUtil';
import { TimeoutError } from '@src/errors/TimeoutError';
import React, { ReactNode } from 'react';
import { Provider } from 'react-redux';

const mockDispatch = jest.fn();
jest.mock('@src/context/Metrics/metricsSlice', () => ({
...jest.requireActual('@src/context/Metrics/metricsSlice'),
updateShouldRetryPipelineConfig: jest.fn(),
}));

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));

describe('use get steps effect', () => {
const { params, buildId, organizationId, pipelineType, token } = MOCK_GET_STEPS_PARAMS;
const store = setupStore();
const wrapper = ({ children }: { children: ReactNode }) => {
return <Provider store={store}>{children}</Provider>;
};

const setup = () => renderHook(() => useGetMetricsStepsEffect(), { wrapper });
it('should init data state when render hook', async () => {
const { result } = renderHook(() => useGetMetricsStepsEffect());

expect(result.current.isLoading).toEqual(false);
});

it('should get the union set from steps res', async () => {
metricsClient.getSteps = jest
.fn()
.mockReturnValueOnce({
response: ['a', 'b', 'c'],
haveStep: true,
branches: ['branchA', 'branchB'],
pipelineCrews: ['crewA', 'crewB'],
})
.mockReturnValueOnce({
response: ['a', 'd', 'e'],
haveStep: true,
branches: ['branchC', 'branchD'],
pipelineCrews: [],
})
.mockReturnValueOnce({
response: [],
haveStep: false,
branches: [],
pipelineCrews: [],
});
const { result } = renderHook(() => useGetMetricsStepsEffect());
let res;
await act(async () => {
res = await result.current.getSteps(params, buildId, organizationId, pipelineType, token);
});
expect(res).toEqual({
response: ['a', 'b', 'c', 'd', 'e'],
haveStep: true,
branches: ['branchA', 'branchB', 'branchC', 'branchD'],
pipelineCrews: ['crewA', 'crewB'],
});
});

it('should set error message when get steps throw error', async () => {
jest.useFakeTimers();
metricsClient.getSteps = jest.fn().mockImplementation(() => {
throw new Error('error');
return Promise.reject('error');
});
const { result } = renderHook(() => useGetMetricsStepsEffect());
const { result } = setup();

expect(result.current.isLoading).toEqual(false);

Expand All @@ -30,16 +84,28 @@ describe('use get steps effect', () => {
expect(result.current.errorMessage).toEqual('');
});

it('should set error message when get steps response status 500', async () => {
it('should set error message when get steps responses are failed', async () => {
metricsClient.getSteps = jest.fn().mockImplementation(() => {
throw new InternalServerError('error message', HttpStatusCode.InternalServerError, 'fake description');
return Promise.reject('error');
});
const { result } = setup();
await act(async () => {
await result.current.getSteps(params, buildId, organizationId, pipelineType, token);
});
const { result } = renderHook(() => useGetMetricsStepsEffect());

act(() => {
result.current.getSteps(params, buildId, organizationId, pipelineType, token);
expect(result.current.errorMessage).toEqual('Failed to get BuildKite steps');
});

it('should set error message when get steps responses are timeout', async () => {
metricsClient.getSteps = jest.fn().mockImplementation(() => {
return Promise.reject(new TimeoutError('error', AXIOS_REQUEST_ERROR_CODE.TIMEOUT));
});
const { result } = setup();
await act(async () => {
await result.current.getSteps(params, buildId, organizationId, pipelineType, token);
});

expect(result.current.errorMessage).toEqual('Failed to get BuildKite steps: error message');
expect(result.current.errorMessage).toEqual('Failed to get BuildKite steps: timeout');
expect(mockDispatch).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit 6984484

Please sign in to comment.