Skip to content

Commit

Permalink
ADM-691: [frontend] feat: add feat about rework settings (#1138)
Browse files Browse the repository at this point in the history
* ADM-691: [frontend] feat: add new metrics field

* ADM-691: [frontend] feat: add rework setting title

* ADM-691: [frontend] feat: complete basic function about rework settings

* ADM-691: [frontend] feat: use redux to store fields

* ADM-691: [frontend] feat: replace undefind to null

* ADM-691: [frontend] feat: import config with rework times settings

* ADM-691: [frontend] fix: fix unit test

* ADM-691: [frontend] fix: fix unit test

* ADM-691: [frontend] fix: fix unit test

* ADM-691: [frontend] chore: refactor code after cr

* ADM-691: [frontend] fix: fix unit test

* ADM-691: [frontend] fix: fix unit test

* ADM-691: [frontend] test: improve test coverage

* ADM-691: [frontend] test: remove unused code

* ADM-691: [frontend] test: add test for rework setting

* ADM-691: [frontend] fix: fix e2e

* ADM-691: [frontend] fix: fix sonar

---------

Co-authored-by: Leiqiuhong <qiuhong.lei@thoughtworks.com>
  • Loading branch information
2 people authored and yp.wu committed Mar 13, 2024
1 parent c9640dd commit 92ffd21
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 14 deletions.
2 changes: 2 additions & 0 deletions frontend/__tests__/constants/fileConfig/fileConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BASIC_IMPORTED_OLD_CONFIG_FIXTURE,
REGULAR_CALENDAR,
CHINA_CALENDAR,
DEFAULT_REWORK_SETTINGS,
} from '../../fixtures';
import { convertToNewFileConfig } from '@src/constants/fileConfig';

Expand Down Expand Up @@ -43,6 +44,7 @@ describe('#fileConfig', () => {
organization: 'Thoughtworks-Heartbeat',
},
],
reworkTimesSettings: DEFAULT_REWORK_SETTINGS,
};

it('should return original config when it is not old config', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
MEAN_TIME_TO_RECOVERY,
REQUIRED_DATA,
REQUIRED_DATA_LIST,
REWORK_TIMES,
VELOCITY,
} from '../../fixtures';
import { act, fireEvent, render, waitFor, within, screen } from '@testing-library/react';
Expand Down Expand Up @@ -70,7 +71,7 @@ describe('MetricsTypeCheckbox', () => {

it('should show all selections when all option are select', async () => {
const { getByRole, getByText } = setup();
const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8);
const displayedDataList = REQUIRED_DATA_LIST.slice(1);
await act(async () => {
await userEvent.click(getByRole('button', { name: REQUIRED_DATA }));
});
Expand All @@ -85,7 +86,7 @@ describe('MetricsTypeCheckbox', () => {

it('should show all selections when click velocity selection and then click all selection', async () => {
const { getByRole, getByText } = setup();
const displayedDataList = REQUIRED_DATA_LIST.slice(1, 8);
const displayedDataList = REQUIRED_DATA_LIST.slice(1);

await act(async () => {
await userEvent.click(getByRole('button', { name: REQUIRED_DATA }));
Expand Down Expand Up @@ -113,6 +114,7 @@ describe('MetricsTypeCheckbox', () => {
listBox.getByRole('option', { name: VELOCITY }),
listBox.getByRole('option', { name: CYCLE_TIME }),
listBox.getByRole('option', { name: CLASSIFICATION }),
listBox.getByRole('option', { name: REWORK_TIMES }),
listBox.getByRole('option', { name: LEAD_TIME_FOR_CHANGES }),
listBox.getByRole('option', { name: DEPLOYMENT_FREQUENCY }),
listBox.getByRole('option', { name: CHANGE_FAILURE_RATE }),
Expand All @@ -125,7 +127,7 @@ describe('MetricsTypeCheckbox', () => {

it('should show some selections when click all option and then click velocity selection', async () => {
const { getByRole, getByText } = setup();
const displayedDataList = REQUIRED_DATA_LIST.slice(1, 7);
const displayedDataList = REQUIRED_DATA_LIST.slice(1, REQUIRED_DATA_LIST.length - 1);

await act(async () => {
await userEvent.click(getByRole('button', { name: REQUIRED_DATA }));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { SingleSelection } from '@src/containers/MetricsStep/ReworkSettings/SingleSelection';
import { act, render, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LIST_OPEN } from '@test/fixtures';

describe('SingleSelection', () => {
const mockOptions = ['opton1', 'opton2', 'opton3'];
const mockLabel = 'mockLabel';
const mockValue = 'mockOptions 1';
const mockOnValueChange = jest.fn();

afterEach(() => {
jest.clearAllMocks();
});

const setup = () =>
render(
<SingleSelection options={mockOptions} label={mockLabel} value={mockValue} onValueChange={mockOnValueChange} />,
);

it('should trigger onValueChange callback when select value option', async () => {
const { getByText, getByRole, getAllByRole } = setup();

await waitFor(() => {
expect(getByText(mockLabel)).toBeInTheDocument();
});

await act(async () => {
await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[0]);
});

const stepsListBox = within(getByRole('listbox'));
await act(async () => {
await userEvent.click(stepsListBox.getByText(mockOptions[1]));
});

expect(mockOnValueChange).toHaveBeenCalledTimes(1);
});

it('should show no options when search the wrong keyword', async () => {
const { getAllByRole, getByText } = setup();
const buttonElements = getAllByRole('button', { name: LIST_OPEN });

await act(async () => {
await userEvent.type(buttonElements[0], 'wrong keyword');
});

expect(getByText('No options')).toBeInTheDocument();
});
});
90 changes: 90 additions & 0 deletions frontend/__tests__/containers/MetricsStep/reworkSettings.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
ALL,
LIST_OPEN,
REWORK_EXCLUDE_WHICH_STATE,
REWORK_SETTINGS_TITLE,
REWORK_TO_WHICH_STATE,
} from '../../fixtures';
import { act, render, screen, waitFor, within } from '@testing-library/react';
import ReworkSettings from '@src/containers/MetricsStep/ReworkSettings';
import { CYCLE_TIME_LIST } from '@src/constants/resources';
import { setupStore } from '../../utils/setupStoreUtil';
import userEvent from '@testing-library/user-event';
import { Provider } from 'react-redux';
import React from 'react';

const mockedUseAppDispatch = jest.fn();
jest.mock('@src/hooks/useAppDispatch', () => ({
useAppDispatch: () => mockedUseAppDispatch,
}));

const store = setupStore();

describe('reworkSetting', () => {
const setup = () =>
render(
<Provider store={store}>
<ReworkSettings />
</Provider>,
);
afterEach(() => {
jest.clearAllMocks();
});

it('should show initial content', () => {
setup();

expect(screen.getByText(REWORK_SETTINGS_TITLE)).toBeInTheDocument();
expect(screen.getByText(REWORK_TO_WHICH_STATE)).toBeInTheDocument();
expect(screen.getByText(REWORK_EXCLUDE_WHICH_STATE)).toBeInTheDocument();
});

it('should get correct rework setting when pick option', async () => {
const { getByRole, getAllByRole } = setup();
await act(async () => {
await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[0]);
});
const stepsListBox = within(getByRole('listbox'));
await act(async () => {
await userEvent.click(stepsListBox.getByText('----'));
});
await waitFor(async () => {
await expect(
(screen.getByTestId('rework-single-selection-rework-to-which-state').querySelector('input') as HTMLInputElement)
.value,
).toBe('----');
});
});

it('should get correct value when pick all or other value', async () => {
const { getByRole, getAllByRole, queryByRole } = setup();
await act(async () => {
await userEvent.click(getAllByRole('button', { name: LIST_OPEN })[1]);
});
const stepsListBox = within(getByRole('listbox'));
await act(async () => {
await userEvent.click(stepsListBox.getByText(ALL));
});
await waitFor(async () => {
CYCLE_TIME_LIST.forEach((value) => {
expect(getByRole('button', { name: value })).toBeInTheDocument();
});
});

await act(async () => {
await userEvent.click(stepsListBox.getByText(ALL));
});
await waitFor(() => {
CYCLE_TIME_LIST.forEach((value) => {
expect(queryByRole('button', { name: value })).not.toBeInTheDocument();
});
});

await act(async () => {
await userEvent.click(stepsListBox.getByText(CYCLE_TIME_LIST[0]));
});
await waitFor(async () => {
expect(getByRole('button', { name: CYCLE_TIME_LIST[0] })).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BASE_PAGE_ROUTE,
BOARD_TYPES,
CONFIRM_DIALOG_DESCRIPTION,
DEFAULT_REWORK_SETTINGS,
MOCK_REPORT_URL,
NEXT,
PIPELINE_TOOL_TYPES,
Expand Down Expand Up @@ -357,6 +358,7 @@ describe('MetricsStepper', () => {
deployment: undefined,
doneStatus: undefined,
leadTime: undefined,
reworkTimesSettings: DEFAULT_REWORK_SETTINGS,
};
setup();

Expand Down Expand Up @@ -388,6 +390,10 @@ describe('MetricsStepper', () => {
deployment: undefined,
doneStatus: undefined,
leadTime: undefined,
reworkTimesSettings: {
rework2State: null,
excludeStates: [],
},
};

setup();
Expand Down
16 changes: 8 additions & 8 deletions frontend/__tests__/containers/ReportStep/ReportStep.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,28 +182,28 @@ describe('Report Step', () => {
});

it('should render the Lead Time For Change component with correct props', () => {
setup([REQUIRED_DATA_LIST[4]]);
setup([REQUIRED_DATA_LIST[5]]);

expect(screen.getByText('60.79')).toBeInTheDocument();
expect(screen.getByText('39.03')).toBeInTheDocument();
expect(screen.getByText('99.82')).toBeInTheDocument();
});

it('should render the Deployment frequency component with correct props', () => {
setup([REQUIRED_DATA_LIST[5]]);
setup([REQUIRED_DATA_LIST[6]]);

expect(screen.getByText('0.40')).toBeInTheDocument();
});

it('should render the Change failure rate component with correct props', () => {
setup([REQUIRED_DATA_LIST[6]]);
setup([REQUIRED_DATA_LIST[7]]);

expect(screen.getByText('0.00')).toBeInTheDocument();
expect(screen.getByText('% (0/6)')).toBeInTheDocument();
});

it('should render the Mean time to recovery component with correct props', () => {
setup([REQUIRED_DATA_LIST[7]]);
setup([REQUIRED_DATA_LIST[8]]);

expect(screen.getByText('4.00')).toBeInTheDocument();
});
Expand Down Expand Up @@ -252,7 +252,7 @@ describe('Report Step', () => {
jest.useRealTimers();
});

it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])(
it.each([[REQUIRED_DATA_LIST[2]], [REQUIRED_DATA_LIST[5]]])(
'should render detail page when clicking show more button given metric %s',
async (requiredData) => {
setup([requiredData], MOCK_DATE_RANGE);
Expand All @@ -266,7 +266,7 @@ describe('Report Step', () => {
},
);

it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])(
it.each([[REQUIRED_DATA_LIST[2]], [REQUIRED_DATA_LIST[5]]])(
'should return report page when clicking back button in Breadcrumb in detail page given metric %s',
async (requiredData) => {
setup([requiredData]);
Expand All @@ -286,7 +286,7 @@ describe('Report Step', () => {
},
);

it.each([[REQUIRED_DATA_LIST[1]], [REQUIRED_DATA_LIST[4]]])(
it.each([[REQUIRED_DATA_LIST[2]], [REQUIRED_DATA_LIST[5]]])(
'should return report page when clicking previous button in detail page given metric %s',
async (requiredData) => {
setup([requiredData]);
Expand Down Expand Up @@ -318,7 +318,7 @@ describe('Report Step', () => {
expect(exportPipelineButton).not.toBeInTheDocument();
});

it.each([[REQUIRED_DATA_LIST[4]], [REQUIRED_DATA_LIST[5]], [REQUIRED_DATA_LIST[6]], [REQUIRED_DATA_LIST[7]]])(
it.each([[REQUIRED_DATA_LIST[5]], [REQUIRED_DATA_LIST[6]], [REQUIRED_DATA_LIST[7]], [REQUIRED_DATA_LIST[8]]])(
'should show export pipeline button when selecting %s',
(requiredData) => {
setup([requiredData]);
Expand Down
11 changes: 10 additions & 1 deletion frontend/__tests__/context/metricsSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ import saveMetricsSettingReducer, {
updatePipelineStep,
updateTreatFlagCardAsBlock,
} from '@src/context/Metrics/metricsSlice';
import {
CLASSIFICATION_WARNING_MESSAGE,
DEFAULT_REWORK_SETTINGS,
NO_RESULT_DASH,
PIPELINE_SETTING_TYPES,
} from '../fixtures';
import { ASSIGNEE_FILTER_TYPES, CYCLE_TIME_SETTINGS_TYPES, MESSAGE } from '@src/constants/resources';
import { CLASSIFICATION_WARNING_MESSAGE, NO_RESULT_DASH, PIPELINE_SETTING_TYPES } from '../fixtures';
import { setupStore } from '../utils/setupStoreUtil';
import { store } from '@src/store';

Expand Down Expand Up @@ -50,6 +55,7 @@ const initState = {
importedDeployment: [],
importedLeadTime: [],
importedAdvancedSettings: null,
reworkTimesSettings: DEFAULT_REWORK_SETTINGS,
},
cycleTimeWarningMessage: null,
classificationWarningMessage: null,
Expand Down Expand Up @@ -111,6 +117,7 @@ describe('saveMetricsSetting reducer', () => {
importedDeployment: [],
importedPipelineCrews: [],
importedAdvancedSettings: null,
reworkTimesSettings: DEFAULT_REWORK_SETTINGS,
});
});

Expand Down Expand Up @@ -193,6 +200,7 @@ describe('saveMetricsSetting reducer', () => {
storyPoint: '1',
flag: '2',
},
reworkTimesSettings: DEFAULT_REWORK_SETTINGS,
};
const savedMetricsSetting = saveMetricsSettingReducer(
initState,
Expand All @@ -212,6 +220,7 @@ describe('saveMetricsSetting reducer', () => {
importedDeployment: mockMetricsImportedData.deployment,
importedLeadTime: mockMetricsImportedData.leadTime,
importedAdvancedSettings: mockMetricsImportedData.advancedSettings,
reworkTimesSettings: mockMetricsImportedData.reworkTimesSettings,
});
});

Expand Down
19 changes: 19 additions & 0 deletions frontend/__tests__/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const REQUIRED_DATA_LIST = [
'Velocity',
'Cycle time',
'Classification',
'Rework times',
'Lead time for changes',
'Deployment frequency',
'Change failure rate',
Expand All @@ -58,6 +59,7 @@ export const ALL = 'All';
export const VELOCITY = 'Velocity';
export const CYCLE_TIME = 'Cycle time';
export const CLASSIFICATION = 'Classification';
export const REWORK_TIMES = 'Rework times';
export const LEAD_TIME_FOR_CHANGES = 'Lead time for changes';
export const DEPLOYMENT_FREQUENCY = 'Deployment frequency';
export const CHANGE_FAILURE_RATE = 'Change failure rate';
Expand Down Expand Up @@ -247,6 +249,10 @@ export const IMPORTED_NEW_CONFIG_FIXTURE = {
},
],
},
reworkTimesSettings: {
rework2State: null,
excludeStates: [],
},
};

export const MOCK_EXPORT_CSV_REQUEST_PARAMS: CSVReportRequestDTO = {
Expand Down Expand Up @@ -701,6 +707,10 @@ export const BASIC_IMPORTED_OLD_CONFIG_FIXTURE = {
orgId: 'Thoughtworks-Heartbeat',
},
],
reworkTimesSettings: {
rework2State: null,
excludeStates: [],
},
};

export const ERROR_MESSAGE_TIME_DURATION = 4000;
Expand Down Expand Up @@ -746,3 +756,12 @@ export const FAKE_TOKEN = 'fake-token';
export const FAKE_PIPELINE_TOKEN = 'bkua_mockTokenMockTokenMockTokenMockToken1234';

export const ADVANCED_SETTINGS_TITLE = 'Advanced settings';

export const REWORK_SETTINGS_TITLE = 'Rework times settings';
export const REWORK_TO_WHICH_STATE = 'Rework to which state';
export const REWORK_EXCLUDE_WHICH_STATE = 'Exclude which states (optional)';

export const DEFAULT_REWORK_SETTINGS = {
rework2State: null,
excludeStates: [],
};
Loading

0 comments on commit 92ffd21

Please sign in to comment.