From 7d7e85236c1812b082434fa0b4b2fc3023660304 Mon Sep 17 00:00:00 2001 From: yulongcai <141199398+yulongcai@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:39:50 +0800 Subject: [PATCH 01/11] ADM-898[backend][frontend]: feat: block' in board mapping is mutually exclusive with 'flag as block' (#1350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ADM-898:[backend] feat: should not export cycle time flag on table header when not consider flag as block * ADM-898:[frontend]feat: not show flagCard when board mapping table has ‘block’ column * ADM-898:[frontend]feat: warning pop-up when conflict * ADM-898:[frontend]refactor: refactor codes * ADM-898:[frontend]refactor: remove fromFlag * ADM-898:[frontend]feat: not show flagCard when board mapping table has ‘block’ column * ADM-873-bug:[backend] feat: add test for as flag is block is false * ADM-898:[frontend]feat: add status logic * ADM-898:[frontend]refactor: refactor code * ADM-898:[backend] fix: fix bug for not calculate flag block to cycle when consider flag as block * ADM-886:[docs]docs: refactor chart API docs sequence diagram (#1339) * ADM-886:[docs]docs: update chart API image size * ADM-886:[docs]docs: update chart API docs content * ADM-886:[docs]docs: refactor chart API docs * ADM-886:[docs]docs: refactor chart API docs sequence diagram * ADM 834 add frontend validation to board Id (#1338) * ADM-834:[frontend]feat: fix rebase * ADM-834:[backend]feat: added api for domain url check * ADM-834:[backend]feat: add dashboard api for site check * ADM-834:[backend]feat: added api for domain url check * ADM-834:[frontend]fix: fix rebase * ADM-834:[frontend]fix: add regex validation to board id * ADM-834:[frontend]fix: fix lint * ADM 751 [docs]docs: fix readme in board mappings part (#1334) * ADM-751:[frontend]feat: add by status e2e test flow into import test * ADM-751:[frontend]feat: add more flow in create test * ADM-751:[frontend]feat: fix lint and refactor fixture file * ADM-751:[frontend]feat: fix by state flow and add new test * ADM-751:[frontend]feat: refactored file strcture and implemented new e2e test for cycle time status testing, just need to fix the data then * ADM-751:[frontend]feat: rework the by status flow e2e test, remove unnecessary steps * ADM-751:[frontend]feat: fix test flow again * ADM-751:[frontend]feat: fix type check * ADM-751:[frontend]feat: add status flow e2e test in import test * ADM-751:[frontend]fix: fix lint * ADM-882:[backend]feat: add import json file * ADM-751:[frontend]fix: refactor code * ADM-751:[frontend]fix: refactor code fix type * ADM-751:[docs]docs: fix readme in board mappings * ADM-751:[docs]docs: remove conflicted rework part * ADM-751 refactor --------- Co-authored-by: guzhongren * chore(sbom): generate sbom when releasing (#1340) * ADM-833:[frontend]feat:add e2e unhappy path scenarios (#1328) * ADM-833:[frontend]feat:add e2e unhappy path scenarios ADM-833: [frontend] add e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] feat: add unhappy path scenoria ADM-833: [frontend] feat: optimize add and remove new pipeline,add unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833:[frontend]feat:merge unhappy path scenario ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix e2e test error ADM-833:[frontend]feat:distinguish major and unhappy path test in local environment ADM-833: [frontend] feat: sperate major and all ADM-833[frontend]feat:change branch invalid error message ADM-833: [frontend] fix: fix index.ts error ADM-833: [frontend] fix: remove the log file ADM-833: [frontend] fix: fix conflict with main branch ADM-833: [frontend] fix: revert readme ADM-833: [frontend] fix: remove log file ADM-833: [frontend] fix: modify .gitignore, add omit logs * ADM-833 fix path * ADM-833: [frontend] fix: update the spec location * ADM-833: [frontend] fix: fix daterange error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix review error --------- Co-authored-by: YaoZhang-Daniel Co-authored-by: guzhongren * ADM-833: [frontend] fix: fix env error (#1341) * ADM-833:[frontend]feat:add e2e unhappy path scenarios ADM-833: [frontend] add e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] feat: add unhappy path scenoria ADM-833: [frontend] feat: optimize add and remove new pipeline,add unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833:[frontend]feat:merge unhappy path scenario ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix e2e test error ADM-833:[frontend]feat:distinguish major and unhappy path test in local environment ADM-833: [frontend] feat: sperate major and all ADM-833[frontend]feat:change branch invalid error message ADM-833: [frontend] fix: fix index.ts error ADM-833: [frontend] fix: remove the log file ADM-833: [frontend] fix: fix conflict with main branch ADM-833: [frontend] fix: revert readme ADM-833: [frontend] fix: remove log file ADM-833: [frontend] fix: modify .gitignore, add omit logs * ADM-833 fix path * ADM-833: [frontend] fix: update the spec location * ADM-833: [frontend] fix: fix daterange error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix env error --------- Co-authored-by: YaoZhang-Daniel Co-authored-by: guzhongren * Revert "ADM-833: [frontend] fix: fix env error (#1341)" (#1344) This reverts commit eed90dc2ee055d2b0601db4ddfb22877effbf7a7. * [ADM-833][frontend]: chore: fix the e2e env injection error. (#1345) * refactor(e2e): name conversion[frontend] * fix(e2e): fix type check[frontend] * ADM-898:[frontend]fix: fix test * ADM-898:[frontend]feat: add test for CycleTime * ADM-898:[frontend]fix: fix UpperCase error * ADM-898:[frontend]fix: fix default value * ADM-898:[frontend]fix: fix import treatFlagCardAsBlock value * ADM-898:[backend] fix: repair e2e test * ADM-898:[backend] fix: repair e2e test to delete check consider flag as block * ADM-898:[backend] fix: sonar issue * [ADM-877][frontend]: feat: remove the limit of start-date & end-date selection. (#1342) * [ADM-877][frontend]: feat: remove the limit of start-date & end-date selection. * [ADM-877][frontend]: chore: refine code. * ADM-873:[frontend][backend] fix: fix success notification (#1327) * ADM-873-bug:[docx] docx: fix docx * ADM-873:[frontend]fix: fix error notification * ADM-873:[frontend]refactor: refactor code * ADM-873-bug:[backend] feat: return flag for whether it has file create by this invoke * Revert "ADM-873:[frontend]refactor: refactor code" This reverts commit e2fe52fc2c1b41dd0c2743daed78a6ff08a98f56. * Revert "ADM-873:[frontend]fix: fix error notification" This reverts commit b659c63af9143eecc97ae83f322f61885b9ad447. * ADM-873:[frontend]fix: use hasCsvFileCreateSuccessful to handle success notification * ADM-873-bug:[backend] feat: return has csv create successful when get completed * ADM-873-bug:[backend] fix: rename * ADM-873:[frontend]fix: fix test * ADM-873:[frontend]refactor: rename hasCsvFileCreateSuccessful to isSuccessfulCreateCsvFile * ADM-873:[backend]refactor: modify test info * ADM-873:[backend]refactor: format code --------- Co-authored-by: yulongcai Co-authored-by: Rui7ing <129819182+Rui7ing@users.noreply.github.com> * [frontend] feat/adm 811: Do not clear the configuration settings when returning from the metric page to the configuration page. (#1343) * [feat][adm-811]: remove GoCD option * [frontend][adm-811]: feat: remove clear config data trigger when back to config page * [frontend][adm-811]: refact: refact unit test * [frontend][adm-811]: replace fireEvent use userEvent * [frontend][adm-811]: fix failed case * Adm 910[docs] feat: add Github graghQL rate limit issue (#1348) * ADM-910[docs] feat: add Node and Rate limits and calculate * ADM-910[docs] feat: add flew chart for get data by github api * Adm 912[frontend]: ui refine date picker in metrics page (#1349) * ADM-912 feat: display date ranges * ADM-912 test: add unit test * ADM-912 fix: fix css * ADM-912 fix: fix color * ADM-912 fix: fix hex color * ADM-912 fix: use color from theme * ADM-898:[frontend]feat: add block case * ADM-898:[frontend]fix: fix test for treatFlagCardAsBlock * ADM-898:[frontend]fix: fix bug * ADM-898 fix: fix treat flag card as block when import * ADM-898 fix: fix treat flag card as block when create * ADM-898 refactor: rename * ADM-898:[frontend]fix: remove unnecessary cycleTimeSettings * ADM-898 fix: fix test * ADM-898:[backend] refactor: refactor code * ADM-898 test: add unit test * ADM-898 test: add e2e test for no block column board when create project * ADM-898 test: add e2e test for no block column board when import project * ADM-898:[frontend]refactor: iterate over test data and run tests using forEach * ADM-898:[frontend]refactor: export BLOCK_COLUMN_NAME * ADM-898:[frontend]refactor: fix prettier error * ADM-898:[frontend]fix: fix the error from merge --------- Co-authored-by: Tingyu Dong Co-authored-by: Steveay <907221539@qq.com> Co-authored-by: PengxiWPix <113176309+PengxiWPix@users.noreply.github.com> Co-authored-by: guzhongren Co-authored-by: YaoZhang87 <162096287+YaoZhang87@users.noreply.github.com> Co-authored-by: YaoZhang-Daniel Co-authored-by: Chao <89126516+mrcuriosity-tw@users.noreply.github.com> Co-authored-by: TingyuDong <113588395+TingyuDong@users.noreply.github.com> Co-authored-by: Rui7ing <129819182+Rui7ing@users.noreply.github.com> Co-authored-by: K Chow Co-authored-by: junbo dai Co-authored-by: Leiqiuhong <141199516+Leiqiuhong@users.noreply.github.com> Co-authored-by: Leiqiuhong --- .../service/board/jira/JiraService.java | 3 +- .../main/java/heartbeat/util/BoardUtil.java | 22 +++++---- .../service/jira/JiraServiceTest.java | 2 +- .../java/heartbeat/util/BoardUtilTest.java | 18 ++++++- ...ItemsListAndCycleTimeInfosListFixture.java | 46 +++++++++++------- .../containers/MetricsStep/CycleTime.test.tsx | 21 +++++++- .../MetricsStep/MetricsStep.test.tsx | 2 +- .../__tests__/context/metricsSlice.test.ts | 28 +++++++++++ frontend/__tests__/fixtures.ts | 1 - .../hooks/reportMapper/report.test.tsx | 10 ++-- .../board-data-without-block-column.csv | 4 ++ .../e2e/fixtures/create-new/board-data.csv | 48 +++++++++---------- .../e2e/fixtures/create-new/config-step.ts | 19 ++++++++ .../e2e/fixtures/create-new/metrics-step.ts | 24 +++++++++- .../e2e/fixtures/create-new/report-result.ts | 4 +- .../board-data-by-status.csv | 10 ++-- .../cycle-time-by-column-fixture.ts | 2 +- .../board-data-without-block-column.csv | 7 +++ .../e2e/fixtures/import-file/board-data.csv | 2 +- frontend/e2e/pages/metrics/config-step.ts | 3 +- frontend/e2e/pages/metrics/metrics-step.ts | 36 ++++++++++++-- frontend/e2e/pages/metrics/report-step.ts | 15 ++++++ .../major-path/create-a-new-project.spec.ts | 46 +++++++++++++++++- .../cycle-time-by-status-test.spec.ts | 2 - .../import-project-from-file.spec.ts | 10 +++- .../e2e/specs/major-path/page-jumps.spec.ts | 2 - frontend/src/clients/report/ReportClient.ts | 1 - frontend/src/clients/report/dto/response.ts | 1 - frontend/src/constants/resources.ts | 5 +- .../MetricsStep/CycleTime/index.tsx | 36 ++++++++++++-- frontend/src/context/Metrics/metricsSlice.ts | 35 +++++++++++--- frontend/src/hooks/useVerifyBoardEffect.ts | 2 - frontend/src/utils/util.ts | 16 ++++++- 33 files changed, 385 insertions(+), 98 deletions(-) create mode 100644 frontend/e2e/fixtures/create-new/board-data-without-block-column.csv create mode 100644 frontend/e2e/fixtures/import-file/board-data-without-block-column.csv diff --git a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java index b22b29a3dd..27e77de786 100644 --- a/backend/src/main/java/heartbeat/service/board/jira/JiraService.java +++ b/backend/src/main/java/heartbeat/service/board/jira/JiraService.java @@ -844,7 +844,8 @@ private CycleTimeInfoDTO getCycleTime(CardHistoryResponseDTO cardHistoryResponse keyFlagged); List cycleTimeInfos = boardUtil.getCycleTimeInfos(statusChangedArray, realDoneStatus, treatFlagCardAsBlock); - List originCycleTimeInfos = boardUtil.getOriginCycleTimeInfos(statusChangedArray); + List originCycleTimeInfos = boardUtil.getOriginCycleTimeInfos(statusChangedArray, + treatFlagCardAsBlock); return CycleTimeInfoDTO.builder() .cycleTimeInfos(cycleTimeInfos) diff --git a/backend/src/main/java/heartbeat/util/BoardUtil.java b/backend/src/main/java/heartbeat/util/BoardUtil.java index c974755051..fd316d5e6c 100644 --- a/backend/src/main/java/heartbeat/util/BoardUtil.java +++ b/backend/src/main/java/heartbeat/util/BoardUtil.java @@ -20,10 +20,12 @@ public class BoardUtil { private final WorkDay workDay; - public List getOriginCycleTimeInfos(List statusChangedArray) { + public List getOriginCycleTimeInfos(List statusChangedArray, + Boolean treatFlagCardAsBlock) { List flagTimeStamp = getFlagTimeStamps(statusChangedArray); List columnTimeStamp = getColumnTimeStamps(statusChangedArray); - List originCycleTimeInfos = calculateOriginCycleTime(flagTimeStamp, columnTimeStamp); + List originCycleTimeInfos = calculateOriginCycleTime(flagTimeStamp, columnTimeStamp, + treatFlagCardAsBlock); return getCollectRemovedDuplicates(originCycleTimeInfos); } @@ -50,7 +52,7 @@ public List getCycleTimeInfos(List statusChang } private List calculateOriginCycleTime(List flagTimeStamp, - List columnTimeStamp) { + List columnTimeStamp, Boolean treatFlagCardAsBlock) { List originCycleTimeInfos = new ArrayList<>(); for (StatusTimeStamp columnTimeStampItem : columnTimeStamp) { @@ -62,9 +64,11 @@ private List calculateOriginCycleTime(List flagT .build()); } - double totalFlagTimeInDays = calculateTotalFlagCycleTime(flagTimeStamp); - originCycleTimeInfos - .add(CycleTimeInfo.builder().day(totalFlagTimeInDays).column(CardStepsEnum.FLAG.getValue()).build()); + if (Boolean.TRUE.equals(treatFlagCardAsBlock)) { + double totalFlagTimeInDays = calculateTotalFlagCycleTime(flagTimeStamp); + originCycleTimeInfos + .add(CycleTimeInfo.builder().day(totalFlagTimeInDays).column(CardStepsEnum.FLAG.getValue()).build()); + } return originCycleTimeInfos; } @@ -101,10 +105,8 @@ private List calculateCycleTime(List realDoneStatus, List } if (!isBlockColumnExisted(columnTimeStamp) && totalFlagTimeInDays > 0) { double blockDays = totalFlagTimeInDays - totalFlagAndRealDoneOverlapTime; - cycleTimeInfos.add(CycleTimeInfo.builder() - .day(blockDays) - .column(CardStepsEnum.BLOCK.getValue().toUpperCase()) - .build()); + cycleTimeInfos.add( + CycleTimeInfo.builder().day(blockDays).column(CardStepsEnum.FLAG.getValue().toUpperCase()).build()); } return cycleTimeInfos; diff --git a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java index f404d6525e..b08f514373 100644 --- a/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java +++ b/backend/src/test/java/heartbeat/service/jira/JiraServiceTest.java @@ -1166,7 +1166,7 @@ public void shouldProcessCustomFieldsForCardsWhenCallGetStoryPointsAndCycleTime( when(jiraFeignClient.getJiraCardHistoryByCount(any(), any(), anyInt(), anyInt(), any())) .thenReturn(CARD_HISTORY_MULTI_RESPONSE_BUILDER().build()); when(boardUtil.getCycleTimeInfos(any(), any(), any())).thenReturn(CYCLE_TIME_INFO_LIST()); - when(boardUtil.getOriginCycleTimeInfos(any())).thenReturn(CYCLE_TIME_INFO_LIST()); + when(boardUtil.getOriginCycleTimeInfos(any(), any())).thenReturn(CYCLE_TIME_INFO_LIST()); CardCollection doneCards = jiraService.getStoryPointsAndCycleTimeAndReworkInfoForDoneCards( storyPointsAndCycleTimeRequest, jiraBoardSetting.getBoardColumns(), List.of("Zhang San"), ""); diff --git a/backend/src/test/java/heartbeat/util/BoardUtilTest.java b/backend/src/test/java/heartbeat/util/BoardUtilTest.java index f676888c5b..65bfbd565f 100644 --- a/backend/src/test/java/heartbeat/util/BoardUtilTest.java +++ b/backend/src/test/java/heartbeat/util/BoardUtilTest.java @@ -81,7 +81,7 @@ void calculateCycleTimeWhenTreatFlagCardAsBlockIsFalse() { } @Test - void calculateOriginCycleTimeOfColumn() { + void shouldCalculateOriginCycleTimeGivenTreatFlagCardAsBlockIsTrue() { List statusChangedItems = StatusChangedItemsListAndCycleTimeInfosListFixture .STATUS_CHANGED_ITEMS_LIST_OF_ORIGIN(); List statusChangedItemsExpect = StatusChangedItemsListAndCycleTimeInfosListFixture @@ -89,8 +89,22 @@ void calculateOriginCycleTimeOfColumn() { when(workDay.calculateWorkDaysBy24Hours(anyLong(), anyLong())) .thenReturn(StatusChangedItemsListAndCycleTimeInfosListFixture.EXPECT_DAYS); - List result = boardUtil.getOriginCycleTimeInfos(statusChangedItems); + + List result = boardUtil.getOriginCycleTimeInfos(statusChangedItems, Boolean.TRUE); Assertions.assertEquals(statusChangedItemsExpect, result); } + @Test + void shouldCalculateOriginCycleTimeGivenTreatFlagCardAsBlockIsFalse() { + List statusChangedItems = StatusChangedItemsListAndCycleTimeInfosListFixture + .STATUS_CHANGED_ITEMS_LIST_OF_ORIGIN(); + List statusChangedItemsWithoutFlagExpect = StatusChangedItemsListAndCycleTimeInfosListFixture + .CYCLE_TIME_INFOS_LIST_OF_ORIGIN_WITHOUT_FLAG(); + + when(workDay.calculateWorkDaysBy24Hours(anyLong(), anyLong())) + .thenReturn(StatusChangedItemsListAndCycleTimeInfosListFixture.EXPECT_DAYS); + List result = boardUtil.getOriginCycleTimeInfos(statusChangedItems, Boolean.FALSE); + Assertions.assertEquals(statusChangedItemsWithoutFlagExpect, result); + } + } diff --git a/backend/src/test/java/heartbeat/util/StatusChangedItemsListAndCycleTimeInfosListFixture.java b/backend/src/test/java/heartbeat/util/StatusChangedItemsListAndCycleTimeInfosListFixture.java index 8e0c8126ed..f2cbdc7fed 100644 --- a/backend/src/test/java/heartbeat/util/StatusChangedItemsListAndCycleTimeInfosListFixture.java +++ b/backend/src/test/java/heartbeat/util/StatusChangedItemsListAndCycleTimeInfosListFixture.java @@ -9,6 +9,14 @@ public class StatusChangedItemsListAndCycleTimeInfosListFixture { public static double EXPECT_DAYS = 4.0; + public static final String DONE = "DONE"; + + public static final String BLOCK = "BLOCK"; + + public static final String IN_PROGRESS = "IN PROGRESS"; + + public static final String FLAG = "FLAG"; + public static List STATUS_CHANGED_ITEMS_LIST_OF_REAL_DONE_COLUMN() { return List.of(StatusChangedItem.builder().timestamp(1000000L).status("In Progress").build(), StatusChangedItem.builder().timestamp(2000000L).status("DONE").build(), @@ -17,9 +25,9 @@ public static List STATUS_CHANGED_ITEMS_LIST_OF_REAL_DONE_COL } public static List CYCLE_TIME_INFOS_LIST_OF_REAL_DONE_COLUMN() { - return List.of(CycleTimeInfo.builder().column("DONE").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("BLOCK").day(0.0).build(), - CycleTimeInfo.builder().column("IN PROGRESS").day(EXPECT_DAYS).build()); + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(FLAG).day(0.0).build()); } public static List STATUS_CHANGED_ITEMS_LIST_OF_BLOCK_COLUMN() { @@ -31,9 +39,9 @@ public static List STATUS_CHANGED_ITEMS_LIST_OF_BLOCK_COLUMN( } public static List CYCLE_TIME_INFOS_LIST_OF_BLOCK_COLUMN() { - return List.of(CycleTimeInfo.builder().column("DONE").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("BLOCK").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("IN PROGRESS").day(EXPECT_DAYS).build()); + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(BLOCK).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(EXPECT_DAYS).build()); } public static List STATUS_CHANGED_ITEMS_LIST_OF_OTHER_COLUMN() { @@ -45,9 +53,9 @@ public static List STATUS_CHANGED_ITEMS_LIST_OF_OTHER_COLUMN( } public static List CYCLE_TIME_INFOS_LIST_OF_OTHER_COLUMN() { - return List.of(CycleTimeInfo.builder().column("DONE").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("BLOCK").day(8.0).build(), - CycleTimeInfo.builder().column("IN PROGRESS").day(0.0).build()); + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(BLOCK).day(8.0).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(0.0).build()); } public static List STATUS_CHANGED_ITEMS_LIST_WHEN_NOT_TREAT_FLAG_AS_BLOCK() { @@ -59,9 +67,9 @@ public static List STATUS_CHANGED_ITEMS_LIST_WHEN_NOT_TREAT_F } public static List CYCLE_TIME_INFOS_LIST_WHEN_NOT_TREAT_FLAG_AS_BLOCK() { - return List.of(CycleTimeInfo.builder().column("DONE").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("BLOCK").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("IN PROGRESS").day(EXPECT_DAYS).build()); + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(BLOCK).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(EXPECT_DAYS).build()); } public static List STATUS_CHANGED_ITEMS_LIST_OF_ORIGIN() { @@ -73,10 +81,16 @@ public static List STATUS_CHANGED_ITEMS_LIST_OF_ORIGIN() { } public static List CYCLE_TIME_INFOS_LIST_OF_ORIGIN() { - return List.of(CycleTimeInfo.builder().column("DONE").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("BLOCK").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("IN PROGRESS").day(EXPECT_DAYS).build(), - CycleTimeInfo.builder().column("FLAG").day(EXPECT_DAYS).build()); + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(BLOCK).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(FLAG).day(EXPECT_DAYS).build()); + } + + public static List CYCLE_TIME_INFOS_LIST_OF_ORIGIN_WITHOUT_FLAG() { + return List.of(CycleTimeInfo.builder().column(DONE).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(BLOCK).day(EXPECT_DAYS).build(), + CycleTimeInfo.builder().column(IN_PROGRESS).day(EXPECT_DAYS).build()); } } diff --git a/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx b/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx index 52f70410c1..ef27fad62d 100644 --- a/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx +++ b/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx @@ -6,7 +6,7 @@ import { updateTreatFlagCardAsBlock, } from '@src/context/Metrics/metricsSlice'; import { BOARD_MAPPING, ERROR_MESSAGE_TIME_DURATION, LIST_OPEN, NO_RESULT_DASH } from '../../fixtures'; -import { CYCLE_TIME_SETTINGS_TYPES, METRICS_CONSTANTS } from '@src/constants/resources'; +import { CYCLE_TIME_SETTINGS_TYPES, MESSAGE, METRICS_CONSTANTS } from '@src/constants/resources'; import { act, render, screen, waitFor, within } from '@testing-library/react'; import { CycleTime } from '@src/containers/MetricsStep/CycleTime'; import { setupStore } from '../../utils/setupStoreUtil'; @@ -406,4 +406,23 @@ describe('CycleTime', () => { expect(mockedUseAppDispatch).not.toHaveBeenCalledWith(saveDoneColumn([])); }); }); + + [CYCLE_TIME_SETTINGS_TYPES.BY_STATUS, CYCLE_TIME_SETTINGS_TYPES.BY_COLUMN].forEach((cycleTimeSettingsType) => { + it('should show warning message given both mapping block column and add flag as block', () => { + (selectMetricsContent as jest.Mock).mockReturnValue({ + cycleTimeSettingsType, + cycleTimeSettings: [ + ...cycleTimeSettings, + { + column: 'Blocked', + status: 'BLOCKED', + value: 'Block', + }, + ], + }); + setup(); + + expect(screen.getByText(MESSAGE.FLAG_CARD_DROPPED_WARNING)).toBeVisible(); + }); + }); }); diff --git a/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx b/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx index 49346b1642..b2d074360d 100644 --- a/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx +++ b/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx @@ -217,7 +217,7 @@ describe('MetricsStep', () => { await userEvent.click(doneSelectTrigger as HTMLInputElement); - const noneOption = within(screen.getByRole('presentation')).getByText('----'); + const noneOption = within(screen.getAllByRole('presentation')[1]).getByText('----'); await userEvent.click(noneOption); expect(realDoneSettingSection).toHaveTextContent(SELECT_CONSIDER_AS_DONE_MESSAGE); diff --git a/frontend/__tests__/context/metricsSlice.test.ts b/frontend/__tests__/context/metricsSlice.test.ts index 8f9d4a80f9..8cfc3c9797 100644 --- a/frontend/__tests__/context/metricsSlice.test.ts +++ b/frontend/__tests__/context/metricsSlice.test.ts @@ -61,6 +61,7 @@ const initState = { classification: [], treatFlagCardAsBlock: true, assigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, + displayFlagCardDropWarning: true, importedData: { importedCrews: [], importedAssigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, @@ -304,6 +305,32 @@ describe('saveMetricsSetting reducer', () => { expect(savedMetricsSetting.doneColumn).toEqual(['DONE']); }); + it('should not be able to show conflict warning and check the flag card as block', () => { + const mockUpdateMetricsStateArguments = { + ...mockJiraResponse, + isProjectCreated: true, + }; + const savedMetricsSetting = saveMetricsSettingReducer( + { + ...initState, + cycleTimeSettingsType: CYCLE_TIME_SETTINGS_TYPES.BY_STATUS, + importedData: { + ...initState.importedData, + importedCrews: ['User B', 'User C'], + importedClassification: ['issuetype'], + importedCycleTime: { + importedCycleTimeSettings: [{ DOING: 'Doing' }, { TESTING: 'Testing' }, { DONE: 'Done' }], + importedTreatFlagCardAsBlock: false, + }, + importedDoneStatus: ['DONE'], + }, + }, + updateMetricsState(mockUpdateMetricsStateArguments), + ); + expect(savedMetricsSetting.displayFlagCardDropWarning).toEqual(false); + expect(savedMetricsSetting.treatFlagCardAsBlock).toEqual(true); + }); + it('should update metricsState given cycleTimeSettingsType is by status', () => { const mockUpdateMetricsStateArguments = { ...mockJiraResponse, @@ -336,6 +363,7 @@ describe('saveMetricsSetting reducer', () => { { column: 'Testing', status: 'TESTING', value: 'Testing' }, ]); expect(savedMetricsSetting.doneColumn).toEqual(['DONE']); + expect(savedMetricsSetting.displayFlagCardDropWarning).toEqual(true); }); it('should update metricsState given its value changed given isProjectCreated is false and selectedDoneColumns and cycleTimeSettingsType is byStatus', () => { diff --git a/frontend/__tests__/fixtures.ts b/frontend/__tests__/fixtures.ts index b88860ccac..4cfa77c67c 100644 --- a/frontend/__tests__/fixtures.ts +++ b/frontend/__tests__/fixtures.ts @@ -467,7 +467,6 @@ export const MOCK_REPORT_RESPONSE: ReportResponseDTO = { fromAnalysis: null, fromInDev: null, fromBlock: 111, - fromFlag: null, fromReview: 111, fromWaitingForTesting: 111, fromTesting: null, diff --git a/frontend/__tests__/hooks/reportMapper/report.test.tsx b/frontend/__tests__/hooks/reportMapper/report.test.tsx index e78629a51b..6a9b084fb3 100644 --- a/frontend/__tests__/hooks/reportMapper/report.test.tsx +++ b/frontend/__tests__/hooks/reportMapper/report.test.tsx @@ -171,7 +171,7 @@ export const EXPECTED_REPORT_VALUES = { ], }, { - id: 5, + id: 4, name: ( From review to in dev @@ -185,7 +185,7 @@ export const EXPECTED_REPORT_VALUES = { ], }, { - id: 6, + id: 5, name: ( From waiting for testing to in dev @@ -199,7 +199,7 @@ export const EXPECTED_REPORT_VALUES = { ], }, { - id: 8, + id: 7, name: ( From done to in dev @@ -213,7 +213,7 @@ export const EXPECTED_REPORT_VALUES = { ], }, { - id: 9, + id: 8, name: Total rework cards, valueList: [ { @@ -223,7 +223,7 @@ export const EXPECTED_REPORT_VALUES = { ], }, { - id: 10, + id: 9, name: Rework cards ratio, valueList: [ { diff --git a/frontend/e2e/fixtures/create-new/board-data-without-block-column.csv b/frontend/e2e/fixtures/create-new/board-data-without-block-column.csv new file mode 100644 index 0000000000..4e27e05de3 --- /dev/null +++ b/frontend/e2e/fixtures/create-new/board-data-without-block-column.csv @@ -0,0 +1,4 @@ +"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TESTING","OriginCycleTime: WAITING FOR TESTING","OriginCycleTime: TO DO","OriginCycleTime: IN DEV","OriginCycleTime: REVIEW","OriginCycleTime: FLAG","Rework: total - In dev","Rework: from Block","Rework: from Waiting for testing","Rework: from Done" +"TNB-1","ADM-898-card1","Task","Done","2024-04-08","0.0","Shiqi Yuan","heartbeat user","TNB","Test-no-block","Medium",,,"","0","","0","0","0","0","0","0","0","0","0","0.01","0","0.01","1","1","0","0" +"TNB-2","ADM-898-card2","Task","Done","2024-04-08","0.0","Shiqi Yuan","heartbeat user","TNB","Test-no-block","Medium",,,"","0","","0","0","0","0","0","0","0","0","0","0.01","0","0.01","2","1","0","1" +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/frontend/e2e/fixtures/create-new/board-data.csv b/frontend/e2e/fixtures/create-new/board-data.csv index 9cce0e14a2..da5ffc9569 100644 --- a/frontend/e2e/fixtures/create-new/board-data.csv +++ b/frontend/e2e/fixtures/create-new/board-data.csv @@ -1,24 +1,24 @@ -"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","Story testing-1","Flagged","Fix versions","Partner","Time tracking","Story point estimate","QA","Feature/Operation","Story testing-2","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TODO","OriginCycleTime: TESTING","OriginCycleTime: WAIT FOR TEST","OriginCycleTime: DOING","OriginCycleTime: REVIEW","OriginCycleTime: FLAG","OriginCycleTime: BLOCKED",Rework: total - In dev,Rework: from Block,Rework: from Review,Rework: from Waiting for testing,Rework: from Testing,Rework: from Done -"ADM-735","[backend]identify the source of the error when generate reports encounter exception","Task","Done","2024-01-19","1.0","Yunsong Yang","Yunsong Yang","ADM","Auto Dora Metrics","Medium","Precise on Metrics","Sprint 28","Stream2","7.70","1.0","","","","None","1.0","","","","7.70","0","2.02","1.81","0","0","3.87","3.03","0","1.81","2.02","3.87","0","0","0","0","0","0","0","0" -"ADM-708","[Backend] Verify board and obtain board data with new API","Task","Done","2024-01-19","3.0","Weiran Sun","heartbeat user","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream1","9.95","1.0","","","","None","3.0","","","","3.32","0","4.00","0.93","1.04","0.98","3.00","7.10","1.04","0.93","4.00","3.00","0","0.98","2","2","0","0","0","0" -"ADM-699","[Frontend] Optimize the 4xx&504 error display of report overview","Task","Done","2024-01-18","2.0","heartbeat user","heartbeat user","ADM","Auto Dora Metrics","Medium","Performance Improvement","Sprint 28","Stream2","10.93","1.0","","","","None","2.0","","","","5.46","0","5.14","0.04","0.78","2.01","2.96","10.75","0.78","0.04","5.14","2.96","0","2.01","2","2","0","0","0","0" -"ADM-717","[Backend] Verify github and obtain github data with new API","Task","Done","2024-01-17","2.0","Junbo Dai","Yufan Wang","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream1","8.09","1.0","","","","None","2.0","Weiran Sun","","","4.04","0","2.83","2.72","0.05","2.14","0.35","6.00","0.05","2.72","2.83","0.35","0","2.14","3","3","0","0","0","0" -"ADM-724","[Spike] redesign board verify API to meet business requirements","Spike","Done","2024-01-17","1.0","heartbeat user","Yufan Wang","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream1","12.94","","","","","None","1.0","","","","12.94","0","1.08","1.99","0","7.65","2.22","0.27","0","1.99","1.08","2.22","0","7.65","2","2","0","0","0","0" -"ADM-652","[Frontend]Generate the separate modules detail report","Task","Done","2024-01-17","3.0","Xuebing Li","heartbeat user","ADM","Auto Dora Metrics","Medium","Performance Improvement","Sprint 28","Stream2","10.15","1.0","","","","None","3.0","","","","3.38","0","5.94","1.35","1.87","0.72","0.27","22.87","1.87","1.35","5.94","0.27","0","0.72","1","1","0","0","0","0" -"ADM-683","[Frontend] UI refine for the date picker in report page","Task","Done","2024-01-17","1.0","heartbeat user","heartbeat user","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream2","8.92","1.0","","","","None","1.0","","","","8.92","0","3.00","0.10","1.84","3.00","0.98","15.05","1.84","0.10","3.00","0.98","0","3.00","1","1","0","0","0","0" -"ADM-669","[Frontend] UI refine for notification pop up change in report page","Task","Done","2024-01-17","1.0","heartbeat user","heartbeat user","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream2","7.13","1.0","","","","None","1.0","","","","7.13","0","4.22","0.02","1.16","0","1.73","17.80","1.16","0.02","4.22","1.73","0","0","0","0","0","0","0","0" -"ADM-709","[Backend] Verify buildkite and obtain buildkite data with new API","Task","Done","2024-01-15","3.0","Xinyi Wang","heartbeat user","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 27","Stream1","6.85","1.0","","","","None","3.0","","","","2.28","0","2.81","0.07","0.78","0","3.19","8.03","0.78","0.07","2.81","3.19","0","0",,,,,, -,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -"ADM-806","[BE]no need to obtain pipeline data twice in backend","Bug","Review","2024-02-26","2.0","heartbeat user","Yufan Wang","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream2,v1.1.5","0","","","","","None","2.0","","","","0","0","2.89","0","0","0","0.04","8.10","0","0","2.89","0.04","0","0",,,,,, -"ADM-813","[FE]add new field 'Advance' in metrics page","Task","Review","2024-02-26","2.0","heartbeat user","Yufan Wang","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream2,v1.1.5","0","1.0","","","","None","2.0","","","","0","0","4.78","0","0","4.68","0.53","0.19","0","0","4.78","0.53","0","4.68",,,,,, -"ADM-677","[Spike]Investigate Github graphQL API about replacing existing REST API","Spike","Blocked","2024-02-21","2.0","Junbo Dai","Yichen Wang","ADM","Auto Dora Metrics","Medium","Performance Improvement","Sprint 30","Stream1","0","","","","","None","2.0","","","","0","0","1.05","0","0","10.17","0","38.43","0","0","1.05","0","0","10.17",,,,,, -"ADM-819","[BE]cache doesn't work in one case","Bug","Doing","2024-02-26","2.0","Shiqi Yuan","Yufan Wang","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream2,v1.1.5","0","","","","","None","2.0","","","","0","0","3.14","0","0","1.05","0","0.84","0","0","3.14","0","0","1.05",,,,,, -"ADM-797","[BE]The add flag as block logic is not working","Bug","Doing","2024-02-26","2.0","heartbeat user","Wenting Yan","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream2,v1.1.5","0","","","","","None","2.0","","","","0","0","7.13","0","0","5.00","0","2.03","0","0","7.38","0","1.05","5.80",,,,,, -"ADM-829","jump home page when user click next button in config page","Bug","Doing","2024-02-23","2.0","Junbo Dai","Yufan Wang","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream1,v1.1.5","0","","","","","None","2.0","","","","0","0","1.03","0","0","0","0","1.17","0","0","1.03","0","0","0",,,,,, -"ADM-812","[FE]metrics page needs to retain the modified data","Bug","Doing","2024-02-23","2.0","heartbeat user","Yufan Wang","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream1","0","","","","","None","2.0","","","","0","0","3.04","0","0","1.03","0","6.67","0","0","3.04","0","0","1.03",,,,,, -"ADM-809","[E2E] build ""import a new project"" scenario","Task","Doing","2024-02-22","2.0","heartbeat user","Xingmeng Tao","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream1,v1.1.5","0","1.0","","","","None","2.0","","","","0","0","2.24","0","0","0","0","8.00","0","0","2.24","0","0","0",,,,,, -"ADM-808","[E2E] build ""Create a new Project"" scenario","Task","Doing","2024-02-19","3.5","heartbeat user","Xingmeng Tao","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream1,v1.1.5","0","1.0","","","","None","3.5","","","","0","0","8.04","0","0","1.99","0","0.95","0","0","8.04","0","0","1.99",,,,,, -"ADM-825","[E2E] build ""page jumps"" scenario","Task","TODO",,"2.0",,"Yufan Wang","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream1,v1.1.5","0","1.0","","","","None","2.0","","","","0","0","0","0","0","0","0","0","0","0","0","0","0","0",,,,,, -"ADM-820","user was misguided to home page when they want to enter metrics page","Bug","TODO",,"0.0","heartbeat user","Yufan Wang","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream2","0","","","","","None","","","","","","0","0","0","0","0","0","0","0","0","0","0","0","0",,,,,, -"ADM-833","[E2E] build ""unhappy path"" scenario","Task","TODO",,"0.0",,"heartbeat user","ADM","Auto Dora Metrics","Medium",,"Sprint 30","Stream1","0","1.0","","","","None","","","","","","0","0","0","0","0","0","0","0","0","0","0","0","0",,,,,, -"ADM-789","refactor E2E-step2","Task","TODO",,"1.0",,"Yufan Wang","ADM","Auto Dora Metrics","High",,"Sprint 30","Stream2","0","1.0","","","","None","1.0","","","","0","0","0","0","0","0","0","0","0","0","0","0","0","0",,,,,, +Issue key,Summary,Issue Type,Status,Status Date,Story Points,assignee,Reporter,Project Key,Project Name,Priority,Parent Summary,Sprint,Labels,Cycle Time,Story testing-1,Flagged,Fix versions,Partner,Time tracking,Story point estimate,QA,Feature/Operation,Story testing-2,Cycle Time / Story Points,Analysis Days,In Dev Days,Waiting Days,Testing Days,Block Days,Review Days,OriginCycleTime: TODO,OriginCycleTime: TESTING,OriginCycleTime: WAIT FOR TEST,OriginCycleTime: DOING,OriginCycleTime: REVIEW,OriginCycleTime: BLOCKED,Rework: total - In dev,Rework: from Block,Rework: from Review,Rework: from Waiting for testing,Rework: from Testing,Rework: from Done +ADM-735,[backend]identify the source of the error when generate reports encounter exception,Task,Done,2024-01-19,1.0,Yunsong Yang,Yunsong Yang,ADM,Auto Dora Metrics,Medium,Precise on Metrics,Sprint 28,Stream2,7.70,1.0,"","","",None,1.0,"","","",7.70,0,2.02,1.81,0,0,3.87,3.03,0,1.81,2.02,3.87,0,0,0,0,0,0,0 +ADM-708,[Backend] Verify board and obtain board data with new API,Task,Done,2024-01-19,3.0,Weiran Sun,heartbeat user,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 28,Stream1,9.95,1.0,"","","",None,3.0,"","","",3.32,0,4.00,0.93,1.04,0.98,3.00,7.10,1.04,0.93,4.00,3.00,0.98,2,2,0,0,0,0 +ADM-699,[Frontend] Optimize the 4xx&504 error display of report overview,Task,Done,2024-01-18,2.0,heartbeat user,heartbeat user,ADM,Auto Dora Metrics,Medium,Performance Improvement,Sprint 28,Stream2,10.93,1.0,"","","",None,2.0,"","","",5.46,0,5.14,0.04,0.78,2.01,2.96,10.75,0.78,0.04,5.14,2.96,2.01,2,2,0,0,0,0 +ADM-717,[Backend] Verify github and obtain github data with new API,Task,Done,2024-01-17,2.0,Junbo Dai,Yufan Wang,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 28,Stream1,8.09,1.0,"","","",None,2.0,Weiran Sun,"","",4.04,0,2.83,2.72,0.05,2.14,0.35,6.00,0.05,2.72,2.83,0.35,2.14,3,3,0,0,0,0 +ADM-724,[Spike] redesign board verify API to meet business requirements,Spike,Done,2024-01-17,1.0,heartbeat user,Yufan Wang,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 28,Stream1,12.94,"","","","",None,1.0,"","","",12.94,0,1.08,1.99,0,7.65,2.22,0.27,0,1.99,1.08,2.22,7.65,2,2,0,0,0,0 +ADM-652,[Frontend]Generate the separate modules detail report,Task,Done,2024-01-17,3.0,Xuebing Li,heartbeat user,ADM,Auto Dora Metrics,Medium,Performance Improvement,Sprint 28,Stream2,10.15,1.0,"","","",None,3.0,"","","",3.38,0,5.94,1.35,1.87,0.72,0.27,22.87,1.87,1.35,5.94,0.27,0.72,1,1,0,0,0,0 +ADM-683,[Frontend] UI refine for the date picker in report page,Task,Done,2024-01-17,1.0,heartbeat user,heartbeat user,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 28,Stream2,8.92,1.0,"","","",None,1.0,"","","",8.92,0,3.00,0.10,1.84,3.00,0.98,15.05,1.84,0.10,3.00,0.98,3.00,1,1,0,0,0,0 +ADM-669,[Frontend] UI refine for notification pop up change in report page,Task,Done,2024-01-17,1.0,heartbeat user,heartbeat user,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 28,Stream2,7.13,1.0,"","","",None,1.0,"","","",7.13,0,4.22,0.02,1.16,0,1.73,17.80,1.16,0.02,4.22,1.73,0,0,0,0,0,0,0 +ADM-709,[Backend] Verify buildkite and obtain buildkite data with new API,Task,Done,2024-01-15,3.0,Xinyi Wang,heartbeat user,ADM,Auto Dora Metrics,Medium,easy to use,Sprint 27,Stream1,6.85,1.0,"","","",None,3.0,"","","",2.28,0,2.81,0.07,0.78,0,3.19,8.03,0.78,0.07,2.81,3.19,0,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +ADM-806,[BE]no need to obtain pipeline data twice in backend,Bug,Review,2024-02-26,2.0,heartbeat user,Yufan Wang,ADM,Auto Dora Metrics,Medium,,Sprint 30,"Stream2,v1.1.5",0,"","","","",None,2.0,"","","",0,0,2.89,0,0,0,0.04,8.10,0,0,2.89,0.04,0,,,,,, +ADM-813,[FE]add new field 'Advance' in metrics page,Task,Review,2024-02-26,2.0,heartbeat user,Yufan Wang,ADM,Auto Dora Metrics,Medium,,Sprint 30,"Stream2,v1.1.5",0,1.0,"","","",None,2.0,"","","",0,0,4.78,0,0,4.68,0.53,0.19,0,0,4.78,0.53,4.68,,,,,, +ADM-677,[Spike]Investigate Github graphQL API about replacing existing REST API,Spike,Blocked,2024-02-21,2.0,Junbo Dai,Yichen Wang,ADM,Auto Dora Metrics,Medium,Performance Improvement,Sprint 30,Stream1,0,"","","","",None,2.0,"","","",0,0,1.05,0,0,10.17,0,38.43,0,0,1.05,0,10.17,,,,,, +ADM-819,[BE]cache doesn't work in one case,Bug,Doing,2024-02-26,2.0,Shiqi Yuan,Yufan Wang,ADM,Auto Dora Metrics,Medium,,Sprint 30,"Stream2,v1.1.5",0,"","","","",None,2.0,"","","",0,0,3.14,0,0,1.05,0,0.84,0,0,3.14,0,1.05,,,,,, +ADM-797,[BE]The add flag as block logic is not working,Bug,Doing,2024-02-26,2.0,heartbeat user,Wenting Yan,ADM,Auto Dora Metrics,High,,Sprint 30,"Stream2,v1.1.5",0,"","","","",None,2.0,"","","",0,0,7.13,0,0,5.00,0,2.03,0,0,7.38,0,5.80,,,,,, +ADM-829,jump home page when user click next button in config page,Bug,Doing,2024-02-23,2.0,Junbo Dai,Yufan Wang,ADM,Auto Dora Metrics,High,,Sprint 30,"Stream1,v1.1.5",0,"","","","",None,2.0,"","","",0,0,1.03,0,0,0,0,1.17,0,0,1.03,0,0,,,,,, +ADM-812,[FE]metrics page needs to retain the modified data,Bug,Doing,2024-02-23,2.0,heartbeat user,Yufan Wang,ADM,Auto Dora Metrics,Medium,,Sprint 30,Stream1,0,"","","","",None,2.0,"","","",0,0,3.04,0,0,1.03,0,6.67,0,0,3.04,0,1.03,,,,,, +ADM-809,"[E2E] build ""import a new project"" scenario",Task,Doing,2024-02-22,2.0,heartbeat user,Xingmeng Tao,ADM,Auto Dora Metrics,High,,Sprint 30,"Stream1,v1.1.5",0,1.0,"","","",None,2.0,"","","",0,0,2.24,0,0,0,0,8.00,0,0,2.24,0,0,,,,,, +ADM-808,"[E2E] build ""Create a new Project"" scenario",Task,Doing,2024-02-19,3.5,heartbeat user,Xingmeng Tao,ADM,Auto Dora Metrics,High,,Sprint 30,"Stream1,v1.1.5",0,1.0,"","","",None,3.5,"","","",0,0,8.04,0,0,1.99,0,0.95,0,0,8.04,0,1.99,,,,,, +ADM-825,"[E2E] build ""page jumps"" scenario",Task,TODO,,2.0,,Yufan Wang,ADM,Auto Dora Metrics,High,,Sprint 30,"Stream1,v1.1.5",0,1.0,"","","",None,2.0,"","","",0,0,0,0,0,0,0,0,0,0,0,0,0,,,,,, +ADM-820,user was misguided to home page when they want to enter metrics page,Bug,TODO,,0.0,heartbeat user,Yufan Wang,ADM,Auto Dora Metrics,Medium,,Sprint 30,Stream2,0,"","","","",None,"","","","","",0,0,0,0,0,0,0,0,0,0,0,0,,,,,, +ADM-833,"[E2E] build ""unhappy path"" scenario",Task,TODO,,0.0,,heartbeat user,ADM,Auto Dora Metrics,Medium,,Sprint 30,Stream1,0,1.0,"","","",None,"","","","","",0,0,0,0,0,0,0,0,0,0,0,0,,,,,, +ADM-789,refactor E2E-step2,Task,TODO,,1.0,,Yufan Wang,ADM,Auto Dora Metrics,High,,Sprint 30,Stream2,0,1.0,"","","",None,1.0,"","","",0,0,0,0,0,0,0,0,0,0,0,0,0,,,,,, diff --git a/frontend/e2e/fixtures/create-new/config-step.ts b/frontend/e2e/fixtures/create-new/config-step.ts index 4333ed331d..82c45c54c1 100644 --- a/frontend/e2e/fixtures/create-new/config-step.ts +++ b/frontend/e2e/fixtures/create-new/config-step.ts @@ -33,3 +33,22 @@ export const config = { token: process.env.E2E_TOKEN_GITHUB as string, }, }; + +export const configWithoutBlockColumn = { + projectName: 'Heartbeat Metrics', + dateRange: [ + { + startDate: '2024-04-07T00:00:00.000+08:00', + endDate: '2024-04-08T23:59:59.999+08:00', + }, + ], + calendarType: 'Calendar with Chinese Holiday', + metrics: ['Cycle time'], + board: { + type: 'Jira', + boardId: '33', + email: 'heartbeatuser2023@gmail.com', + site: 'dorametrics', + token: process.env.E2E_TOKEN_JIRA as string, + }, +}; diff --git a/frontend/e2e/fixtures/create-new/metrics-step.ts b/frontend/e2e/fixtures/create-new/metrics-step.ts index 9420861234..c0857167b2 100644 --- a/frontend/e2e/fixtures/create-new/metrics-step.ts +++ b/frontend/e2e/fixtures/create-new/metrics-step.ts @@ -73,7 +73,7 @@ export const config = { Done: 'Done', }, ], - treatFlagCardAsBlock: true, + treatFlagCardAsBlock: false, }, doneStatus: ['DONE'], classification: [ @@ -177,3 +177,25 @@ export const modifiedConfig = { }, ], }; + +export const configWithoutBlockColumn = { + crews: ['Shiqi Yuan'], + cycleTime: { + type: 'byColumn', + jiraColumns: [ + { + 'TO DO': 'To do', + }, + { + 'IN DEV': 'In Dev', + }, + { + 'WAITING FOR TESTING': 'Waiting for testing', + }, + { + Done: 'Done', + }, + ], + }, + reworkTimesSettings: { excludeStates: [], reworkState: 'In Dev' }, +}; diff --git a/frontend/e2e/fixtures/create-new/report-result.ts b/frontend/e2e/fixtures/create-new/report-result.ts index ed25741be1..f65517c11b 100644 --- a/frontend/e2e/fixtures/create-new/report-result.ts +++ b/frontend/e2e/fixtures/create-new/report-result.ts @@ -12,8 +12,8 @@ export const BOARD_METRICS_RESULT = { export const FLAG_AS_BLOCK_PROJECT_BOARD_METRICS_RESULT = { Velocity: '7.5', Throughput: '5', - AverageCycleTime4SP: '0.50', - AverageCycleTime4Card: '0.75', + AverageCycleTime4SP: '0.55', + AverageCycleTime4Card: '0.83', totalReworkTimes: '3', totalReworkCards: '3', reworkCardsRatio: '0.6000', diff --git a/frontend/e2e/fixtures/cycle-time-by-status/board-data-by-status.csv b/frontend/e2e/fixtures/cycle-time-by-status/board-data-by-status.csv index 29fd87d1ea..ebdbc9c4bb 100644 --- a/frontend/e2e/fixtures/cycle-time-by-status/board-data-by-status.csv +++ b/frontend/e2e/fixtures/cycle-time-by-status/board-data-by-status.csv @@ -1,5 +1,5 @@ -"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","story point","Flagged","Story point estimate","Design","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TO DO","OriginCycleTime: IN PROGRESS","OriginCycleTime: REVIEW","OriginCycleTime: FLAG","OriginCycleTime: ANALYSIS","OriginCycleTime: READY FOR TESTING","OriginCycleTime: DOING" -"ST-5","card5","Task","done","2024-03-29","3.0","Junbo Dai","heartbeat user","ST","status-test","Medium",,,"","1.17","","","3.0","","0.39","0","1.12","0","0","0","0.05","0.78","1.12","0.05","0","0","0","0" -"ST-6","card6","Task","done","2024-03-29","2.0","Chao Wang","heartbeat user","ST","status-test","Medium",,,"","0.98","","","2.0","","0.49","0","0.93","0.05","0","0","0","0.78","0","0","0","0.93","0.05","0" -"ST-4","card4","Task","done","2024-03-29","2.0","heartbeat user","heartbeat user","ST","status-test","Medium",,,"","0.99","","","2.0","","0.49","0","0.99","0","0","0","0","0.78","0","0","0","0","0","0.99" -,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Issue key,Summary,Issue Type,Status,Status Date,Story Points,assignee,Reporter,Project Key,Project Name,Priority,Parent Summary,Sprint,Labels,Cycle Time,story point,Flagged,Story point estimate,Design,Cycle Time / Story Points,Analysis Days,In Dev Days,Waiting Days,Testing Days,Block Days,Review Days,OriginCycleTime: TO DO,OriginCycleTime: IN PROGRESS,OriginCycleTime: REVIEW,OriginCycleTime: ANALYSIS,OriginCycleTime: READY FOR TESTING,OriginCycleTime: DOING +ST-5,card5,Task,done,2024-03-29,3.0,Junbo Dai,heartbeat user,ST,status-test,Medium,,,"",1.17,"","",3.0,"",0.39,0,1.12,0,0,0,0.05,0.78,1.12,0.05,0,0,0 +ST-6,card6,Task,done,2024-03-29,2.0,Chao Wang,heartbeat user,ST,status-test,Medium,,,"",0.98,"","",2.0,"",0.49,0,0.93,0.05,0,0,0,0.78,0,0,0.93,0.05,0 +ST-4,card4,Task,done,2024-03-29,2.0,heartbeat user,heartbeat user,ST,status-test,Medium,,,"",0.99,"","",2.0,"",0.49,0,0.99,0,0,0,0,0.78,0,0,0,0,0.99 +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/frontend/e2e/fixtures/cycle-time-by-status/cycle-time-by-column-fixture.ts b/frontend/e2e/fixtures/cycle-time-by-status/cycle-time-by-column-fixture.ts index a8103adeee..864ef55cf0 100644 --- a/frontend/e2e/fixtures/cycle-time-by-status/cycle-time-by-column-fixture.ts +++ b/frontend/e2e/fixtures/cycle-time-by-status/cycle-time-by-column-fixture.ts @@ -60,7 +60,7 @@ export const cycleTimeByColumnFixture = { Done: 'Done', }, ], - treatFlagCardAsBlock: true, + treatFlagCardAsBlock: false, }, doneStatus: ['DONE'], classification: [ diff --git a/frontend/e2e/fixtures/import-file/board-data-without-block-column.csv b/frontend/e2e/fixtures/import-file/board-data-without-block-column.csv new file mode 100644 index 0000000000..098a9b4e3d --- /dev/null +++ b/frontend/e2e/fixtures/import-file/board-data-without-block-column.csv @@ -0,0 +1,7 @@ +"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TESTING","OriginCycleTime: TO DO","OriginCycleTime: IN PROGRESS","OriginCycleTime: REVIEW","OriginCycleTime: FLAG","Rework: total - In dev","Rework: from Block","Rework: from Review","Rework: from Testing","Rework: from Done" +"TFB-6","test - 1","Task","Done","2024-04-09","1.5","heartbeat user","heartbeat user","TFB","E2E-Test-For-Flag-As-Block","Medium",,,"","0.70","0.47","0","0.70","0","0","0","0","0","0","0.86","0","0.16","1","1","0","0","0" +"TFB-7","test - 2","Task","Done","2024-04-09","2.0","heartbeat user","heartbeat user","TFB","E2E-Test-For-Flag-As-Block","Medium",,,"","0.75","0.38","0","0.73","0","0","0","0.02","0","0","0.84","0.02","0.11","1","1","0","0","0" +"TFB-8","test - 3","Task","Done","2024-04-09","1.0","heartbeat user","heartbeat user","TFB","E2E-Test-For-Flag-As-Block","Medium",,,"","0.70","0.70","0","0.70","0","0","0","0","0","0","0.81","0","0.11","1","1","0","0","0" +"TFB-10","test - 5","Task","Done","2024-04-09","2.0","heartbeat user","heartbeat user","TFB","E2E-Test-For-Flag-As-Block","Medium",,,"","0.81","0.41","0","0.81","0","0","0","0","0","0","0.81","0","0","0","0","0","0","0" +"TFB-9","test - 4","Task","Done","2024-04-09","1.0","heartbeat user","heartbeat user","TFB","E2E-Test-For-Flag-As-Block","Medium",,,"","0.81","0.81","0","0.81","0","0","0","0","0","0","0.81","0","0","0","0","0","0","0" +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/frontend/e2e/fixtures/import-file/board-data.csv b/frontend/e2e/fixtures/import-file/board-data.csv index 13aca7665b..e1910f5730 100644 --- a/frontend/e2e/fixtures/import-file/board-data.csv +++ b/frontend/e2e/fixtures/import-file/board-data.csv @@ -1,4 +1,4 @@ -"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","Story testing-1","Flagged","Fix versions","Partner","Time tracking","Story point estimate","QA","Feature/Operation","Story testing-2","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TODO","OriginCycleTime: TESTING","OriginCycleTime: WAIT FOR TEST","OriginCycleTime: DOING","OriginCycleTime: REVIEW","OriginCycleTime: FLAG","OriginCycleTime: BLOCKED" +"Issue key","Summary","Issue Type","Status","Status Date","Story Points","assignee","Reporter","Project Key","Project Name","Priority","Parent Summary","Sprint","Labels","Cycle Time","Story testing-1","Flagged","Fix versions","Partner","Time tracking","Story point estimate","QA","Feature/Operation","Story testing-2","Cycle Time / Story Points","Analysis Days","In Dev Days","Waiting Days","Testing Days","Block Days","Review Days","OriginCycleTime: TODO","OriginCycleTime: TESTING","OriginCycleTime: WAIT FOR TEST","OriginCycleTime: DOING","OriginCycleTime: REVIEW","OriginCycleTime: BLOCKED" "ADM-735","[backend]identify the source of the error when generate reports encounter exception","Task","Done","2024-01-19","1.0","Yunsong Yang","Yunsong Yang","ADM","Auto Dora Metrics","Medium","Precise on Metrics","Sprint 28","Stream2","7.70","1.0","","","","None","1.0","","","","7.70","0","2.02","1.81","0","0","3.87","3.03","0","1.81","2.02","3.87","0","0" "ADM-708","[Backend] Verify board and obtain board data with new API","Task","Done","2024-01-19","3.0","Weiran Sun","heartbeat user","ADM","Auto Dora Metrics","Medium","easy to use","Sprint 28","Stream1","9.95","1.0","","","","None","3.0","","","","3.32","0","4.00","0.93","1.04","0.98","3.00","7.10","1.04","0.93","4.00","3.00","0","0.98" "ADM-699","[Frontend] Optimize the 4xx&504 error display of report overview","Task","Done","2024-01-18","2.0","heartbeat user","heartbeat user","ADM","Auto Dora Metrics","Medium","Performance Improvement","Sprint 28","Stream2","10.93","1.0","","","","None","2.0","","","","5.46","0","5.14","0.04","0.78","2.01","2.96","10.75","0.78","0.04","5.14","2.96","0","2.01" diff --git a/frontend/e2e/pages/metrics/config-step.ts b/frontend/e2e/pages/metrics/config-step.ts index 8fe53851cf..bc03e04b3e 100644 --- a/frontend/e2e/pages/metrics/config-step.ts +++ b/frontend/e2e/pages/metrics/config-step.ts @@ -47,11 +47,11 @@ export class ConfigStep { readonly requiredMetricsVelocityOption: Locator; readonly requiredMetricsCycleTimeOption: Locator; readonly requiredMetricsClassificationOption: Locator; - readonly requiredMetricsReworkTimesOption: Locator; readonly requiredMetricsLeadTimeForChangesOption: Locator; readonly requiredMetricsDeploymentFrequencyOption: Locator; readonly requiredMetricsChangeFailureRateOption: Locator; readonly requiredMetricsMeanTimeToRecoveryOption: Locator; + readonly requiredMetricsReworkTimesOption: Locator; readonly boardContainer: Locator; readonly boardTypeSelect: Locator; readonly boardIdInput: Locator; @@ -121,6 +121,7 @@ export class ConfigStep { this.requiredMetricsDeploymentFrequencyOption = page.getByRole('option', { name: 'Deployment frequency' }); this.requiredMetricsChangeFailureRateOption = page.getByRole('option', { name: 'Change failure rate' }); this.requiredMetricsMeanTimeToRecoveryOption = page.getByRole('option', { name: 'Mean time to recovery' }); + this.requiredMetricsReworkTimesOption = page.getByRole('option', { name: 'Rework times' }); this.boardContainer = page.getByLabel('Board Config'); this.boardTypeSelect = this.boardContainer.getByLabel('Board *'); diff --git a/frontend/e2e/pages/metrics/metrics-step.ts b/frontend/e2e/pages/metrics/metrics-step.ts index 8d4b346586..cd1cad57b1 100644 --- a/frontend/e2e/pages/metrics/metrics-step.ts +++ b/frontend/e2e/pages/metrics/metrics-step.ts @@ -25,20 +25,24 @@ export class MetricsStep { readonly boardByColumnRadioBox: Locator; readonly boardByStatusRadioBox: Locator; readonly boardCycleTimeSelectForTODO: Locator; - readonly boardCycleTimeSelectForTO_DO_BYSTATUS: Locator; + readonly boardCycleTimeSelectForTO_DO: Locator; readonly boardCycleTimeSelectForDoing: Locator; + readonly boardCycleTimeSelectForIN_DEV: Locator; readonly boardCycleTimeSelectForInProgress: Locator; readonly boardCycleTimeSelectForAnalysis: Locator; readonly boardCycleTimeSelectForBlocked: Locator; readonly boardCycleTimeSelectForReview: Locator; readonly boardCycleTimeSelectForREADY: Locator; readonly boardCycleTimeSelectForWAITFORTESTING: Locator; + readonly boardCycleTimeSelectForWAIT_FOR_TESTING: Locator; readonly boardCycleTimeSelectForTesting: Locator; + readonly boardCycleTimeSelectForTESTING: Locator; readonly boardCycleTimeSelectForDone: Locator; readonly boardCycleTimeInputForTODO: Locator; readonly boardCycleTimeInputForDoing: Locator; readonly boardCycleTimeInputForBlocked: Locator; readonly boardCycleTimeInputForReview: Locator; + readonly boardCycleTimeInputForREVIEW: Locator; readonly boardCycleTimeInputForREADY: Locator; readonly boardCycleTimeInputForTesting: Locator; readonly boardCycleTimeInputForDone: Locator; @@ -102,12 +106,15 @@ export class MetricsStep { this.boardCycleTimeSelectForTODO = this.boardCycleTimeSection .getByLabel('Cycle time select for TODO') .getByLabel('Open'); - this.boardCycleTimeSelectForTO_DO_BYSTATUS = this.boardCycleTimeSection + this.boardCycleTimeSelectForTO_DO = this.boardCycleTimeSection .getByLabel('Cycle time select for TO DO') .getByLabel('Open'); this.boardCycleTimeSelectForDoing = this.boardCycleTimeSection .getByLabel('Cycle time select for Doing') .getByLabel('Open'); + this.boardCycleTimeSelectForIN_DEV = this.boardCycleTimeSection + .getByLabel('Cycle time select for IN DEV') + .getByLabel('Open'); this.boardCycleTimeSelectForInProgress = this.boardCycleTimeSection .getByLabel('Cycle time select for IN PROGRESS') .getByLabel('Open'); @@ -126,9 +133,15 @@ export class MetricsStep { this.boardCycleTimeSelectForWAITFORTESTING = this.boardCycleTimeSection .getByLabel('Cycle time select for WAIT FOR TEST') .getByLabel('Open'); + this.boardCycleTimeSelectForWAIT_FOR_TESTING = this.boardCycleTimeSection + .getByLabel('Cycle time select for WAITING FOR TESTING') + .getByLabel('Open'); this.boardCycleTimeSelectForTesting = this.boardCycleTimeSection .getByLabel('Cycle time select for Testing') .getByLabel('Open'); + this.boardCycleTimeSelectForTESTING = this.boardCycleTimeSection + .getByLabel('Cycle time select for TESTING') + .getByLabel('Open'); this.boardCycleTimeSelectForDone = this.boardCycleTimeSection .getByLabel('Cycle time select for Done') .getByLabel('Open'); @@ -144,6 +157,9 @@ export class MetricsStep { this.boardCycleTimeInputForReview = this.boardCycleTimeSection .getByLabel('Cycle time select for Review') .getByRole('combobox'); + this.boardCycleTimeInputForREVIEW = this.boardCycleTimeSection + .getByLabel('Cycle time select for REVIEW') + .getByRole('combobox'); this.boardCycleTimeInputForREADY = this.boardCycleTimeSection .getByLabel('Cycle time select for WAIT FOR TEST') .getByRole('combobox'); @@ -398,7 +414,7 @@ export class MetricsStep { [todoOption, doingOption, blockOption, reviewOption, forReadyOption, testingOption, doneOption]: string[], isByColumn: boolean, ) { - await this.boardCycleTimeSelectForTO_DO_BYSTATUS.click(); + await this.boardCycleTimeSelectForTO_DO.click(); await this.page.getByRole('option', { name: todoOption }).click(); await this.boardCycleTimeSelectForDoing.click(); @@ -428,6 +444,20 @@ export class MetricsStep { await this.page.getByRole('option', { name: doneOption }).click(); } + async selectHeartbeatStateWithoutBlock([todoOption, inDevOption, waitForTestingOption, doneOption]: string[]) { + await this.boardCycleTimeSelectForTO_DO.click(); + await this.page.getByRole('option', { name: todoOption }).click(); + + await this.boardCycleTimeSelectForIN_DEV.click(); + await this.page.getByRole('option', { name: inDevOption }).click(); + + await this.boardCycleTimeSelectForWAIT_FOR_TESTING.click(); + await this.page.getByRole('option', { name: waitForTestingOption }).click(); + + await this.boardCycleTimeSelectForDone.click(); + await this.page.getByRole('option', { name: doneOption }).click(); + } + async checkHeartbeatStateIsSet( [todoOption, doingOption, blockOption, reviewOption, forReadyOption, testingOption, doneOption]: string[], isByColumn: boolean, diff --git a/frontend/e2e/pages/metrics/report-step.ts b/frontend/e2e/pages/metrics/report-step.ts index 904c75f863..28d5c890d2 100644 --- a/frontend/e2e/pages/metrics/report-step.ts +++ b/frontend/e2e/pages/metrics/report-step.ts @@ -356,6 +356,21 @@ export class ReportStep { await this.backButton.click(); } + async checkBoardDownloadDataWithoutBlock(fileName: string) { + await downloadFileAndCheck( + this.page, + this.exportBoardData, + 'board-data-without-block-column.csv', + async (fileDataString) => { + const localCsvFile = fs.readFileSync(path.resolve(__dirname, fileName)); + const localCsv = parse(localCsvFile); + const downloadCsv = parse(fileDataString); + + expect(localCsv).toStrictEqual(downloadCsv); + }, + ); + } + async checkDoraMetrics( prLeadTime: string, pipelineLeadTime: string, diff --git a/frontend/e2e/specs/major-path/create-a-new-project.spec.ts b/frontend/e2e/specs/major-path/create-a-new-project.spec.ts index 018291b331..9784022991 100644 --- a/frontend/e2e/specs/major-path/create-a-new-project.spec.ts +++ b/frontend/e2e/specs/major-path/create-a-new-project.spec.ts @@ -1,3 +1,5 @@ +import { configWithoutBlockColumn as metricsStepWithoutBlockColumnData } from '../../fixtures/create-new/metrics-step'; +import { configWithoutBlockColumn as configWithoutBlockColumnData } from '../../fixtures/create-new/config-step'; import { cycleTimeByStatusFixture } from '../../fixtures/cycle-time-by-status/cycle-time-by-status-fixture'; import { BOARD_METRICS_RESULT, DORA_METRICS_RESULT } from '../../fixtures/create-new/report-result'; import { config as metricsStepData } from '../../fixtures/create-new/metrics-step'; @@ -50,7 +52,6 @@ test('Create a new project', async ({ homePage, configStep, metricsStep, reportS await metricsStep.checkBoardConfigurationVisible(); await metricsStep.checkPipelineConfigurationVisible(); await metricsStep.checkLastAssigneeCrewFilterChecked(); - await metricsStep.checkCycleTimeConsiderCheckboxChecked(); await metricsStep.checkCycleTimeSettingIsByColumn(); await metricsStep.waitForHiddenLoading(); await metricsStep.selectCrews(metricsStepData.crews); @@ -97,3 +98,46 @@ test('Create a new project', async ({ homePage, configStep, metricsStep, reportS await reportStep.checkDoraMetricsDetails(ProjectCreationType.CREATE_A_NEW_PROJECT); await reportStep.checkMetricDownloadData(); }); + +test('Create a new project without block column in boarding mapping', async ({ + homePage, + configStep, + metricsStep, + reportStep, +}) => { + const dateRange = { + startDate: format(configWithoutBlockColumnData.dateRange[0].startDate), + endDate: format(configWithoutBlockColumnData.dateRange[0].endDate), + }; + + await homePage.goto(); + await homePage.createANewProject(); + await configStep.waitForShown(); + await configStep.typeInProjectName(configWithoutBlockColumnData.projectName); + await configStep.selectRegularCalendar(configWithoutBlockColumnData.calendarType); + await configStep.typeInDateRange(dateRange); + await configStep.selectReworkTimesRequiredMetrics(); + await configStep.checkBoardFormVisible(); + await configStep.checkPipelineToolFormInvisible(); + await configStep.checkSourceControlFormInvisible(); + await configStep.fillAndVerifyBoardConfig(configWithoutBlockColumnData.board); + await configStep.validateNextButtonClickable(); + await configStep.goToMetrics(); + + await metricsStep.checkBoardConfigurationVisible(); + await metricsStep.checkPipelineConfigurationInvisible(); + await metricsStep.checkClassificationSettingInvisible(); + await metricsStep.selectCrews(metricsStepWithoutBlockColumnData.crews); + await metricsStep.selectCycleTimeSettingsType(metricsStepWithoutBlockColumnData.cycleTime.type); + await metricsStep.checkCycleTimeConsiderCheckboxChecked(); + await metricsStep.selectHeartbeatStateWithoutBlock( + metricsStepWithoutBlockColumnData.cycleTime.jiraColumns.map( + (jiraToHBSingleMap) => Object.values(jiraToHBSingleMap)[0], + ), + ); + await metricsStep.selectReworkSettings(metricsStepWithoutBlockColumnData.reworkTimesSettings); + + await metricsStep.goToReportPage(); + await reportStep.confirmGeneratedReport(); + await reportStep.checkBoardDownloadDataWithoutBlock('../../fixtures/create-new/board-data-without-block-column.csv'); +}); diff --git a/frontend/e2e/specs/major-path/cycle-time-by-status-test.spec.ts b/frontend/e2e/specs/major-path/cycle-time-by-status-test.spec.ts index 0bb40ef345..d75bf914a5 100644 --- a/frontend/e2e/specs/major-path/cycle-time-by-status-test.spec.ts +++ b/frontend/e2e/specs/major-path/cycle-time-by-status-test.spec.ts @@ -27,7 +27,6 @@ test('Create a new project with cycle time by status', async ({ homePage, config await metricsStep.waitForShown(); await metricsStep.validateNextButtonNotClickable(); await metricsStep.checkLastAssigneeCrewFilterChecked(); - await metricsStep.checkCycleTimeConsiderCheckboxChecked(); await metricsStep.checkCycleTimeSettingIsByColumn(); await metricsStep.waitForHiddenLoading(); await metricsStep.selectCrews(cycleTimeByStatusFixture.crews); @@ -65,7 +64,6 @@ test('Create a new project with cycle time by status', async ({ homePage, config await metricsStep.waitForShown(); await metricsStep.validateNextButtonNotClickable(); await metricsStep.checkLastAssigneeCrewFilterChecked(); - await metricsStep.checkCycleTimeConsiderCheckboxChecked(); await metricsStep.checkCycleTimeSettingIsByColumn(); await metricsStep.waitForHiddenLoading(); await metricsStep.selectCrews(cycleTimeByColumnFixture.crews); diff --git a/frontend/e2e/specs/major-path/import-project-from-file.spec.ts b/frontend/e2e/specs/major-path/import-project-from-file.spec.ts index 16cbf741a6..7f69a111eb 100644 --- a/frontend/e2e/specs/major-path/import-project-from-file.spec.ts +++ b/frontend/e2e/specs/major-path/import-project-from-file.spec.ts @@ -68,15 +68,22 @@ test('Import project from file', async ({ homePage, configStep, metricsStep, rep await reportStep.checkDownloadReports(); }); -test('Import project from flag as block', async ({ homePage, configStep, metricsStep, reportStep }) => { +test('Import project from flag as block and without block column', async ({ + homePage, + configStep, + metricsStep, + reportStep, +}) => { await homePage.goto(); await homePage.importProjectFromFile('../fixtures/input-files/add-flag-as-block-config-file.json'); await configStep.verifyBoardConfig(); await configStep.goToMetrics(); await metricsStep.waitForShown(); + await metricsStep.checkCycleTimeConsiderCheckboxChecked(); await metricsStep.goToReportPage(); + await reportStep.confirmGeneratedReport(); await reportStep.checkBoardMetrics( FLAG_AS_BLOCK_PROJECT_BOARD_METRICS_RESULT.Velocity, FLAG_AS_BLOCK_PROJECT_BOARD_METRICS_RESULT.Throughput, @@ -87,4 +94,5 @@ test('Import project from flag as block', async ({ homePage, configStep, metrics FLAG_AS_BLOCK_PROJECT_BOARD_METRICS_RESULT.reworkCardsRatio, FLAG_AS_BLOCK_PROJECT_BOARD_METRICS_RESULT.throughput, ); + await reportStep.checkBoardDownloadDataWithoutBlock('../../fixtures/import-file/board-data-without-block-column.csv'); }); diff --git a/frontend/e2e/specs/major-path/page-jumps.spec.ts b/frontend/e2e/specs/major-path/page-jumps.spec.ts index b319037ae3..08c4fc1dcb 100644 --- a/frontend/e2e/specs/major-path/page-jumps.spec.ts +++ b/frontend/e2e/specs/major-path/page-jumps.spec.ts @@ -28,7 +28,6 @@ test('Page jump for import', async ({ homePage, configStep, metricsStep, reportS await metricsStep.selectCrews(modifiedMetricsStepData.crews); await metricsStep.selectCycleTimeSettingsType(modifiedMetricsStepData.cycleTime.type); await metricsStep.selectModifiedHeartbeatState(modifiedHbStateData); - await metricsStep.selectCycleTimeConsiderAsBlockCheckbox(); await metricsStep.selectClassifications(modifiedMetricsStepData.classification); await metricsStep.selectReworkSettings(metricsStepData.reworkTimesSettings); await metricsStep.goToReportPage(); @@ -38,7 +37,6 @@ test('Page jump for import', async ({ homePage, configStep, metricsStep, reportS await metricsStep.checkCrews(modifiedMetricsStepData.crews); await metricsStep.checkBoardByStatusRadioBoxChecked(); await metricsStep.checkModifiedHeartbeatState(modifiedHbStateData); - await metricsStep.checkCycleTimeConsiderAsBlockUnchecked(); await metricsStep.checkClassifications(modifiedMetricsStepData.classification); await metricsStep.checkReworkSettings(metricsStepData.reworkTimesSettings); diff --git a/frontend/src/clients/report/ReportClient.ts b/frontend/src/clients/report/ReportClient.ts index 7d7127fc30..811352c415 100644 --- a/frontend/src/clients/report/ReportClient.ts +++ b/frontend/src/clients/report/ReportClient.ts @@ -31,7 +31,6 @@ export class ReportClient extends HttpClient { reworkState: 'Done', fromAnalysis: 0, fromInDev: 0, - fromFlag: 0, fromBlock: 0, fromWaitingForTesting: 0, fromTesting: 0, diff --git a/frontend/src/clients/report/dto/response.ts b/frontend/src/clients/report/dto/response.ts index 4219fae83c..c9b1b7e64f 100644 --- a/frontend/src/clients/report/dto/response.ts +++ b/frontend/src/clients/report/dto/response.ts @@ -49,7 +49,6 @@ export interface ReworkTimeResponse { fromAnalysis: number | null; fromInDev: number | null; fromBlock: number | null; - fromFlag: number | null; fromWaitingForTesting: number | null; fromTesting: number | null; fromReview: number | null; diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index 1efe24ee53..34e1041d09 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -218,7 +218,6 @@ export const REWORK_TIME_MAPPING = { fromAnalysis: 'analysis', fromInDev: 'in dev', fromBlock: 'block', - fromFlag: 'flag', fromReview: 'review', fromWaitingForTesting: 'waiting for testing', fromTesting: 'testing', @@ -231,7 +230,6 @@ export const REWORK_BOARD_STATUS: string[] = [ REWORK_TIME_MAPPING.fromAnalysis, REWORK_TIME_MAPPING.fromInDev, REWORK_TIME_MAPPING.fromBlock, - REWORK_TIME_MAPPING.fromFlag, REWORK_TIME_MAPPING.fromWaitingForTesting, REWORK_TIME_MAPPING.fromTesting, REWORK_TIME_MAPPING.fromReview, @@ -268,6 +266,7 @@ export const MESSAGE = { HOME_VERIFY_IMPORT_WARNING: 'The content of the imported JSON file is empty. Please confirm carefully', CONFIG_PAGE_VERIFY_IMPORT_ERROR: 'Imported data is not perfectly matched. Please review carefully before going next!', CLASSIFICATION_WARNING: 'Some classifications in import data might be removed.', + FLAG_CARD_DROPPED_WARNING: 'Please note: ’consider the “Flag” as “Block” ‘ has been dropped!', REAL_DONE_WARNING: 'Some selected doneStatus in import data might be removed', ORGANIZATION_WARNING: 'This organization in import data might be removed', PIPELINE_NAME_WARNING: 'This Pipeline in import data might be removed', @@ -427,3 +426,5 @@ export const REMOVE_BUTTON_TEXT = 'Remove'; export const MAX_TIME_RANGE_AMOUNT = 6; export const START_DATE_INVALID_TEXT = 'Start date is invalid'; export const END_DATE_INVALID_TEXT = 'End date is invalid'; + +export const BLOCK_COLUMN_NAME = ['BLOCKED', 'BLOCK']; diff --git a/frontend/src/containers/MetricsStep/CycleTime/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/index.tsx index 2152dbc3e5..1fcf12999d 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/index.tsx @@ -1,20 +1,50 @@ +import { + selectCycleTimeWarningMessage, + selectDisplayFlagCardDropWarning, + selectMetricsContent, + selectTreatFlagCardAsBlock, + updateDisplayFlagCardDropWarning, + updateTreatFlagCardAsBlock, +} from '@src/context/Metrics/metricsSlice'; import SectionTitleWithTooltip from '@src/components/Common/SectionTitleWithTooltip'; -import { selectCycleTimeWarningMessage } from '@src/context/Metrics/metricsSlice'; import { WarningNotification } from '@src/components/Common/WarningNotification'; import CycleTimeTable from '@src/containers/MetricsStep/CycleTime/Table'; import FlagCard from '@src/containers/MetricsStep/CycleTime/FlagCard'; -import { TIPS } from '@src/constants/resources'; +import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { MESSAGE, TIPS } from '@src/constants/resources'; +import { useEffect, useMemo, useState } from 'react'; +import { existBlockColumn } from '@src/utils/util'; import { useAppSelector } from '@src/hooks'; export const CycleTime = () => { + const dispatch = useAppDispatch(); + const flagCardAsBlock = useAppSelector(selectTreatFlagCardAsBlock); + const displayFlagCardDropWarning = useAppSelector(selectDisplayFlagCardDropWarning); const warningMessage = useAppSelector(selectCycleTimeWarningMessage); + const { cycleTimeSettings, cycleTimeSettingsType } = useAppSelector(selectMetricsContent); + const hasBlockColumn = useMemo(() => { + return existBlockColumn(cycleTimeSettingsType, cycleTimeSettings); + }, [cycleTimeSettingsType, cycleTimeSettings]); + const [shouldShowConflictMessage, setShouldShowConflictMessage] = useState(false); + + useEffect(() => { + if (hasBlockColumn && displayFlagCardDropWarning) { + setShouldShowConflictMessage(true); + dispatch(updateDisplayFlagCardDropWarning(false)); + } + + if (hasBlockColumn && flagCardAsBlock) { + dispatch(updateTreatFlagCardAsBlock(false)); + } + }, [dispatch, flagCardAsBlock, displayFlagCardDropWarning, hasBlockColumn]); return (
+ {shouldShowConflictMessage && } {warningMessage && } - + {hasBlockColumn || }
); }; diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index 2b6316b564..9fb44e60fb 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -5,7 +5,7 @@ import { MESSAGE, METRICS_CONSTANTS, } from '@src/constants/resources'; -import { convertCycleTimeSettings, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; +import { convertCycleTimeSettings, existBlockColumn, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice'; import { createSlice } from '@reduxjs/toolkit'; import camelCase from 'lodash.camelcase'; @@ -51,6 +51,7 @@ export interface ISavedMetricsSettingState { deploymentFrequencySettings: IPipelineConfig[]; leadTimeForChanges: IPipelineConfig[]; treatFlagCardAsBlock: boolean; + displayFlagCardDropWarning: boolean; assigneeFilter: string; firstTimeRoadMetricData: boolean; importedData: { @@ -86,6 +87,7 @@ const initialState: ISavedMetricsSettingState = { deploymentFrequencySettings: [], leadTimeForChanges: [{ id: 0, organization: '', pipelineName: '', step: '', branches: [] }], treatFlagCardAsBlock: true, + displayFlagCardDropWarning: true, assigneeFilter: ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE, firstTimeRoadMetricData: true, importedData: { @@ -290,6 +292,20 @@ function resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue: string[] }; } +function initTreatFlagCardAsBlock( + preTreatFlagCardAsBlock: boolean, + preHasBlockColumn: boolean, + state: ISavedMetricsSettingState, +) { + if ( + !preTreatFlagCardAsBlock && + preHasBlockColumn && + !existBlockColumn(state.cycleTimeSettingsType, state.cycleTimeSettings) + ) { + state.treatFlagCardAsBlock = true; + } +} + export const metricsSlice = createSlice({ name: 'metrics', initialState, @@ -380,6 +396,11 @@ export const metricsSlice = createSlice({ const preJiraColumnsValue = getSortedAndDeduplicationBoardingMapping(state.cycleTimeSettings).filter( (item) => item !== METRICS_CONSTANTS.cycleTimeEmptyStr, ); + const preHasBlockColumn = existBlockColumn(state.cycleTimeSettingsType, state.cycleTimeSettings); + const preTreatFlagCardAsBlock = state.treatFlagCardAsBlock; + + state.displayFlagCardDropWarning = + state.displayFlagCardDropWarning && !isProjectCreated && importedCycleTime.importedTreatFlagCardAsBlock; state.users = isProjectCreated ? setCreateSelectUsers(state, users) : setImportSelectUsers(state, users, importedCrews); @@ -442,6 +463,7 @@ export const metricsSlice = createSlice({ ? getCycleTimeSettingsByColumn(state, jiraColumns) : getCycleTimeSettingsByStatus(state, jiraColumns); } + initTreatFlagCardAsBlock(preTreatFlagCardAsBlock, preHasBlockColumn, state); resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue, state); if (!isProjectCreated && importedDoneStatus.length > 0) { @@ -457,11 +479,6 @@ export const metricsSlice = createSlice({ importedAssigneeFilter === ASSIGNEE_FILTER_TYPES.HISTORICAL_ASSIGNEE ? importedAssigneeFilter : ASSIGNEE_FILTER_TYPES.LAST_ASSIGNEE; - - state.treatFlagCardAsBlock = - typeof importedCycleTime.importedTreatFlagCardAsBlock === 'boolean' - ? importedCycleTime.importedTreatFlagCardAsBlock - : true; }, updatePipelineSettings: (state, action) => { @@ -575,6 +592,10 @@ export const metricsSlice = createSlice({ state.treatFlagCardAsBlock = action.payload; }, + updateDisplayFlagCardDropWarning: (state, action) => { + state.displayFlagCardDropWarning = action.payload; + }, + updateAssigneeFilter: (state, action) => { state.assigneeFilter = action.payload; }, @@ -607,6 +628,7 @@ export const { updateMetricsImportedData, initDeploymentFrequencySettings, updateTreatFlagCardAsBlock, + updateDisplayFlagCardDropWarning, updateAssigneeFilter, updateMetricsState, updatePipelineSettings, @@ -630,6 +652,7 @@ export const selectCycleTimeSettings = (state: RootState) => state.metrics.cycle export const selectMetricsContent = (state: RootState) => state.metrics; export const selectAdvancedSettings = (state: RootState) => state.metrics.importedData.importedAdvancedSettings; export const selectTreatFlagCardAsBlock = (state: RootState) => state.metrics.treatFlagCardAsBlock; +export const selectDisplayFlagCardDropWarning = (state: RootState) => state.metrics.displayFlagCardDropWarning; export const selectAssigneeFilter = (state: RootState) => state.metrics.assigneeFilter; export const selectCycleTimeWarningMessage = (state: RootState) => state.metrics.cycleTimeWarningMessage; export const selectClassificationWarningMessage = (state: RootState) => state.metrics.classificationWarningMessage; diff --git a/frontend/src/hooks/useVerifyBoardEffect.ts b/frontend/src/hooks/useVerifyBoardEffect.ts index d97a3a6d17..d44643a4f2 100644 --- a/frontend/src/hooks/useVerifyBoardEffect.ts +++ b/frontend/src/hooks/useVerifyBoardEffect.ts @@ -1,6 +1,5 @@ import { BOARD_TYPES, AXIOS_REQUEST_ERROR_CODE, MESSAGE, UNKNOWN_ERROR_TITLE } from '@src/constants/resources'; import { selectBoard, updateBoard, updateBoardVerifyState } from '@src/context/config/configSlice'; -import { updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice'; import { findCaseInsensitiveType, getJiraBoardToken } from '@src/utils/util'; import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons'; @@ -187,7 +186,6 @@ export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { const verifyJira = async () => { setIsLoading(true); - dispatch(updateTreatFlagCardAsBlock(true)); const boardInfo = getBoardInfo(fields) as BoardRequestDTO; try { const res: { response: Record } = await boardClient.getVerifyBoard({ diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 0d62c6c58e..65f08103a5 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -1,4 +1,9 @@ -import { CYCLE_TIME_LIST, CYCLE_TIME_SETTINGS_TYPES, METRICS_CONSTANTS } from '@src/constants/resources'; +import { + BLOCK_COLUMN_NAME, + CYCLE_TIME_LIST, + CYCLE_TIME_SETTINGS_TYPES, + METRICS_CONSTANTS, +} from '@src/constants/resources'; import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/constants/emojis/emoji'; import { ICycleTimeSetting, IPipelineConfig } from '@src/context/Metrics/metricsSlice'; import { ITargetFieldType } from '@src/components/Common/MultiAutoComplete/styles'; @@ -156,3 +161,12 @@ export function convertCycleTimeSettings( } return cycleTimeSettings?.map(({ status, value }: ICycleTimeSetting) => ({ [status]: value })); } + +export function existBlockColumn( + cycleTimeSettingsType: CYCLE_TIME_SETTINGS_TYPES, + cycleTimeSettings: ICycleTimeSetting[], +) { + return cycleTimeSettingsType === CYCLE_TIME_SETTINGS_TYPES.BY_COLUMN + ? cycleTimeSettings.some(({ column }) => BLOCK_COLUMN_NAME.includes(column.toUpperCase())) + : cycleTimeSettings.some(({ status }) => BLOCK_COLUMN_NAME.includes(status.toUpperCase())); +} From 6b43c85fc41dfba195835b41d69e69ffb4b3b3ca Mon Sep 17 00:00:00 2001 From: yulongcai <141199398+yulongcai@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:00:15 +0800 Subject: [PATCH 02/11] ADM-919[backend] : Speed up the generating of pipeline data in report page and add foru column in csv (#1361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ADM-919:[backend] feat: Speed up the generating of pipeline data in report page * ADM-919:[backend] feat: fix e2e test * fix(e2e): fix env * Adm 920[backend] feat: Support to retrieve multiple pages of pipelines in metrics page (#1359) * ADM-920:[backend] feat: can get all pipeline info * ADM-920:[backend] fix: change Cache type * ADM-873:[frontend]fix: fix notification logic (#1356) Co-authored-by: yulongcai <141199398+yulongcai@users.noreply.github.com> * ADM-898[backend][frontend]: feat: block' in board mapping is mutually exclusive with 'flag as block' (#1350) * ADM-898:[backend] feat: should not export cycle time flag on table header when not consider flag as block * ADM-898:[frontend]feat: not show flagCard when board mapping table has ‘block’ column * ADM-898:[frontend]feat: warning pop-up when conflict * ADM-898:[frontend]refactor: refactor codes * ADM-898:[frontend]refactor: remove fromFlag * ADM-898:[frontend]feat: not show flagCard when board mapping table has ‘block’ column * ADM-873-bug:[backend] feat: add test for as flag is block is false * ADM-898:[frontend]feat: add status logic * ADM-898:[frontend]refactor: refactor code * ADM-898:[backend] fix: fix bug for not calculate flag block to cycle when consider flag as block * ADM-886:[docs]docs: refactor chart API docs sequence diagram (#1339) * ADM-886:[docs]docs: update chart API image size * ADM-886:[docs]docs: update chart API docs content * ADM-886:[docs]docs: refactor chart API docs * ADM-886:[docs]docs: refactor chart API docs sequence diagram * ADM 834 add frontend validation to board Id (#1338) * ADM-834:[frontend]feat: fix rebase * ADM-834:[backend]feat: added api for domain url check * ADM-834:[backend]feat: add dashboard api for site check * ADM-834:[backend]feat: added api for domain url check * ADM-834:[frontend]fix: fix rebase * ADM-834:[frontend]fix: add regex validation to board id * ADM-834:[frontend]fix: fix lint * ADM 751 [docs]docs: fix readme in board mappings part (#1334) * ADM-751:[frontend]feat: add by status e2e test flow into import test * ADM-751:[frontend]feat: add more flow in create test * ADM-751:[frontend]feat: fix lint and refactor fixture file * ADM-751:[frontend]feat: fix by state flow and add new test * ADM-751:[frontend]feat: refactored file strcture and implemented new e2e test for cycle time status testing, just need to fix the data then * ADM-751:[frontend]feat: rework the by status flow e2e test, remove unnecessary steps * ADM-751:[frontend]feat: fix test flow again * ADM-751:[frontend]feat: fix type check * ADM-751:[frontend]feat: add status flow e2e test in import test * ADM-751:[frontend]fix: fix lint * ADM-882:[backend]feat: add import json file * ADM-751:[frontend]fix: refactor code * ADM-751:[frontend]fix: refactor code fix type * ADM-751:[docs]docs: fix readme in board mappings * ADM-751:[docs]docs: remove conflicted rework part * ADM-751 refactor --------- Co-authored-by: guzhongren * chore(sbom): generate sbom when releasing (#1340) * ADM-833:[frontend]feat:add e2e unhappy path scenarios (#1328) * ADM-833:[frontend]feat:add e2e unhappy path scenarios ADM-833: [frontend] add e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] feat: add unhappy path scenoria ADM-833: [frontend] feat: optimize add and remove new pipeline,add unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833:[frontend]feat:merge unhappy path scenario ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix e2e test error ADM-833:[frontend]feat:distinguish major and unhappy path test in local environment ADM-833: [frontend] feat: sperate major and all ADM-833[frontend]feat:change branch invalid error message ADM-833: [frontend] fix: fix index.ts error ADM-833: [frontend] fix: remove the log file ADM-833: [frontend] fix: fix conflict with main branch ADM-833: [frontend] fix: revert readme ADM-833: [frontend] fix: remove log file ADM-833: [frontend] fix: modify .gitignore, add omit logs * ADM-833 fix path * ADM-833: [frontend] fix: update the spec location * ADM-833: [frontend] fix: fix daterange error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix review error --------- Co-authored-by: YaoZhang-Daniel Co-authored-by: guzhongren * ADM-833: [frontend] fix: fix env error (#1341) * ADM-833:[frontend]feat:add e2e unhappy path scenarios ADM-833: [frontend] add e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] feat: add unhappy path scenoria ADM-833: [frontend] feat: optimize add and remove new pipeline,add unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833: [frontend] feat: optimize remove new pipeline,unhappy path template ADM-833:[frontend]feat:merge unhappy path scenario ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix code style error ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] update: update e2e test scenarios ADM-833: [frontend] fix: fix e2e test error ADM-833:[frontend]feat:distinguish major and unhappy path test in local environment ADM-833: [frontend] feat: sperate major and all ADM-833[frontend]feat:change branch invalid error message ADM-833: [frontend] fix: fix index.ts error ADM-833: [frontend] fix: remove the log file ADM-833: [frontend] fix: fix conflict with main branch ADM-833: [frontend] fix: revert readme ADM-833: [frontend] fix: remove log file ADM-833: [frontend] fix: modify .gitignore, add omit logs * ADM-833 fix path * ADM-833: [frontend] fix: update the spec location * ADM-833: [frontend] fix: fix daterange error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix review error * ADM-833: [frontend] fix: fix env error --------- Co-authored-by: YaoZhang-Daniel Co-authored-by: guzhongren * Revert "ADM-833: [frontend] fix: fix env error (#1341)" (#1344) This reverts commit eed90dc2ee055d2b0601db4ddfb22877effbf7a7. * [ADM-833][frontend]: chore: fix the e2e env injection error. (#1345) * refactor(e2e): name conversion[frontend] * fix(e2e): fix type check[frontend] * ADM-898:[frontend]fix: fix test * ADM-898:[frontend]feat: add test for CycleTime * ADM-898:[frontend]fix: fix UpperCase error * ADM-898:[frontend]fix: fix default value * ADM-898:[frontend]fix: fix import treatFlagCardAsBlock value * ADM-898:[backend] fix: repair e2e test * ADM-898:[backend] fix: repair e2e test to delete check consider flag as block * ADM-898:[backend] fix: sonar issue * [ADM-877][frontend]: feat: remove the limit of start-date & end-date selection. (#1342) * [ADM-877][frontend]: feat: remove the limit of start-date & end-date selection. * [ADM-877][frontend]: chore: refine code. * ADM-873:[frontend][backend] fix: fix success notification (#1327) * ADM-873-bug:[docx] docx: fix docx * ADM-873:[frontend]fix: fix error notification * ADM-873:[frontend]refactor: refactor code * ADM-873-bug:[backend] feat: return flag for whether it has file create by this invoke * Revert "ADM-873:[frontend]refactor: refactor code" This reverts commit e2fe52fc2c1b41dd0c2743daed78a6ff08a98f56. * Revert "ADM-873:[frontend]fix: fix error notification" This reverts commit b659c63af9143eecc97ae83f322f61885b9ad447. * ADM-873:[frontend]fix: use hasCsvFileCreateSuccessful to handle success notification * ADM-873-bug:[backend] feat: return has csv create successful when get completed * ADM-873-bug:[backend] fix: rename * ADM-873:[frontend]fix: fix test * ADM-873:[frontend]refactor: rename hasCsvFileCreateSuccessful to isSuccessfulCreateCsvFile * ADM-873:[backend]refactor: modify test info * ADM-873:[backend]refactor: format code --------- Co-authored-by: yulongcai Co-authored-by: Rui7ing <129819182+Rui7ing@users.noreply.github.com> * [frontend] feat/adm 811: Do not clear the configuration settings when returning from the metric page to the configuration page. (#1343) * [feat][adm-811]: remove GoCD option * [frontend][adm-811]: feat: remove clear config data trigger when back to config page * [frontend][adm-811]: refact: refact unit test * [frontend][adm-811]: replace fireEvent use userEvent * [frontend][adm-811]: fix failed case * Adm 910[docs] feat: add Github graghQL rate limit issue (#1348) * ADM-910[docs] feat: add Node and Rate limits and calculate * ADM-910[docs] feat: add flew chart for get data by github api * Adm 912[frontend]: ui refine date picker in metrics page (#1349) * ADM-912 feat: display date ranges * ADM-912 test: add unit test * ADM-912 fix: fix css * ADM-912 fix: fix color * ADM-912 fix: fix hex color * ADM-912 fix: use color from theme * ADM-898:[frontend]feat: add block case * ADM-898:[frontend]fix: fix test for treatFlagCardAsBlock * ADM-898:[frontend]fix: fix bug * ADM-898 fix: fix treat flag card as block when import * ADM-898 fix: fix treat flag card as block when create * ADM-898 refactor: rename * ADM-898:[frontend]fix: remove unnecessary cycleTimeSettings * ADM-898 fix: fix test * ADM-898:[backend] refactor: refactor code * ADM-898 test: add unit test * ADM-898 test: add e2e test for no block column board when create project * ADM-898 test: add e2e test for no block column board when import project * ADM-898:[frontend]refactor: iterate over test data and run tests using forEach * ADM-898:[frontend]refactor: export BLOCK_COLUMN_NAME * ADM-898:[frontend]refactor: fix prettier error * ADM-898:[frontend]fix: fix the error from merge --------- Co-authored-by: Tingyu Dong Co-authored-by: Steveay <907221539@qq.com> Co-authored-by: PengxiWPix <113176309+PengxiWPix@users.noreply.github.com> Co-authored-by: guzhongren Co-authored-by: YaoZhang87 <162096287+YaoZhang87@users.noreply.github.com> Co-authored-by: YaoZhang-Daniel Co-authored-by: Chao <89126516+mrcuriosity-tw@users.noreply.github.com> Co-authored-by: TingyuDong <113588395+TingyuDong@users.noreply.github.com> Co-authored-by: Rui7ing <129819182+Rui7ing@users.noreply.github.com> Co-authored-by: K Chow Co-authored-by: junbo dai Co-authored-by: Leiqiuhong <141199516+Leiqiuhong@users.noreply.github.com> Co-authored-by: Leiqiuhong * ADM-919:[backend] feat: add revert column in csv * ADM-919:[backend] feat: add job start time and pipeline start time and no pr commit time in csv * ADM-919:[backend] feat: fix e2e expect csv * ADM-919:[backend] feat: fix sonar error * ADM-919:[backend] fix: delete no use code * ADM-919:[backend] fix: add test for when build kite data info have author * ADM-919:[backend] fix: fix sonar error for eq * ADM-919:[backend] fix: update check error * ADM-919:[backend] fix: repair sonar issue * ADM-919:[backend] fix: format code * ADM-919:[backend] fix: add Nullable for field --------- Co-authored-by: guzhongren Co-authored-by: junbo dai Co-authored-by: TingyuDong <113588395+TingyuDong@users.noreply.github.com> Co-authored-by: Tingyu Dong Co-authored-by: Steveay <907221539@qq.com> Co-authored-by: PengxiWPix <113176309+PengxiWPix@users.noreply.github.com> Co-authored-by: YaoZhang87 <162096287+YaoZhang87@users.noreply.github.com> Co-authored-by: YaoZhang-Daniel Co-authored-by: Chao <89126516+mrcuriosity-tw@users.noreply.github.com> Co-authored-by: Rui7ing <129819182+Rui7ing@users.noreply.github.com> Co-authored-by: K Chow Co-authored-by: Leiqiuhong <141199516+Leiqiuhong@users.noreply.github.com> Co-authored-by: Leiqiuhong --- .../client/dto/codebase/github/LeadTime.java | 11 ++ .../report/dto/response/LeadTimeInfo.java | 55 +++--- .../service/report/CSVFileGenerator.java | 31 ++-- .../service/report/PipelineService.java | 13 +- .../service/source/github/GitHubService.java | 34 +++- .../report/dto/response/LeadTimeInfoTest.java | 16 +- .../service/report/CSVFileGeneratorTest.java | 80 +++++++-- .../service/report/PipelineCsvFixture.java | 156 +++++++++++++++--- .../service/report/PipelineServiceTest.java | 57 ++++++- .../source/github/GithubServiceTest.java | 93 ++++++++++- .../e2e/fixtures/create-new/pipeline-data.csv | 96 +++++------ .../fixtures/import-file/pipeline-data.csv | 96 +++++------ 12 files changed, 537 insertions(+), 201 deletions(-) diff --git a/backend/src/main/java/heartbeat/client/dto/codebase/github/LeadTime.java b/backend/src/main/java/heartbeat/client/dto/codebase/github/LeadTime.java index 92a45843b2..167d6b48c4 100644 --- a/backend/src/main/java/heartbeat/client/dto/codebase/github/LeadTime.java +++ b/backend/src/main/java/heartbeat/client/dto/codebase/github/LeadTime.java @@ -25,8 +25,19 @@ public class LeadTime { private long jobFinishTime; + private long jobStartTime; + + @Nullable + private Long noPRCommitTime; + + @Nullable + private Long firstCommitTime; + private long pipelineCreateTime; + @Nullable + private Boolean isRevert; + @Nullable private Long prLeadTime; diff --git a/backend/src/main/java/heartbeat/controller/report/dto/response/LeadTimeInfo.java b/backend/src/main/java/heartbeat/controller/report/dto/response/LeadTimeInfo.java index e1572895b8..d88154e880 100644 --- a/backend/src/main/java/heartbeat/controller/report/dto/response/LeadTimeInfo.java +++ b/backend/src/main/java/heartbeat/controller/report/dto/response/LeadTimeInfo.java @@ -25,6 +25,12 @@ public class LeadTimeInfo { private String jobFinishTime; + private String jobStartTime; + + private String noPRCommitTime; + + private String firstCommitTime; + @Nullable private String prLeadTime; @@ -33,33 +39,34 @@ public class LeadTimeInfo { @Nullable private String totalTime; - public LeadTimeInfo(LeadTime leadTime) { - if (leadTime != null) { - if (leadTime.getFirstCommitTimeInPr() != null) { - this.firstCommitTimeInPr = TimeUtil - .convertToISOFormat(String.valueOf(leadTime.getFirstCommitTimeInPr())); - } - - if (leadTime.getPrCreatedTime() != null) { - this.prCreatedTime = TimeUtil.convertToISOFormat(String.valueOf(leadTime.getPrCreatedTime())); - } - - if (leadTime.getPrMergedTime() != null) { - this.prMergedTime = TimeUtil.convertToISOFormat(String.valueOf(leadTime.getPrMergedTime())); - } - - this.jobFinishTime = TimeUtil.convertToISOFormat(String.valueOf(leadTime.getJobFinishTime())); + private Boolean isRevert; - this.pipelineLeadTime = TimeUtil.msToHMS(leadTime.getPipelineLeadTime()); - - if (leadTime.getPrLeadTime() != null) { - this.prLeadTime = TimeUtil.msToHMS(leadTime.getPrLeadTime()); - } + public LeadTimeInfo(LeadTime leadTime) { + if (leadTime == null) { + return; + } + this.firstCommitTimeInPr = convertToISOFormat(leadTime.getFirstCommitTimeInPr()); + this.prCreatedTime = convertToISOFormat(leadTime.getPrCreatedTime()); + this.prMergedTime = convertToISOFormat(leadTime.getPrMergedTime()); + this.jobFinishTime = convertToISOFormat(leadTime.getJobFinishTime()); + this.jobStartTime = convertToISOFormat(leadTime.getJobStartTime()); + this.firstCommitTime = convertToISOFormat(leadTime.getFirstCommitTime()); + this.noPRCommitTime = convertToISOFormat(leadTime.getNoPRCommitTime()); + + this.pipelineLeadTime = TimeUtil.msToHMS(leadTime.getPipelineLeadTime()); + this.isRevert = leadTime.getIsRevert(); + + if (leadTime.getPrLeadTime() != null) { + this.prLeadTime = TimeUtil.msToHMS(leadTime.getPrLeadTime()); + } - if (leadTime.getTotalTime() != 0) { - this.totalTime = TimeUtil.msToHMS(leadTime.getTotalTime()); - } + if (leadTime.getTotalTime() != 0) { + this.totalTime = TimeUtil.msToHMS(leadTime.getTotalTime()); } } + private String convertToISOFormat(Long time) { + return time != null ? TimeUtil.convertToISOFormat(String.valueOf(time)) : null; + } + } diff --git a/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java b/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java index cf7788abec..0f6552d4f1 100644 --- a/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java +++ b/backend/src/main/java/heartbeat/service/report/CSVFileGenerator.java @@ -43,8 +43,6 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -101,9 +99,10 @@ public void convertPipelineDataToCSV(List leadTimeData, String File file = new File(fileName); try (CSVWriter csvWriter = new CSVWriter(new FileWriter(file))) { String[] headers = { "Organization", "Pipeline Name", "Pipeline Step", "Valid", "Build Number", - "Code Committer", "Pipeline Creator", "First Code Committed Time In PR", "Code Committed Time", - "PR Created Time", "PR Merged Time", "Deployment Completed Time", "Total Lead Time (HH:mm:ss)", - "PR Lead Time (HH:mm:ss)", "Pipeline Lead Time (HH:mm:ss)", "Status", "Branch" }; + "Code Committer", "Pipeline Creator", "First Code Committed Time In PR", "PR Created Time", + "PR Merged Time", "No PR Committed Time", "Job Start Time", "Pipeline Start Time", + "Pipeline Finish Time", "Total Lead Time (HH:mm:ss)", "PR Lead Time (HH:mm:ss)", + "Pipeline Lead Time (HH:mm:ss)", "Status", "Branch", "Revert" }; csvWriter.writeNext(headers); @@ -120,23 +119,21 @@ public void convertPipelineDataToCSV(List leadTimeData, String } private String[] getRowData(PipelineCSVInfo csvInfo) { - String committerName = null; - String commitDate = null; - if (csvInfo.getCommitInfo() != null) { - committerName = csvInfo.getCommitInfo().getCommit().getAuthor().getName(); - commitDate = csvInfo.getCommitInfo().getCommit().getAuthor().getDate(); - } - String creatorName = null; if (csvInfo.getBuildInfo().getCreator() != null && csvInfo.getBuildInfo().getCreator().getName() != null) { creatorName = csvInfo.getBuildInfo().getCreator().getName(); } + String committerName = null; + if (csvInfo.getBuildInfo().getAuthor() != null && csvInfo.getBuildInfo().getAuthor().getName() != null) { + committerName = String.valueOf(csvInfo.getBuildInfo().getAuthor().getName()); + } String organization = csvInfo.getOrganizationName(); String pipelineName = csvInfo.getPipeLineName(); String stepName = csvInfo.getStepName(); String valid = String.valueOf(csvInfo.getValid()).toLowerCase(); String buildNumber = String.valueOf(csvInfo.getBuildInfo().getNumber()); + String state = csvInfo.getPiplineStatus().equals(CANCELED_STATUS) ? CANCELED_STATUS : csvInfo.getDeployInfo().getState(); String branch = csvInfo.getBuildInfo().getBranch(); @@ -145,14 +142,18 @@ private String[] getRowData(PipelineCSVInfo csvInfo) { String firstCommitTimeInPr = leadTimeInfo.getFirstCommitTimeInPr(); String prCreatedTime = leadTimeInfo.getPrCreatedTime(); String prMergedTime = leadTimeInfo.getPrMergedTime(); - String jobFinishTime = csvInfo.getDeployInfo().getJobFinishTime(); + String noPRCommitTime = leadTimeInfo.getNoPRCommitTime(); + String jobStartTime = leadTimeInfo.getJobStartTime(); + String pipelineStartTime = leadTimeInfo.getFirstCommitTime(); + String pipelineFinishTime = csvInfo.getDeployInfo().getJobFinishTime(); String totalTime = leadTimeInfo.getTotalTime(); String prLeadTime = leadTimeInfo.getPrLeadTime(); String pipelineLeadTime = leadTimeInfo.getPipelineLeadTime(); + String isRevert = leadTimeInfo.getIsRevert() == null ? "" : String.valueOf(leadTimeInfo.getIsRevert()); return new String[] { organization, pipelineName, stepName, valid, buildNumber, committerName, creatorName, - firstCommitTimeInPr, commitDate, prCreatedTime, prMergedTime, jobFinishTime, totalTime, prLeadTime, - pipelineLeadTime, state, branch }; + firstCommitTimeInPr, prCreatedTime, prMergedTime, noPRCommitTime, jobStartTime, pipelineStartTime, + pipelineFinishTime, totalTime, prLeadTime, pipelineLeadTime, state, branch, isRevert }; } public InputStreamResource getDataFromCSV(ReportType reportDataType, long csvTimeStamp) { diff --git a/backend/src/main/java/heartbeat/service/report/PipelineService.java b/backend/src/main/java/heartbeat/service/report/PipelineService.java index 7ba6350cbe..08dca92425 100644 --- a/backend/src/main/java/heartbeat/service/report/PipelineService.java +++ b/backend/src/main/java/heartbeat/service/report/PipelineService.java @@ -1,5 +1,6 @@ package heartbeat.service.report; +import heartbeat.client.dto.codebase.github.Author; import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.codebase.github.LeadTime; import heartbeat.client.dto.codebase.github.PipelineLeadTime; @@ -56,7 +57,7 @@ public FetchedData.BuildKiteData fetchBuildKiteInfo(GenerateReportRequest reques String endTime = request.getEndTime(); FetchedData.BuildKiteData result = new FetchedData.BuildKiteData(); - request.getBuildKiteSetting().getDeploymentEnvList().stream().forEach(deploymentEnvironment -> { + request.getBuildKiteSetting().getDeploymentEnvList().forEach(deploymentEnvironment -> { List buildKiteBuildInfo = getBuildKiteBuildInfo(startTime, endTime, deploymentEnvironment, request.getBuildKiteSetting().getToken(), request.getBuildKiteSetting().getPipelineCrews()); @@ -97,12 +98,15 @@ private PipelineCSVInfo getPipelineCSVInfo(CodebaseSetting codebaseSetting, Stri BuildKiteBuildInfo buildInfo, List pipelineSteps) { DeployInfo deployInfo = buildKiteService.mapToDeployInfo(buildInfo, pipelineSteps, REQUIRED_STATES, startTime, endTime); - CommitInfo commitInfo = null; if (Objects.nonNull(codebaseSetting) && StringUtils.hasLength(codebaseSetting.getToken()) - && Objects.nonNull(deployInfo.getCommitId())) { - commitInfo = gitHubService.fetchCommitInfo(deployInfo.getCommitId(), + && Objects.nonNull(deployInfo.getCommitId()) && Objects.isNull(buildInfo.getAuthor())) { + CommitInfo commitInfo = gitHubService.fetchCommitInfo(deployInfo.getCommitId(), GithubUtil.getGithubUrlFullName(deploymentEnvironment.getRepository()), codebaseSetting.getToken()); + Author author = commitInfo.getCommit().getAuthor(); + buildInfo + .setAuthor(BuildKiteBuildInfo.Author.builder().name(author.getName()).email(author.getEmail()).build()); } + return PipelineCSVInfo.builder() .organizationName(deploymentEnvironment.getOrgName()) .pipeLineName(deploymentEnvironment.getName()) @@ -111,7 +115,6 @@ private PipelineCSVInfo getPipelineCSVInfo(CodebaseSetting codebaseSetting, Stri .piplineStatus(buildInfo.getState()) .buildInfo(buildInfo) .deployInfo(deployInfo) - .commitInfo(commitInfo) .leadTimeInfo(new LeadTimeInfo(filterLeadTime(buildKiteData, deploymentEnvironment, deployInfo))) .build(); } diff --git a/backend/src/main/java/heartbeat/service/source/github/GitHubService.java b/backend/src/main/java/heartbeat/service/source/github/GitHubService.java index 526483e948..27a51d55a8 100644 --- a/backend/src/main/java/heartbeat/service/source/github/GitHubService.java +++ b/backend/src/main/java/heartbeat/service/source/github/GitHubService.java @@ -205,9 +205,11 @@ private LeadTime parseNoMergeLeadTime(DeployInfo deployInfo, PipelineInfoOfRepos deployInfo.getCommitId(), e.getMessage()); } + Long noPRCommitTime = null; if (commitInfo.getCommit() != null && commitInfo.getCommit().getCommitter() != null && commitInfo.getCommit().getCommitter().getDate() != null) { - firstCommitTime = Instant.parse(commitInfo.getCommit().getCommitter().getDate()).toEpochMilli(); + noPRCommitTime = Instant.parse(commitInfo.getCommit().getCommitter().getDate()).toEpochMilli(); + firstCommitTime = noPRCommitTime; } else { firstCommitTime = jobStartTime; @@ -217,12 +219,24 @@ private LeadTime parseNoMergeLeadTime(DeployInfo deployInfo, PipelineInfoOfRepos .commitId(deployInfo.getCommitId()) .pipelineCreateTime(pipelineCreateTime) .jobFinishTime(jobFinishTime) + .jobStartTime(jobStartTime) + .noPRCommitTime(noPRCommitTime) + .firstCommitTime(firstCommitTime) .pipelineLeadTime(jobFinishTime - firstCommitTime) .totalTime(jobFinishTime - firstCommitTime) .prLeadTime(prLeadTime) + .isRevert(isRevert(commitInfo)) .build(); } + private Boolean isRevert(CommitInfo commitInfo) { + Boolean isRevert = null; + if (commitInfo.getCommit() != null && commitInfo.getCommit().getMessage() != null) { + isRevert = commitInfo.getCommit().getMessage().toLowerCase().startsWith("revert"); + } + return isRevert; + } + public LeadTime mapLeadTimeWithInfo(PullRequestInfo pullRequestInfo, DeployInfo deployInfo, CommitInfo commitInfo) { if (pullRequestInfo.getMergedAt() == null) { return null; @@ -230,6 +244,7 @@ public LeadTime mapLeadTimeWithInfo(PullRequestInfo pullRequestInfo, DeployInfo long prCreatedTime = Instant.parse(pullRequestInfo.getCreatedAt()).toEpochMilli(); long prMergedTime = Instant.parse(pullRequestInfo.getMergedAt()).toEpochMilli(); long jobFinishTime = Instant.parse(deployInfo.getJobFinishTime()).toEpochMilli(); + long jobStartTime = Instant.parse(deployInfo.getJobStartTime()).toEpochMilli(); long pipelineCreateTime = Instant.parse(deployInfo.getPipelineCreateTime()).toEpochMilli(); long firstCommitTimeInPr; if (commitInfo.getCommit() != null && commitInfo.getCommit().getCommitter() != null @@ -243,16 +258,12 @@ public LeadTime mapLeadTimeWithInfo(PullRequestInfo pullRequestInfo, DeployInfo long pipelineLeadTime = jobFinishTime - prMergedTime; long prLeadTime; long totalTime; - if (commitInfo.getCommit().getMessage().toLowerCase().startsWith("revert")) { + Boolean isRevert = isRevert(commitInfo); + if (Boolean.TRUE.equals(isRevert) || isNoFirstCommitTimeInPr(firstCommitTimeInPr)) { prLeadTime = 0; } else { - if (firstCommitTimeInPr > 0) { - prLeadTime = prMergedTime - firstCommitTimeInPr; - } - else { - prLeadTime = prMergedTime - prCreatedTime; - } + prLeadTime = prMergedTime - firstCommitTimeInPr; } totalTime = prLeadTime + pipelineLeadTime; @@ -265,10 +276,17 @@ public LeadTime mapLeadTimeWithInfo(PullRequestInfo pullRequestInfo, DeployInfo .prCreatedTime(prCreatedTime) .commitId(deployInfo.getCommitId()) .jobFinishTime(jobFinishTime) + .jobStartTime(jobStartTime) + .firstCommitTime(prMergedTime) .pipelineCreateTime(pipelineCreateTime) + .isRevert(isRevert) .build(); } + private static boolean isNoFirstCommitTimeInPr(long firstCommitTimeInPr) { + return firstCommitTimeInPr == 0; + } + public CommitInfo fetchCommitInfo(String commitId, String repositoryId, String token) { try { String realToken = BEARER_TITLE + token; diff --git a/backend/src/test/java/heartbeat/controller/report/dto/response/LeadTimeInfoTest.java b/backend/src/test/java/heartbeat/controller/report/dto/response/LeadTimeInfoTest.java index 690d16df6a..25a045ba49 100644 --- a/backend/src/test/java/heartbeat/controller/report/dto/response/LeadTimeInfoTest.java +++ b/backend/src/test/java/heartbeat/controller/report/dto/response/LeadTimeInfoTest.java @@ -10,18 +10,24 @@ public class LeadTimeInfoTest { @Test void shouldCreateLeadTimeInfoWithLeadTime() { LeadTimeInfo info = new LeadTimeInfo(LeadTime.builder() - .prLeadTime(47255635l) - .firstCommitTimeInPr(1672556350000l) - .prCreatedTime(1706067997l) - .prMergedTime(1706067997l) - .totalTime(57255635l) + .prLeadTime(47255635L) + .firstCommitTimeInPr(1672556350000L) + .prCreatedTime(1706067997L) + .prMergedTime(1706067997L) + .firstCommitTime(1706067997L) + .noPRCommitTime(1706067997L) + .totalTime(57255635L) + .isRevert(Boolean.FALSE) .build()); assertEquals("13:7:35", info.getPrLeadTime()); assertEquals("2023-01-01T06:59:10Z", info.getFirstCommitTimeInPr()); assertEquals("1970-01-20T17:54:27Z", info.getPrMergedTime()); assertEquals("1970-01-20T17:54:27Z", info.getPrCreatedTime()); + assertEquals("1970-01-20T17:54:27Z", info.getFirstCommitTime()); + assertEquals("1970-01-20T17:54:27Z", info.getNoPRCommitTime()); assertEquals("15:54:15", info.getTotalTime()); + assertEquals(Boolean.FALSE, info.getIsRevert()); } } diff --git a/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java b/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java index 839316fc7b..a81b8a6edb 100644 --- a/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java +++ b/backend/src/test/java/heartbeat/service/report/CSVFileGeneratorTest.java @@ -72,11 +72,11 @@ void shouldConvertPipelineDataToCsvGivenCommitInfoNotNull() throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); String headers = reader.readLine(); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); String firstLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", firstLine); reader.close(); fileInputStream.close(); @@ -98,11 +98,11 @@ void shouldConvertPipelineDataToCsvGivenCommitInfoNotNullAndPipelineStateIsCance BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); String headers = reader.readLine(); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); String firstLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"canceled\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"canceled\",\"branch\",\"\"", firstLine); reader.close(); fileInputStream.close(); @@ -123,10 +123,10 @@ void shouldConvertPipelineDataToCsvWithoutCreator() throws IOException { assertTrue(file.exists()); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"1683793037000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", firstLine); reader.close(); fileInputStream.close(); @@ -147,10 +147,10 @@ void shouldConvertPipelineDataToCsvWithoutCreatorName() throws IOException { assertTrue(file.exists()); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",,,\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", firstLine); reader.close(); fileInputStream.close(); @@ -171,11 +171,59 @@ void shouldConvertPipelineDataToCsvGivenNullCommitInfo() throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); String headers = reader.readLine(); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); String firstLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",,,\"2023-05-08T07:18:18Z\",,\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", + firstLine); + reader.close(); + fileInputStream.close(); + file.delete(); + } + + @Test + void shouldConvertPipelineDataToCsvGivenCommitMessageIsRevert() throws IOException { + List pipelineCSVInfos = PipelineCsvFixture.MOCK_PIPELINE_CSV_DATA_WITH_MESSAGE_IS_REVERT(); + String fileName = CSVFileNameEnum.PIPELINE.getValue() + "-" + mockTimeStamp + ".csv"; + File file = new File(fileName); + + csvFileGenerator.convertPipelineDataToCSV(pipelineCSVInfos, mockTimeStamp); + FileInputStream fileInputStream = new FileInputStream(file); + BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); + String headers = reader.readLine(); + String firstLine = reader.readLine(); + + assertTrue(file.exists()); + assertEquals( + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", + headers); + assertEquals( + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",\"XXXX\",,\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"true\"", + firstLine); + reader.close(); + fileInputStream.close(); + file.delete(); + } + + @Test + void shouldConvertPipelineDataToCsvGivenAuthorIsNull() throws IOException { + List pipelineCSVInfos = PipelineCsvFixture.MOCK_PIPELINE_CSV_DATA_WITHOUT_Author_NAME(); + String fileName = CSVFileNameEnum.PIPELINE.getValue() + "-" + mockTimeStamp + ".csv"; + File file = new File(fileName); + + csvFileGenerator.convertPipelineDataToCSV(pipelineCSVInfos, mockTimeStamp); + FileInputStream fileInputStream = new FileInputStream(file); + BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); + String headers = reader.readLine(); + String firstLine = reader.readLine(); + + assertTrue(file.exists()); + assertEquals( + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", + headers); + assertEquals( + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"null\",\"880\",,,\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", firstLine); reader.close(); fileInputStream.close(); @@ -209,8 +257,8 @@ void shouldHasContentWhenGetDataFromCsvGivenDataTypeIsPipeline() throws IOExcept .collect(Collectors.joining("\n")); Assertions.assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"\n" - + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"\n" + + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", csvPipelineData); String fileName = CSVFileNameEnum.PIPELINE.getValue() + "-" + mockTimeStamp + ".csv"; @@ -233,22 +281,22 @@ void shouldConvertPipelineDataToCsvGivenTwoOrganizationsPipeline() throws IOExce BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream)); String headers = reader.readLine(); assertEquals( - "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"Code Committed Time\",\"PR Created Time\",\"PR Merged Time\",\"Deployment Completed Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\"", + "\"Organization\",\"Pipeline Name\",\"Pipeline Step\",\"Valid\",\"Build Number\",\"Code Committer\",\"Pipeline Creator\",\"First Code Committed Time In PR\",\"PR Created Time\",\"PR Merged Time\",\"No PR Committed Time\",\"Job Start Time\",\"Pipeline Start Time\",\"Pipeline Finish Time\",\"Total Lead Time (HH:mm:ss)\",\"PR Lead Time (HH:mm:ss)\",\"Pipeline Lead Time (HH:mm:ss)\",\"Status\",\"Branch\",\"Revert\"", headers); String firstLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"yulong\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", firstLine); String secondLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Heartbeat\",\"Heartbeat\",\":rocket: Deploy prod\",\"true\",\"880\",\"yulong\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", secondLine); String thirdLine = reader.readLine(); assertEquals( - "\"Thoughtworks-Foxtel\",\"Heartbeat1\",\":rocket: Deploy prod\",\"true\",\"880\",\"XXXX\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"2023-05-10T06:43:02.653Z\",\"168369327000\",\"1683793037000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\"", + "\"Thoughtworks-Foxtel\",\"Heartbeat1\",\":rocket: Deploy prod\",\"true\",\"880\",\"yulong\",\"XXXX\",\"2023-05-08T07:18:18Z\",\"168369327000\",\"1683793037000\",,\"168369327000\",\"168369327000\",\"1684793037000\",\"8379303\",\"16837\",\"653037000\",\"passed\",\"branch\",\"\"", thirdLine); reader.close(); diff --git a/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java b/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java index 32fbda18ac..c51db7b273 100644 --- a/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java +++ b/backend/src/test/java/heartbeat/service/report/PipelineCsvFixture.java @@ -4,8 +4,6 @@ import heartbeat.client.dto.codebase.github.Commit; import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.codebase.github.Committer; -import heartbeat.client.dto.codebase.github.LeadTime; -import heartbeat.client.dto.codebase.github.PipelineLeadTime; import heartbeat.client.dto.pipeline.buildkite.BuildKiteBuildInfo; import heartbeat.client.dto.pipeline.buildkite.BuildKiteJob; import heartbeat.client.dto.pipeline.buildkite.DeployInfo; @@ -36,6 +34,7 @@ public static List MOCK_PIPELINE_CSV_DATA() { .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("XXXX").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -57,7 +56,9 @@ public static List MOCK_PIPELINE_CSV_DATA() { .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -88,6 +89,7 @@ public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR() { .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("XXXX").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -109,8 +111,10 @@ public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR() { .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("1683793037000") .jobFinishTime("1684793037000") .pipelineLeadTime("653037000") + .firstCommitTime("168369327000") .build()) .deployInfo(DeployInfo.builder() .state("passed") @@ -161,7 +165,121 @@ public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_CREATOR_NAME( .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") + .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") + .pipelineLeadTime("653037000") + .build()) + .deployInfo(DeployInfo.builder() + .state("passed") + .jobFinishTime("1684793037000") + .jobStartTime("168369327000") + .build()) + .build(); + return List.of(pipelineCsvInfo); + } + + public static List MOCK_PIPELINE_CSV_DATA_WITH_MESSAGE_IS_REVERT() { + PipelineCSVInfo pipelineCsvInfo = PipelineCSVInfo.builder() + .organizationName("Thoughtworks-Heartbeat") + .pipeLineName("Heartbeat") + .piplineStatus("passed") + .stepName(":rocket: Deploy prod") + .buildInfo(BuildKiteBuildInfo.builder() + .commit("713b31878c756c205a6c03eac5be3ac7c7e6a227") + .creator(BuildKiteBuildInfo.Creator.builder().name(null).email("XXX@test.com").build()) + .pipelineCreateTime("2023-05-10T06:17:21.844Z") + .state("passed") + .number(880) + .jobs(List.of(BuildKiteJob.builder() + .name(":rocket: Deploy prod") + .state("passed") + .startedAt("2023-05-10T06:42:47.498Z") + .finishedAt("2023-05-10T06:43:02.653Z") + .build())) + .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("XXXX").build()) + .build()) + .commitInfo(CommitInfo.builder() + .commit(Commit.builder() + .author(Author.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .committer(Committer.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .message("Revert:xxxx") + .build()) + .build()) + .leadTimeInfo(LeadTimeInfo.builder() + .firstCommitTimeInPr("2023-05-08T07:18:18Z") + .totalTime("8379303") + .prMergedTime("1683793037000") + .prLeadTime("16837") + .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") + .pipelineLeadTime("653037000") + .isRevert(Boolean.TRUE) + .build()) + .deployInfo(DeployInfo.builder() + .state("passed") + .jobFinishTime("1684793037000") + .jobStartTime("168369327000") + .build()) + .build(); + return List.of(pipelineCsvInfo); + } + + public static List MOCK_PIPELINE_CSV_DATA_WITHOUT_Author_NAME() { + PipelineCSVInfo pipelineCsvInfo = PipelineCSVInfo.builder() + .organizationName("Thoughtworks-Heartbeat") + .pipeLineName("Heartbeat") + .piplineStatus("passed") + .stepName(":rocket: Deploy prod") + .buildInfo(BuildKiteBuildInfo.builder() + .commit("713b31878c756c205a6c03eac5be3ac7c7e6a227") + .creator(BuildKiteBuildInfo.Creator.builder().name(null).email("XXX@test.com").build()) + .pipelineCreateTime("2023-05-10T06:17:21.844Z") + .state("passed") + .number(880) + .jobs(List.of(BuildKiteJob.builder() + .name(":rocket: Deploy prod") + .state("passed") + .startedAt("2023-05-10T06:42:47.498Z") + .finishedAt("2023-05-10T06:43:02.653Z") + .build())) + .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().build()) + .build()) + .commitInfo(CommitInfo.builder() + .commit(Commit.builder() + .author(Author.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .committer(Committer.builder() + .name("XXXX") + .email("XXX@test.com") + .date("2023-05-10T06:43:02.653Z") + .build()) + .build()) + .build()) + .leadTimeInfo(LeadTimeInfo.builder() + .firstCommitTimeInPr("2023-05-08T07:18:18Z") + .totalTime("8379303") + .prMergedTime("1683793037000") + .prLeadTime("16837") + .prCreatedTime("168369327000") + .jobStartTime("168369327000") + .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -193,6 +311,7 @@ public static List MOCK_PIPELINE_CSV_DATA_WITH_PIPELINE_STATUS_ .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("XXXX").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -214,7 +333,9 @@ public static List MOCK_PIPELINE_CSV_DATA_WITH_PIPELINE_STATUS_ .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -245,6 +366,7 @@ public static List MOCK_PIPELINE_CSV_DATA_WITH_NULL_COMMIT_INFO .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("XXXX").build()) .build()) .leadTimeInfo(LeadTimeInfo.builder() .firstCommitTimeInPr("2023-05-08T07:18:18Z") @@ -252,7 +374,9 @@ public static List MOCK_PIPELINE_CSV_DATA_WITH_NULL_COMMIT_INFO .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -264,25 +388,6 @@ public static List MOCK_PIPELINE_CSV_DATA_WITH_NULL_COMMIT_INFO return List.of(pipelineCsvInfo); } - public static PipelineLeadTime MOCK_PIPELINE_LEAD_TIME_DATA() { - return PipelineLeadTime.builder() - .pipelineStep("xx") - .pipelineName("xx") - .leadTimes(List.of(LeadTime.builder() - .commitId("xx") - .prCreatedTime(1658549100000L) - .prMergedTime(1658549160000L) - .firstCommitTimeInPr(1658549100000L) - .jobFinishTime(1658549160000L) - .pipelineCreateTime(1658549100000L) - .prLeadTime(60000L) - .pipelineLeadTime(60000) - .totalTime(120000) - .build())) - .build(); - - } - public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { PipelineCSVInfo pipelineCsvInfo1 = PipelineCSVInfo.builder() .organizationName("Thoughtworks-Heartbeat") @@ -303,6 +408,7 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("yulong").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -324,7 +430,9 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -352,6 +460,7 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("yulong").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -373,7 +482,9 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() @@ -401,6 +512,7 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .finishedAt("2023-05-10T06:43:02.653Z") .build())) .branch("branch") + .author(BuildKiteBuildInfo.Author.builder().name("yulong").build()) .build()) .commitInfo(CommitInfo.builder() .commit(Commit.builder() @@ -422,7 +534,9 @@ public static List MOCK_TWO_ORGANIZATIONS_PIPELINE_CSV_DATA() { .prMergedTime("1683793037000") .prLeadTime("16837") .prCreatedTime("168369327000") + .jobStartTime("168369327000") .jobFinishTime("1684793037000") + .firstCommitTime("168369327000") .pipelineLeadTime("653037000") .build()) .deployInfo(DeployInfo.builder() diff --git a/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java b/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java index 29b6cb172b..206e0138de 100644 --- a/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java +++ b/backend/src/test/java/heartbeat/service/report/PipelineServiceTest.java @@ -1,5 +1,7 @@ package heartbeat.service.report; +import heartbeat.client.dto.codebase.github.Author; +import heartbeat.client.dto.codebase.github.Commit; import heartbeat.client.dto.codebase.github.CommitInfo; import heartbeat.client.dto.codebase.github.LeadTime; import heartbeat.client.dto.codebase.github.PipelineLeadTime; @@ -430,7 +432,9 @@ void shouldGenerateValueWithoutCommitWhenCommitIdIsEmpty() { @Test void shouldGenerateValueHasCommit() { List kiteBuildInfos = List.of(BuildKiteBuildInfo.builder().commit("commit").build()); - CommitInfo fakeCommitInfo = CommitInfo.builder().build(); + CommitInfo fakeCommitInfo = CommitInfo.builder() + .commit(Commit.builder().author(Author.builder().name("xxxx").build()).build()) + .build(); when(buildKiteService.getPipelineStepNames(eq(kiteBuildInfos))).thenReturn(List.of("check")); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("check")); when(buildKiteService.getBuildKiteJob(any(), any(), any(), eq(MOCK_START_TIME), eq(MOCK_END_TIME))) @@ -449,7 +453,7 @@ void shouldGenerateValueHasCommit() { assertEquals(1, result.size()); PipelineCSVInfo pipelineCSVInfo = result.get(0); assertEquals("env-name", pipelineCSVInfo.getPipeLineName()); - assertEquals(fakeCommitInfo, pipelineCSVInfo.getCommitInfo()); + assertEquals("xxxx", pipelineCSVInfo.getBuildInfo().getAuthor().getName()); assertEquals(fakeDeploy, pipelineCSVInfo.getDeployInfo()); verify(buildKiteService, times(1)).getPipelineStepNames(any()); verify(buildKiteService, times(1)).getBuildKiteJob(any(), any(), any(), any(), any()); @@ -458,7 +462,9 @@ void shouldGenerateValueHasCommit() { @Test void shouldGenerateValueWithLeadTimeWhenLeadTimeExisting() { List kiteBuildInfos = List.of(BuildKiteBuildInfo.builder().commit("commit").build()); - CommitInfo fakeCommitInfo = CommitInfo.builder().build(); + CommitInfo fakeCommitInfo = CommitInfo.builder() + .commit(Commit.builder().author(Author.builder().name("xxxx").build()).build()) + .build(); when(buildKiteService.getPipelineStepNames(eq(kiteBuildInfos))).thenReturn(List.of("check")); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("check")); when(buildKiteService.getBuildKiteJob(any(), any(), any(), eq(MOCK_START_TIME), eq(MOCK_END_TIME))) @@ -481,7 +487,7 @@ void shouldGenerateValueWithLeadTimeWhenLeadTimeExisting() { assertEquals(1, result.size()); PipelineCSVInfo pipelineCSVInfo = result.get(0); assertEquals("env-name", pipelineCSVInfo.getPipeLineName()); - assertEquals(fakeCommitInfo, pipelineCSVInfo.getCommitInfo()); + assertEquals("xxxx", pipelineCSVInfo.getBuildInfo().getAuthor().getName()); assertEquals(fakeDeploy, pipelineCSVInfo.getDeployInfo()); verify(buildKiteService, times(1)).getPipelineStepNames(any()); verify(buildKiteService, times(1)).getBuildKiteJob(any(), any(), any(), any(), any()); @@ -490,7 +496,9 @@ void shouldGenerateValueWithLeadTimeWhenLeadTimeExisting() { @Test void shouldGenerateValueWithOrganizationWhenDeployHasOrganization() { List kiteBuildInfos = List.of(BuildKiteBuildInfo.builder().commit("commit").build()); - CommitInfo fakeCommitInfo = CommitInfo.builder().build(); + CommitInfo fakeCommitInfo = CommitInfo.builder() + .commit(Commit.builder().author(Author.builder().name("xxxx").build()).build()) + .build(); when(buildKiteService.getPipelineStepNames(eq(kiteBuildInfos))).thenReturn(List.of("check")); when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("check")); when(buildKiteService.getBuildKiteJob(any(), any(), any(), eq(MOCK_START_TIME), eq(MOCK_END_TIME))) @@ -517,7 +525,44 @@ void shouldGenerateValueWithOrganizationWhenDeployHasOrganization() { assertEquals(1, result.size()); PipelineCSVInfo pipelineCSVInfo = result.get(0); assertEquals("Thoughtworks-Heartbeat", pipelineCSVInfo.getOrganizationName()); - assertEquals(fakeCommitInfo, pipelineCSVInfo.getCommitInfo()); + assertEquals("xxxx", pipelineCSVInfo.getBuildInfo().getAuthor().getName()); + assertEquals(fakeDeploy, pipelineCSVInfo.getDeployInfo()); + verify(buildKiteService, times(1)).getPipelineStepNames(any()); + verify(buildKiteService, times(1)).getBuildKiteJob(any(), any(), any(), any(), any()); + } + + @Test + void shouldGenerateValueWhenBuildKiteDataAuthorIsNotNull() { + List kiteBuildInfos = List.of(BuildKiteBuildInfo.builder() + .commit("commit") + .author(BuildKiteBuildInfo.Author.builder().name("xxxx").build()) + .build()); + when(buildKiteService.getPipelineStepNames(kiteBuildInfos)).thenReturn(List.of("check")); + when(buildKiteService.getStepsBeforeEndStep(any(), any())).thenReturn(List.of("check")); + when(buildKiteService.getBuildKiteJob(any(), any(), any(), eq(MOCK_START_TIME), eq(MOCK_END_TIME))) + .thenReturn(BuildKiteJob.builder().build()); + DeployInfo fakeDeploy = DeployInfo.builder().commitId("commitId").jobName("test").build(); + when(buildKiteService.mapToDeployInfo(any(), any(), any(), any(), any())).thenReturn(fakeDeploy); + + List result = pipelineService.generateCSVForPipelineWithCodebase( + CodebaseSetting.builder().token("token").build(), MOCK_START_TIME, MOCK_END_TIME, + FetchedData.BuildKiteData.builder() + .pipelineLeadTimes(List.of(PipelineLeadTime.builder() + .leadTimes(List.of(LeadTime.builder().commitId("commitId").build())) + .pipelineName("env-name") + .build())) + .buildInfosList(List.of(Map.entry("env1", kiteBuildInfos))) + .build(), + List.of(DeploymentEnvironment.builder() + .id("env1") + .name("env-name") + .orgName("Thoughtworks-Heartbeat") + .build())); + + assertEquals(1, result.size()); + PipelineCSVInfo pipelineCSVInfo = result.get(0); + assertEquals("Thoughtworks-Heartbeat", pipelineCSVInfo.getOrganizationName()); + assertEquals("xxxx", pipelineCSVInfo.getBuildInfo().getAuthor().getName()); assertEquals(fakeDeploy, pipelineCSVInfo.getDeployInfo()); verify(buildKiteService, times(1)).getPipelineStepNames(any()); verify(buildKiteService, times(1)).getBuildKiteJob(any(), any(), any(), any(), any()); diff --git a/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java b/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java index de611d50fe..aa7b23d095 100644 --- a/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java +++ b/backend/src/test/java/heartbeat/service/source/github/GithubServiceTest.java @@ -127,12 +127,15 @@ public void setUp() { .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(1658548980000L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) .prLeadTime(60000L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(180000) + .isRevert(Boolean.FALSE) .build())) .build()); @@ -267,12 +270,15 @@ void shouldReturnLeadTimeWhenMergedTimeIsNotNull() { .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(1658548980000L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) .prLeadTime(60000L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(180000) + .isRevert(Boolean.FALSE) .build(); LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); @@ -288,12 +294,15 @@ void CommitTimeInPrShouldBeZeroWhenCommitInfoIsNull() { .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) - .prLeadTime(60000L) + .prLeadTime(0L) .pipelineLeadTime(120000) - .totalTime(180000) + .firstCommitTime(1658549040000L) + .totalTime(120000) + .isRevert(Boolean.FALSE) .build(); LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); @@ -309,12 +318,15 @@ void CommitTimeInPrLeadTimeShouldBeZeroWhenCommitInfoIsNotNullGivenCommitIsRever .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(120000) + .isRevert(Boolean.TRUE) .build(); LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); @@ -330,11 +342,14 @@ void CommitTimeInPrLeadTimeShouldBeZeroWhenCommitInfoIsInLowerCaseGivenCommitIsR .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) + .isRevert(Boolean.TRUE) .totalTime(120000) .build(); @@ -343,6 +358,54 @@ void CommitTimeInPrLeadTimeShouldBeZeroWhenCommitInfoIsInLowerCaseGivenCommitIsR assertEquals(expect, result); } + @Test + void shouldReturnIsRevertIsNullWhenCommitInfoCommitIsNull() { + commitInfo = CommitInfo.builder().build(); + LeadTime expect = LeadTime.builder() + .commitId("111") + .prCreatedTime(1658548980000L) + .prMergedTime(1658549040000L) + .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) + .jobFinishTime(1658549160000L) + .pipelineLeadTime(1658549100000L) + .pipelineCreateTime(1658549100000L) + .prLeadTime(0L) + .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) + .totalTime(120000L) + .isRevert(null) + .build(); + + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); + + assertEquals(expect, result); + } + + @Test + void shouldReturnIsRevertIsNullWhenCommitInfoCommitMessageIsNull() { + commitInfo = CommitInfo.builder().commit(Commit.builder().build()).build(); + LeadTime expect = LeadTime.builder() + .commitId("111") + .prCreatedTime(1658548980000L) + .prMergedTime(1658549040000L) + .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) + .jobFinishTime(1658549160000L) + .pipelineLeadTime(1658549100000L) + .pipelineCreateTime(1658549100000L) + .prLeadTime(0L) + .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) + .totalTime(120000L) + .isRevert(null) + .build(); + + LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); + + assertEquals(expect, result); + } + @Test void shouldReturnFirstCommitTimeInPrZeroWhenCommitInfoIsNull() { commitInfo = CommitInfo.builder().commit(Commit.builder().message("mock commit message").build()).build(); @@ -351,12 +414,15 @@ void shouldReturnFirstCommitTimeInPrZeroWhenCommitInfoIsNull() { .prCreatedTime(1658548980000L) .prMergedTime(1658549040000L) .firstCommitTimeInPr(0L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) - .prLeadTime(60000L) + .prLeadTime(0L) .pipelineLeadTime(120000) - .totalTime(180000L) + .firstCommitTime(1658549040000L) + .totalTime(120000L) + .isRevert(Boolean.FALSE) .build(); LeadTime result = githubService.mapLeadTimeWithInfo(pullRequestInfo, deployInfo, commitInfo); @@ -398,11 +464,15 @@ void shouldReturnEmptyLeadTimeGithubShaIsDifferent() { .pipelineName("Name") .leadTimes(List.of(LeadTime.builder() .commitId("111") + .noPRCommitTime(1658548980000L) + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(180000) + .firstCommitTime(1658548980000L) .totalTime(180000) + .isRevert(Boolean.FALSE) .build())) .build()); var pullRequestInfoWithDifferentSha = PullRequestInfo.builder() @@ -430,11 +500,14 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoIsEmpty() { .pipelineName("Name") .leadTimes(List.of(LeadTime.builder() .commitId("111") + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) .totalTime(120000) + .firstCommitTime(1658549040000L) + .isRevert(null) .build())) .build()); when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of()); @@ -454,16 +527,20 @@ void shouldReturnEmptyMergeLeadTimeWhenPullRequestInfoGot404Error() { .pipelineName("Name") .leadTimes(List.of(LeadTime.builder() .commitId("111") + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(120000) + .isRevert(Boolean.FALSE) .build())) .build()); when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenThrow(new NotFoundException("")); when(gitHubFeignClient.getPullRequestCommitInfo(any(), any(), any())).thenReturn(List.of()); - when(gitHubFeignClient.getCommitInfo(any(), any(), any())).thenReturn(new CommitInfo()); + when(gitHubFeignClient.getCommitInfo(any(), any(), any())) + .thenReturn(CommitInfo.builder().commit(Commit.builder().message("").build()).build()); List result = githubService.fetchPipelinesLeadTime(deployTimes, repositoryMap, mockToken); @@ -479,11 +556,14 @@ void shouldReturnEmptyMergeLeadTimeWhenMergeTimeIsEmpty() { .pipelineName("Name") .leadTimes(List.of(LeadTime.builder() .commitId("111") + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(120000) + .isRevert(null) .build())) .build()); when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); @@ -591,12 +671,15 @@ void shouldReturnPipeLineLeadTimeWhenDeployCommitShaIsDifferent() { .pipelineStep(PIPELINE_STEP) .leadTimes(List.of(LeadTime.builder() .commitId("111") + .jobStartTime(1658549040000L) .jobFinishTime(1658549160000L) .pipelineLeadTime(1658549100000L) .pipelineCreateTime(1658549100000L) .prLeadTime(0L) .pipelineLeadTime(120000) + .firstCommitTime(1658549040000L) .totalTime(120000) + .isRevert(null) .build())) .build()); when(gitHubFeignClient.getPullRequestListInfo(any(), any(), any())).thenReturn(List.of(pullRequestInfo)); diff --git a/frontend/e2e/fixtures/create-new/pipeline-data.csv b/frontend/e2e/fixtures/create-new/pipeline-data.csv index b3bab67630..2716056019 100644 --- a/frontend/e2e/fixtures/create-new/pipeline-data.csv +++ b/frontend/e2e/fixtures/create-new/pipeline-data.csv @@ -1,48 +1,48 @@ -"Organization","Pipeline Name","Pipeline Step","Valid","Build Number","Code Committer","Pipeline Creator","First Code Committed Time In PR","Code Committed Time","PR Created Time","PR Merged Time","Deployment Completed Time","Total Lead Time (HH:mm:ss)","PR Lead Time (HH:mm:ss)","Pipeline Lead Time (HH:mm:ss)","Status","Branch" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4210","guzhongren","guzhongren","2024-01-19T14:58:27Z","2024-01-19T15:02:47Z","2024-01-19T14:59:15Z","2024-01-19T15:02:47Z","2024-01-19T15:27:32.983Z","0:24:45","0:0:0","0:24:45","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4208","Steveay","guzhongren",,"2024-01-19T09:51:14Z",,,"2024-01-19T15:09:15.439Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Frontend License","false","4204","Steveay",,,"2024-01-19T09:51:14Z",,,"2024-01-19T11:16:12.025Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4187","sqsq5566",,"2024-01-19T04:01:30Z","2024-01-19T06:18:27Z","2024-01-19T06:07:51Z","2024-01-19T06:18:27Z","2024-01-19T06:37:42.885Z","2:36:12","2:16:57","0:19:15","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4185","neomgb",,"2024-01-18T09:08:32Z","2024-01-19T05:47:23Z","2024-01-19T02:59:59Z","2024-01-19T05:47:24Z","2024-01-19T06:14:32.418Z","21:6:0","20:38:52","0:27:8","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4178","guzhongren","guzhongren",,"2024-01-18T15:51:58Z",,,"2024-01-18T16:56:25.673Z","1:4:27","0:0:0","1:4:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4177","guzhongren","guzhongren",,"2024-01-18T15:49:45Z",,,"2024-01-18T16:18:32.089Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4176","guzhongren","guzhongren",,"2024-01-18T15:37:47Z",,,"2024-01-18T16:06:41.439Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4175","guzhongren","guzhongren",,"2024-01-18T15:16:05Z",,,"2024-01-18T15:53:58.280Z","0:37:53","0:0:0","0:37:53","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4174","guzhongren","guzhongren",,"2024-01-18T15:00:53Z",,,"2024-01-18T15:28:06.427Z","0:27:13","0:0:0","0:27:13","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4173","Chao",,"2024-01-18T09:54:35Z","2024-01-18T10:08:17Z","2024-01-18T05:47:24Z","2024-01-18T10:08:17Z","2024-01-18T10:33:46.039Z","0:39:11","0:13:42","0:25:29","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4171","neomgb",,"2024-01-18T02:33:54Z","2024-01-18T09:41:40Z","2024-01-18T02:54:05Z","2024-01-18T09:41:40Z","2024-01-18T10:07:29.676Z","7:33:35","7:7:46","0:25:49","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4166","junbo dai",,"2024-01-18T08:28:22Z","2024-01-18T08:56:52Z","2024-01-18T08:27:11Z","2024-01-18T08:56:52Z","2024-01-18T09:15:44.306Z","0:47:22","0:28:30","0:18:52","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4162","李雪冰",,"2024-01-18T05:45:03Z","2024-01-18T08:28:08Z","2024-01-18T06:34:30Z","2024-01-18T08:28:09Z","2024-01-18T08:52:58.699Z","3:7:55","2:43:6","0:24:49","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4151","yichen.wang","heartbeat-user",,"2024-01-18T05:35:15Z",,,"2024-01-18T05:56:34.575Z","0:21:19","0:0:0","0:21:19","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4148","yichen.wang","heartbeat-user",,"2024-01-17T15:54:45Z",,,"2024-01-18T03:01:41.593Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4147","Steveay",,"2024-01-17T07:43:29Z","2024-01-17T10:47:00Z","2024-01-17T09:42:52Z","2024-01-17T10:47:01Z","2024-01-17T11:22:02.967Z","3:38:33","3:3:32","0:35:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4146","李雪冰",,"2024-01-17T08:06:19Z","2024-01-17T09:48:38Z","2024-01-17T09:34:27Z","2024-01-17T09:48:38Z","2024-01-17T10:13:39.473Z","2:7:20","1:42:19","0:25:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4137","neomgb","heartbeat-user","2024-01-17T03:05:11Z","2024-01-17T06:46:34Z","2024-01-17T04:01:00Z","2024-01-17T06:46:34Z","2024-01-17T07:22:40.087Z","4:17:29","3:41:23","0:36:6","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4133","junbo dai",,"2024-01-17T03:54:13Z","2024-01-17T06:15:16Z","2024-01-17T03:59:41Z","2024-01-17T06:15:16Z","2024-01-17T06:34:03.987Z","2:39:50","2:21:3","0:18:47","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4123","junbo dai",,,"2024-01-17T03:17:10Z",,,"2024-01-17T03:28:28.520Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4119","sqsq5566",,"2024-01-17T02:26:10Z","2024-01-17T02:55:54Z","2024-01-17T02:31:10Z","2024-01-17T02:55:54Z","2024-01-17T03:14:48.671Z","0:48:38","0:29:44","0:18:54","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4117","junbo dai",,"2024-01-17T02:27:23Z","2024-01-17T02:34:10Z","2024-01-17T02:30:48Z","2024-01-17T02:34:10Z","2024-01-17T02:53:37.896Z","0:26:14","0:6:47","0:19:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4114","mjx20045912",,"2024-01-16T08:44:22Z","2024-01-16T16:27:23Z","2024-01-16T08:46:04Z","2024-01-16T16:27:24Z","2024-01-16T16:46:52.588Z","8:2:30","7:43:2","0:19:28","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4109","Steveay",,"2024-01-16T09:35:48Z","2024-01-16T15:24:35Z","2024-01-16T10:42:42Z","2024-01-16T15:24:36Z","2024-01-16T15:44:39.934Z","6:8:51","5:48:48","0:20:3","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4104","sqsq5566",,"2024-01-16T08:55:43Z","2024-01-16T13:49:22Z","2024-01-16T09:33:36Z","2024-01-16T13:49:23Z","2024-01-16T14:10:20.523Z","5:14:37","4:53:40","0:20:57","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4094","xuebing",,"2024-01-16T08:20:20Z","2024-01-16T08:15:29Z","2024-01-16T08:42:05Z","2024-01-16T09:03:42Z","2024-01-16T09:30:16.889Z","1:9:56","0:43:22","0:26:34","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4087","Yunsong",,"2024-01-16T08:42:19Z","2024-01-16T08:42:19Z","2024-01-16T08:43:18Z","2024-01-16T08:43:45Z","2024-01-16T09:02:46.685Z","0:20:27","0:1:26","0:19:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4078","GuangbinMa","heartbeat-user","2024-01-16T06:33:44Z","2024-01-16T07:03:48Z","2024-01-16T07:25:34Z","2024-01-16T07:58:33Z","2024-01-16T08:32:28.349Z","1:58:44","1:24:49","0:33:55","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4075","Yunsong",,,"2024-01-16T07:51:58Z",,,"2024-01-16T07:58:17.589Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4070","Yunsong",,"2024-01-16T07:06:35Z","2024-01-16T07:06:35Z","2024-01-16T07:11:17Z","2024-01-16T07:31:50Z","2024-01-16T07:50:58.369Z","0:44:23","0:25:15","0:19:8","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4065","Nathan Wang",,"2024-01-16T03:51:04Z","2024-01-16T03:30:16Z","2024-01-10T07:48:39Z","2024-01-16T05:44:17Z","2024-01-16T06:36:44.984Z","2:45:40","1:53:13","0:52:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4064","Simon Tal","heartbeat-user","2024-01-16T03:35:20Z","2024-01-16T03:24:08Z","2024-01-16T03:38:26Z","2024-01-16T03:47:27Z","2024-01-16T06:11:26.201Z","2:36:6","0:12:7","2:23:59","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4056","weiran.sun",,"2024-01-16T02:27:37Z","2024-01-16T02:22:01Z","2024-01-16T02:38:02Z","2024-01-16T02:41:35Z","2024-01-16T03:01:19.266Z","0:33:42","0:13:58","0:19:44","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4054","Jianxun.Ma",,,"2024-01-15T02:07:43Z",,,"2024-01-16T02:22:28.775Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4050","junbo.dai",,"2024-01-16T01:14:24Z","2024-01-15T15:01:57Z","2024-01-16T01:13:59Z","2024-01-16T01:36:23Z","2024-01-16T01:57:13.241Z","0:42:49","0:21:59","0:20:50","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4047","guzhongren","guzhongren",,"2024-01-15T15:41:49Z",,,"2024-01-15T16:10:04.028Z","0:28:15","0:0:0","0:28:15","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy e2e","false","4046","guzhongren","guzhongren",,"2024-01-15T15:18:39Z",,,"2024-01-15T15:40:09.332Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4045","guzhongren","guzhongren",,"2024-01-15T14:30:35Z",,,"2024-01-15T14:59:21.298Z","0:28:46","0:0:0","0:28:46","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4044","GuangbinMa",,"2024-01-15T09:00:10Z","2024-01-15T09:53:15Z","2024-01-15T09:55:27Z","2024-01-15T10:06:21Z","2024-01-15T10:33:54.045Z","1:33:44","1:6:11","0:27:33","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4034","JiangRu1",,,"2024-01-15T07:20:09Z",,,"2024-01-15T08:49:47.422Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Backend License","false","4033","Jianxun.Ma","heartbeat-user",,"2024-01-15T07:34:00Z",,,"2024-01-15T08:14:07.027Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4031","Jianxun.Ma","heartbeat-user",,"2024-01-15T07:34:00Z",,,"2024-01-15T07:58:40.203Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4027","Jianxun.Ma",,,"2024-01-15T07:34:00Z",,,"2024-01-15T08:25:40.681Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4018","GuangbinMa",,,"2024-01-15T06:16:33Z",,,"2024-01-15T06:49:24.921Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4012","Steveay",,"2024-01-12T08:02:24Z","2024-01-15T03:45:40Z","2024-01-15T01:27:53Z","2024-01-15T03:45:41Z","2024-01-15T04:05:06.398Z","68:2:42","67:43:17","0:19:25","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4001","andrea999",,"2024-01-12T09:06:51Z","2024-01-15T01:37:21Z","2024-01-12T09:23:06Z","2024-01-15T01:37:22Z","2024-01-15T01:57:54.756Z","64:51:3","64:30:31","0:20:32","passed","main" +"Organization","Pipeline Name","Pipeline Step","Valid","Build Number","Code Committer","Pipeline Creator","First Code Committed Time In PR","PR Created Time","PR Merged Time","No PR Committed Time","Job Start Time","Pipeline Start Time","Pipeline Finish Time","Total Lead Time (HH:mm:ss)","PR Lead Time (HH:mm:ss)","Pipeline Lead Time (HH:mm:ss)","Status","Branch","Revert" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4210","guzhongren","guzhongren","2024-01-19T14:58:27Z","2024-01-19T14:59:15Z","2024-01-19T15:02:47Z",,"2024-01-19T15:26:59Z","2024-01-19T15:02:47Z","2024-01-19T15:27:32.983Z","0:24:45","0:0:0","0:24:45","passed","main","true" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4208","Steveay","guzhongren",,,,,,,"2024-01-19T15:09:15.439Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Frontend License","false","4204","Steveay",,,,,,,,"2024-01-19T11:16:12.025Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4187","sqsq5566",,"2024-01-19T04:01:30Z","2024-01-19T06:07:51Z","2024-01-19T06:18:27Z",,"2024-01-19T06:37:09Z","2024-01-19T06:18:27Z","2024-01-19T06:37:42.885Z","2:36:12","2:16:57","0:19:15","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4185","neomgb",,"2024-01-18T09:08:32Z","2024-01-19T02:59:59Z","2024-01-19T05:47:24Z",,"2024-01-19T06:14:02Z","2024-01-19T05:47:24Z","2024-01-19T06:14:32.418Z","21:6:0","20:38:52","0:27:8","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4178","guzhongren","guzhongren",,,,"2024-01-18T15:51:58Z","2024-01-18T16:55:57Z","2024-01-18T15:51:58Z","2024-01-18T16:56:25.673Z","1:4:27","0:0:0","1:4:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4177","guzhongren","guzhongren",,,,,,,"2024-01-18T16:18:32.089Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4176","guzhongren","guzhongren",,,,,,,"2024-01-18T16:06:41.439Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4175","guzhongren","guzhongren",,,,"2024-01-18T15:16:05Z","2024-01-18T15:53:30Z","2024-01-18T15:16:05Z","2024-01-18T15:53:58.280Z","0:37:53","0:0:0","0:37:53","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4174","guzhongren","guzhongren",,,,"2024-01-18T15:00:53Z","2024-01-18T15:27:37Z","2024-01-18T15:00:53Z","2024-01-18T15:28:06.427Z","0:27:13","0:0:0","0:27:13","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4173","Chao",,"2024-01-18T09:54:35Z","2024-01-18T05:47:24Z","2024-01-18T10:08:17Z",,"2024-01-18T10:33:18Z","2024-01-18T10:08:17Z","2024-01-18T10:33:46.039Z","0:39:11","0:13:42","0:25:29","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4171","neomgb",,"2024-01-18T02:33:54Z","2024-01-18T02:54:05Z","2024-01-18T09:41:40Z",,"2024-01-18T10:07:00Z","2024-01-18T09:41:40Z","2024-01-18T10:07:29.676Z","7:33:35","7:7:46","0:25:49","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4166","junbo dai",,"2024-01-18T08:28:22Z","2024-01-18T08:27:11Z","2024-01-18T08:56:52Z",,"2024-01-18T09:15:09Z","2024-01-18T08:56:52Z","2024-01-18T09:15:44.306Z","0:47:22","0:28:30","0:18:52","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4162","李雪冰",,"2024-01-18T05:45:03Z","2024-01-18T06:34:30Z","2024-01-18T08:28:09Z",,"2024-01-18T08:52:29Z","2024-01-18T08:28:09Z","2024-01-18T08:52:58.699Z","3:7:55","2:43:6","0:24:49","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4151","yichen.wang","heartbeat-user",,,,"2024-01-18T05:35:15Z","2024-01-18T05:55:59Z","2024-01-18T05:35:15Z","2024-01-18T05:56:34.575Z","0:21:19","0:0:0","0:21:19","passed","main","true" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4148","yichen.wang","heartbeat-user",,,,,,,"2024-01-18T03:01:41.593Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4147","Steveay",,"2024-01-17T07:43:29Z","2024-01-17T09:42:52Z","2024-01-17T10:47:01Z",,"2024-01-17T11:21:20Z","2024-01-17T10:47:01Z","2024-01-17T11:22:02.967Z","3:38:33","3:3:32","0:35:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4146","李雪冰",,"2024-01-17T08:06:19Z","2024-01-17T09:34:27Z","2024-01-17T09:48:38Z",,"2024-01-17T10:13:10Z","2024-01-17T09:48:38Z","2024-01-17T10:13:39.473Z","2:7:20","1:42:19","0:25:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4137","neomgb","heartbeat-user","2024-01-17T03:05:11Z","2024-01-17T04:01:00Z","2024-01-17T06:46:34Z",,"2024-01-17T07:22:06Z","2024-01-17T06:46:34Z","2024-01-17T07:22:40.087Z","4:17:29","3:41:23","0:36:6","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4133","junbo dai",,"2024-01-17T03:54:13Z","2024-01-17T03:59:41Z","2024-01-17T06:15:16Z",,"2024-01-17T06:33:32Z","2024-01-17T06:15:16Z","2024-01-17T06:34:03.987Z","2:39:50","2:21:3","0:18:47","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4123","junbo dai",,,,,,,,"2024-01-17T03:28:28.520Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4119","sqsq5566",,"2024-01-17T02:26:10Z","2024-01-17T02:31:10Z","2024-01-17T02:55:54Z",,"2024-01-17T03:14:17Z","2024-01-17T02:55:54Z","2024-01-17T03:14:48.671Z","0:48:38","0:29:44","0:18:54","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4117","junbo dai",,"2024-01-17T02:27:23Z","2024-01-17T02:30:48Z","2024-01-17T02:34:10Z",,"2024-01-17T02:53:05Z","2024-01-17T02:34:10Z","2024-01-17T02:53:37.896Z","0:26:14","0:6:47","0:19:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4114","mjx20045912",,"2024-01-16T08:44:22Z","2024-01-16T08:46:04Z","2024-01-16T16:27:24Z",,"2024-01-16T16:46:22Z","2024-01-16T16:27:24Z","2024-01-16T16:46:52.588Z","8:2:30","7:43:2","0:19:28","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4109","Steveay",,"2024-01-16T09:35:48Z","2024-01-16T10:42:42Z","2024-01-16T15:24:36Z",,"2024-01-16T15:44:09Z","2024-01-16T15:24:36Z","2024-01-16T15:44:39.934Z","6:8:51","5:48:48","0:20:3","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4104","sqsq5566",,"2024-01-16T08:55:43Z","2024-01-16T09:33:36Z","2024-01-16T13:49:23Z",,"2024-01-16T14:09:48Z","2024-01-16T13:49:23Z","2024-01-16T14:10:20.523Z","5:14:37","4:53:40","0:20:57","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4094","xuebing",,"2024-01-16T08:20:20Z","2024-01-16T08:42:05Z","2024-01-16T09:03:42Z",,"2024-01-16T09:29:48Z","2024-01-16T09:03:42Z","2024-01-16T09:30:16.889Z","1:9:56","0:43:22","0:26:34","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4087","Yunsong",,"2024-01-16T08:42:19Z","2024-01-16T08:43:18Z","2024-01-16T08:43:45Z",,"2024-01-16T09:02:16Z","2024-01-16T08:43:45Z","2024-01-16T09:02:46.685Z","0:20:27","0:1:26","0:19:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4078","GuangbinMa","heartbeat-user","2024-01-16T06:33:44Z","2024-01-16T07:25:34Z","2024-01-16T07:58:33Z",,"2024-01-16T08:31:57Z","2024-01-16T07:58:33Z","2024-01-16T08:32:28.349Z","1:58:44","1:24:49","0:33:55","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4075","Yunsong",,,,,,,,"2024-01-16T07:58:17.589Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4070","Yunsong",,"2024-01-16T07:06:35Z","2024-01-16T07:11:17Z","2024-01-16T07:31:50Z",,"2024-01-16T07:50:18Z","2024-01-16T07:31:50Z","2024-01-16T07:50:58.369Z","0:44:23","0:25:15","0:19:8","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4065","Nathan Wang",,"2024-01-16T03:51:04Z","2024-01-10T07:48:39Z","2024-01-16T05:44:17Z",,"2024-01-16T06:36:15Z","2024-01-16T05:44:17Z","2024-01-16T06:36:44.984Z","2:45:40","1:53:13","0:52:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4064","Simon Tal","heartbeat-user","2024-01-16T03:35:20Z","2024-01-16T03:38:26Z","2024-01-16T03:47:27Z",,"2024-01-16T06:10:58Z","2024-01-16T03:47:27Z","2024-01-16T06:11:26.201Z","2:36:6","0:12:7","2:23:59","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4056","weiran.sun",,"2024-01-16T02:27:37Z","2024-01-16T02:38:02Z","2024-01-16T02:41:35Z",,"2024-01-16T03:00:48Z","2024-01-16T02:41:35Z","2024-01-16T03:01:19.266Z","0:33:42","0:13:58","0:19:44","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4054","Jianxun.Ma",,,,,,,,"2024-01-16T02:22:28.775Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4050","junbo.dai",,"2024-01-16T01:14:24Z","2024-01-16T01:13:59Z","2024-01-16T01:36:23Z",,"2024-01-16T01:56:42Z","2024-01-16T01:36:23Z","2024-01-16T01:57:13.241Z","0:42:49","0:21:59","0:20:50","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4047","guzhongren","guzhongren",,,,"2024-01-15T15:41:49Z","2024-01-15T16:09:35Z","2024-01-15T15:41:49Z","2024-01-15T16:10:04.028Z","0:28:15","0:0:0","0:28:15","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy e2e","false","4046","guzhongren","guzhongren",,,,,,,"2024-01-15T15:40:09.332Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4045","guzhongren","guzhongren",,,,"2024-01-15T14:30:35Z","2024-01-15T14:58:51Z","2024-01-15T14:30:35Z","2024-01-15T14:59:21.298Z","0:28:46","0:0:0","0:28:46","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4044","GuangbinMa",,"2024-01-15T09:00:10Z","2024-01-15T09:55:27Z","2024-01-15T10:06:21Z",,"2024-01-15T10:33:21Z","2024-01-15T10:06:21Z","2024-01-15T10:33:54.045Z","1:33:44","1:6:11","0:27:33","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4034","JiangRu1",,,,,,,,"2024-01-15T08:49:47.422Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Backend License","false","4033","Jianxun.Ma","heartbeat-user",,,,,,,"2024-01-15T08:14:07.027Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4031","Jianxun.Ma","heartbeat-user",,,,,,,"2024-01-15T07:58:40.203Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4027","Jianxun.Ma",,,,,,,,"2024-01-15T08:25:40.681Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4018","GuangbinMa",,,,,,,,"2024-01-15T06:49:24.921Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4012","Steveay",,"2024-01-12T08:02:24Z","2024-01-15T01:27:53Z","2024-01-15T03:45:41Z",,"2024-01-15T04:04:34Z","2024-01-15T03:45:41Z","2024-01-15T04:05:06.398Z","68:2:42","67:43:17","0:19:25","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4001","andrea999",,"2024-01-12T09:06:51Z","2024-01-12T09:23:06Z","2024-01-15T01:37:22Z",,"2024-01-15T01:57:23Z","2024-01-15T01:37:22Z","2024-01-15T01:57:54.756Z","64:51:3","64:30:31","0:20:32","passed","main","false" diff --git a/frontend/e2e/fixtures/import-file/pipeline-data.csv b/frontend/e2e/fixtures/import-file/pipeline-data.csv index b3bab67630..2716056019 100644 --- a/frontend/e2e/fixtures/import-file/pipeline-data.csv +++ b/frontend/e2e/fixtures/import-file/pipeline-data.csv @@ -1,48 +1,48 @@ -"Organization","Pipeline Name","Pipeline Step","Valid","Build Number","Code Committer","Pipeline Creator","First Code Committed Time In PR","Code Committed Time","PR Created Time","PR Merged Time","Deployment Completed Time","Total Lead Time (HH:mm:ss)","PR Lead Time (HH:mm:ss)","Pipeline Lead Time (HH:mm:ss)","Status","Branch" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4210","guzhongren","guzhongren","2024-01-19T14:58:27Z","2024-01-19T15:02:47Z","2024-01-19T14:59:15Z","2024-01-19T15:02:47Z","2024-01-19T15:27:32.983Z","0:24:45","0:0:0","0:24:45","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4208","Steveay","guzhongren",,"2024-01-19T09:51:14Z",,,"2024-01-19T15:09:15.439Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Frontend License","false","4204","Steveay",,,"2024-01-19T09:51:14Z",,,"2024-01-19T11:16:12.025Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4187","sqsq5566",,"2024-01-19T04:01:30Z","2024-01-19T06:18:27Z","2024-01-19T06:07:51Z","2024-01-19T06:18:27Z","2024-01-19T06:37:42.885Z","2:36:12","2:16:57","0:19:15","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4185","neomgb",,"2024-01-18T09:08:32Z","2024-01-19T05:47:23Z","2024-01-19T02:59:59Z","2024-01-19T05:47:24Z","2024-01-19T06:14:32.418Z","21:6:0","20:38:52","0:27:8","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4178","guzhongren","guzhongren",,"2024-01-18T15:51:58Z",,,"2024-01-18T16:56:25.673Z","1:4:27","0:0:0","1:4:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4177","guzhongren","guzhongren",,"2024-01-18T15:49:45Z",,,"2024-01-18T16:18:32.089Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4176","guzhongren","guzhongren",,"2024-01-18T15:37:47Z",,,"2024-01-18T16:06:41.439Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4175","guzhongren","guzhongren",,"2024-01-18T15:16:05Z",,,"2024-01-18T15:53:58.280Z","0:37:53","0:0:0","0:37:53","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4174","guzhongren","guzhongren",,"2024-01-18T15:00:53Z",,,"2024-01-18T15:28:06.427Z","0:27:13","0:0:0","0:27:13","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4173","Chao",,"2024-01-18T09:54:35Z","2024-01-18T10:08:17Z","2024-01-18T05:47:24Z","2024-01-18T10:08:17Z","2024-01-18T10:33:46.039Z","0:39:11","0:13:42","0:25:29","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4171","neomgb",,"2024-01-18T02:33:54Z","2024-01-18T09:41:40Z","2024-01-18T02:54:05Z","2024-01-18T09:41:40Z","2024-01-18T10:07:29.676Z","7:33:35","7:7:46","0:25:49","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4166","junbo dai",,"2024-01-18T08:28:22Z","2024-01-18T08:56:52Z","2024-01-18T08:27:11Z","2024-01-18T08:56:52Z","2024-01-18T09:15:44.306Z","0:47:22","0:28:30","0:18:52","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4162","李雪冰",,"2024-01-18T05:45:03Z","2024-01-18T08:28:08Z","2024-01-18T06:34:30Z","2024-01-18T08:28:09Z","2024-01-18T08:52:58.699Z","3:7:55","2:43:6","0:24:49","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4151","yichen.wang","heartbeat-user",,"2024-01-18T05:35:15Z",,,"2024-01-18T05:56:34.575Z","0:21:19","0:0:0","0:21:19","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4148","yichen.wang","heartbeat-user",,"2024-01-17T15:54:45Z",,,"2024-01-18T03:01:41.593Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4147","Steveay",,"2024-01-17T07:43:29Z","2024-01-17T10:47:00Z","2024-01-17T09:42:52Z","2024-01-17T10:47:01Z","2024-01-17T11:22:02.967Z","3:38:33","3:3:32","0:35:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4146","李雪冰",,"2024-01-17T08:06:19Z","2024-01-17T09:48:38Z","2024-01-17T09:34:27Z","2024-01-17T09:48:38Z","2024-01-17T10:13:39.473Z","2:7:20","1:42:19","0:25:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4137","neomgb","heartbeat-user","2024-01-17T03:05:11Z","2024-01-17T06:46:34Z","2024-01-17T04:01:00Z","2024-01-17T06:46:34Z","2024-01-17T07:22:40.087Z","4:17:29","3:41:23","0:36:6","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4133","junbo dai",,"2024-01-17T03:54:13Z","2024-01-17T06:15:16Z","2024-01-17T03:59:41Z","2024-01-17T06:15:16Z","2024-01-17T06:34:03.987Z","2:39:50","2:21:3","0:18:47","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4123","junbo dai",,,"2024-01-17T03:17:10Z",,,"2024-01-17T03:28:28.520Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4119","sqsq5566",,"2024-01-17T02:26:10Z","2024-01-17T02:55:54Z","2024-01-17T02:31:10Z","2024-01-17T02:55:54Z","2024-01-17T03:14:48.671Z","0:48:38","0:29:44","0:18:54","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4117","junbo dai",,"2024-01-17T02:27:23Z","2024-01-17T02:34:10Z","2024-01-17T02:30:48Z","2024-01-17T02:34:10Z","2024-01-17T02:53:37.896Z","0:26:14","0:6:47","0:19:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4114","mjx20045912",,"2024-01-16T08:44:22Z","2024-01-16T16:27:23Z","2024-01-16T08:46:04Z","2024-01-16T16:27:24Z","2024-01-16T16:46:52.588Z","8:2:30","7:43:2","0:19:28","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4109","Steveay",,"2024-01-16T09:35:48Z","2024-01-16T15:24:35Z","2024-01-16T10:42:42Z","2024-01-16T15:24:36Z","2024-01-16T15:44:39.934Z","6:8:51","5:48:48","0:20:3","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4104","sqsq5566",,"2024-01-16T08:55:43Z","2024-01-16T13:49:22Z","2024-01-16T09:33:36Z","2024-01-16T13:49:23Z","2024-01-16T14:10:20.523Z","5:14:37","4:53:40","0:20:57","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4094","xuebing",,"2024-01-16T08:20:20Z","2024-01-16T08:15:29Z","2024-01-16T08:42:05Z","2024-01-16T09:03:42Z","2024-01-16T09:30:16.889Z","1:9:56","0:43:22","0:26:34","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4087","Yunsong",,"2024-01-16T08:42:19Z","2024-01-16T08:42:19Z","2024-01-16T08:43:18Z","2024-01-16T08:43:45Z","2024-01-16T09:02:46.685Z","0:20:27","0:1:26","0:19:1","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4078","GuangbinMa","heartbeat-user","2024-01-16T06:33:44Z","2024-01-16T07:03:48Z","2024-01-16T07:25:34Z","2024-01-16T07:58:33Z","2024-01-16T08:32:28.349Z","1:58:44","1:24:49","0:33:55","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4075","Yunsong",,,"2024-01-16T07:51:58Z",,,"2024-01-16T07:58:17.589Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4070","Yunsong",,"2024-01-16T07:06:35Z","2024-01-16T07:06:35Z","2024-01-16T07:11:17Z","2024-01-16T07:31:50Z","2024-01-16T07:50:58.369Z","0:44:23","0:25:15","0:19:8","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4065","Nathan Wang",,"2024-01-16T03:51:04Z","2024-01-16T03:30:16Z","2024-01-10T07:48:39Z","2024-01-16T05:44:17Z","2024-01-16T06:36:44.984Z","2:45:40","1:53:13","0:52:27","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4064","Simon Tal","heartbeat-user","2024-01-16T03:35:20Z","2024-01-16T03:24:08Z","2024-01-16T03:38:26Z","2024-01-16T03:47:27Z","2024-01-16T06:11:26.201Z","2:36:6","0:12:7","2:23:59","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4056","weiran.sun",,"2024-01-16T02:27:37Z","2024-01-16T02:22:01Z","2024-01-16T02:38:02Z","2024-01-16T02:41:35Z","2024-01-16T03:01:19.266Z","0:33:42","0:13:58","0:19:44","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4054","Jianxun.Ma",,,"2024-01-15T02:07:43Z",,,"2024-01-16T02:22:28.775Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4050","junbo.dai",,"2024-01-16T01:14:24Z","2024-01-15T15:01:57Z","2024-01-16T01:13:59Z","2024-01-16T01:36:23Z","2024-01-16T01:57:13.241Z","0:42:49","0:21:59","0:20:50","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4047","guzhongren","guzhongren",,"2024-01-15T15:41:49Z",,,"2024-01-15T16:10:04.028Z","0:28:15","0:0:0","0:28:15","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy e2e","false","4046","guzhongren","guzhongren",,"2024-01-15T15:18:39Z",,,"2024-01-15T15:40:09.332Z",,,,"passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4045","guzhongren","guzhongren",,"2024-01-15T14:30:35Z",,,"2024-01-15T14:59:21.298Z","0:28:46","0:0:0","0:28:46","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4044","GuangbinMa",,"2024-01-15T09:00:10Z","2024-01-15T09:53:15Z","2024-01-15T09:55:27Z","2024-01-15T10:06:21Z","2024-01-15T10:33:54.045Z","1:33:44","1:6:11","0:27:33","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4034","JiangRu1",,,"2024-01-15T07:20:09Z",,,"2024-01-15T08:49:47.422Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Backend License","false","4033","Jianxun.Ma","heartbeat-user",,"2024-01-15T07:34:00Z",,,"2024-01-15T08:14:07.027Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4031","Jianxun.Ma","heartbeat-user",,"2024-01-15T07:34:00Z",,,"2024-01-15T07:58:40.203Z",,,,"canceled","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4027","Jianxun.Ma",,,"2024-01-15T07:34:00Z",,,"2024-01-15T08:25:40.681Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4018","GuangbinMa",,,"2024-01-15T06:16:33Z",,,"2024-01-15T06:49:24.921Z",,,,"failed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4012","Steveay",,"2024-01-12T08:02:24Z","2024-01-15T03:45:40Z","2024-01-15T01:27:53Z","2024-01-15T03:45:41Z","2024-01-15T04:05:06.398Z","68:2:42","67:43:17","0:19:25","passed","main" -"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4001","andrea999",,"2024-01-12T09:06:51Z","2024-01-15T01:37:21Z","2024-01-12T09:23:06Z","2024-01-15T01:37:22Z","2024-01-15T01:57:54.756Z","64:51:3","64:30:31","0:20:32","passed","main" +"Organization","Pipeline Name","Pipeline Step","Valid","Build Number","Code Committer","Pipeline Creator","First Code Committed Time In PR","PR Created Time","PR Merged Time","No PR Committed Time","Job Start Time","Pipeline Start Time","Pipeline Finish Time","Total Lead Time (HH:mm:ss)","PR Lead Time (HH:mm:ss)","Pipeline Lead Time (HH:mm:ss)","Status","Branch","Revert" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4210","guzhongren","guzhongren","2024-01-19T14:58:27Z","2024-01-19T14:59:15Z","2024-01-19T15:02:47Z",,"2024-01-19T15:26:59Z","2024-01-19T15:02:47Z","2024-01-19T15:27:32.983Z","0:24:45","0:0:0","0:24:45","passed","main","true" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4208","Steveay","guzhongren",,,,,,,"2024-01-19T15:09:15.439Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Frontend License","false","4204","Steveay",,,,,,,,"2024-01-19T11:16:12.025Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4187","sqsq5566",,"2024-01-19T04:01:30Z","2024-01-19T06:07:51Z","2024-01-19T06:18:27Z",,"2024-01-19T06:37:09Z","2024-01-19T06:18:27Z","2024-01-19T06:37:42.885Z","2:36:12","2:16:57","0:19:15","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4185","neomgb",,"2024-01-18T09:08:32Z","2024-01-19T02:59:59Z","2024-01-19T05:47:24Z",,"2024-01-19T06:14:02Z","2024-01-19T05:47:24Z","2024-01-19T06:14:32.418Z","21:6:0","20:38:52","0:27:8","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4178","guzhongren","guzhongren",,,,"2024-01-18T15:51:58Z","2024-01-18T16:55:57Z","2024-01-18T15:51:58Z","2024-01-18T16:56:25.673Z","1:4:27","0:0:0","1:4:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4177","guzhongren","guzhongren",,,,,,,"2024-01-18T16:18:32.089Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4176","guzhongren","guzhongren",,,,,,,"2024-01-18T16:06:41.439Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4175","guzhongren","guzhongren",,,,"2024-01-18T15:16:05Z","2024-01-18T15:53:30Z","2024-01-18T15:16:05Z","2024-01-18T15:53:58.280Z","0:37:53","0:0:0","0:37:53","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4174","guzhongren","guzhongren",,,,"2024-01-18T15:00:53Z","2024-01-18T15:27:37Z","2024-01-18T15:00:53Z","2024-01-18T15:28:06.427Z","0:27:13","0:0:0","0:27:13","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4173","Chao",,"2024-01-18T09:54:35Z","2024-01-18T05:47:24Z","2024-01-18T10:08:17Z",,"2024-01-18T10:33:18Z","2024-01-18T10:08:17Z","2024-01-18T10:33:46.039Z","0:39:11","0:13:42","0:25:29","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4171","neomgb",,"2024-01-18T02:33:54Z","2024-01-18T02:54:05Z","2024-01-18T09:41:40Z",,"2024-01-18T10:07:00Z","2024-01-18T09:41:40Z","2024-01-18T10:07:29.676Z","7:33:35","7:7:46","0:25:49","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4166","junbo dai",,"2024-01-18T08:28:22Z","2024-01-18T08:27:11Z","2024-01-18T08:56:52Z",,"2024-01-18T09:15:09Z","2024-01-18T08:56:52Z","2024-01-18T09:15:44.306Z","0:47:22","0:28:30","0:18:52","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4162","李雪冰",,"2024-01-18T05:45:03Z","2024-01-18T06:34:30Z","2024-01-18T08:28:09Z",,"2024-01-18T08:52:29Z","2024-01-18T08:28:09Z","2024-01-18T08:52:58.699Z","3:7:55","2:43:6","0:24:49","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4151","yichen.wang","heartbeat-user",,,,"2024-01-18T05:35:15Z","2024-01-18T05:55:59Z","2024-01-18T05:35:15Z","2024-01-18T05:56:34.575Z","0:21:19","0:0:0","0:21:19","passed","main","true" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4148","yichen.wang","heartbeat-user",,,,,,,"2024-01-18T03:01:41.593Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4147","Steveay",,"2024-01-17T07:43:29Z","2024-01-17T09:42:52Z","2024-01-17T10:47:01Z",,"2024-01-17T11:21:20Z","2024-01-17T10:47:01Z","2024-01-17T11:22:02.967Z","3:38:33","3:3:32","0:35:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4146","李雪冰",,"2024-01-17T08:06:19Z","2024-01-17T09:34:27Z","2024-01-17T09:48:38Z",,"2024-01-17T10:13:10Z","2024-01-17T09:48:38Z","2024-01-17T10:13:39.473Z","2:7:20","1:42:19","0:25:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4137","neomgb","heartbeat-user","2024-01-17T03:05:11Z","2024-01-17T04:01:00Z","2024-01-17T06:46:34Z",,"2024-01-17T07:22:06Z","2024-01-17T06:46:34Z","2024-01-17T07:22:40.087Z","4:17:29","3:41:23","0:36:6","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4133","junbo dai",,"2024-01-17T03:54:13Z","2024-01-17T03:59:41Z","2024-01-17T06:15:16Z",,"2024-01-17T06:33:32Z","2024-01-17T06:15:16Z","2024-01-17T06:34:03.987Z","2:39:50","2:21:3","0:18:47","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4123","junbo dai",,,,,,,,"2024-01-17T03:28:28.520Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4119","sqsq5566",,"2024-01-17T02:26:10Z","2024-01-17T02:31:10Z","2024-01-17T02:55:54Z",,"2024-01-17T03:14:17Z","2024-01-17T02:55:54Z","2024-01-17T03:14:48.671Z","0:48:38","0:29:44","0:18:54","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4117","junbo dai",,"2024-01-17T02:27:23Z","2024-01-17T02:30:48Z","2024-01-17T02:34:10Z",,"2024-01-17T02:53:05Z","2024-01-17T02:34:10Z","2024-01-17T02:53:37.896Z","0:26:14","0:6:47","0:19:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4114","mjx20045912",,"2024-01-16T08:44:22Z","2024-01-16T08:46:04Z","2024-01-16T16:27:24Z",,"2024-01-16T16:46:22Z","2024-01-16T16:27:24Z","2024-01-16T16:46:52.588Z","8:2:30","7:43:2","0:19:28","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4109","Steveay",,"2024-01-16T09:35:48Z","2024-01-16T10:42:42Z","2024-01-16T15:24:36Z",,"2024-01-16T15:44:09Z","2024-01-16T15:24:36Z","2024-01-16T15:44:39.934Z","6:8:51","5:48:48","0:20:3","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4104","sqsq5566",,"2024-01-16T08:55:43Z","2024-01-16T09:33:36Z","2024-01-16T13:49:23Z",,"2024-01-16T14:09:48Z","2024-01-16T13:49:23Z","2024-01-16T14:10:20.523Z","5:14:37","4:53:40","0:20:57","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4094","xuebing",,"2024-01-16T08:20:20Z","2024-01-16T08:42:05Z","2024-01-16T09:03:42Z",,"2024-01-16T09:29:48Z","2024-01-16T09:03:42Z","2024-01-16T09:30:16.889Z","1:9:56","0:43:22","0:26:34","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4087","Yunsong",,"2024-01-16T08:42:19Z","2024-01-16T08:43:18Z","2024-01-16T08:43:45Z",,"2024-01-16T09:02:16Z","2024-01-16T08:43:45Z","2024-01-16T09:02:46.685Z","0:20:27","0:1:26","0:19:1","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4078","GuangbinMa","heartbeat-user","2024-01-16T06:33:44Z","2024-01-16T07:25:34Z","2024-01-16T07:58:33Z",,"2024-01-16T08:31:57Z","2024-01-16T07:58:33Z","2024-01-16T08:32:28.349Z","1:58:44","1:24:49","0:33:55","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4075","Yunsong",,,,,,,,"2024-01-16T07:58:17.589Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4070","Yunsong",,"2024-01-16T07:06:35Z","2024-01-16T07:11:17Z","2024-01-16T07:31:50Z",,"2024-01-16T07:50:18Z","2024-01-16T07:31:50Z","2024-01-16T07:50:58.369Z","0:44:23","0:25:15","0:19:8","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4065","Nathan Wang",,"2024-01-16T03:51:04Z","2024-01-10T07:48:39Z","2024-01-16T05:44:17Z",,"2024-01-16T06:36:15Z","2024-01-16T05:44:17Z","2024-01-16T06:36:44.984Z","2:45:40","1:53:13","0:52:27","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4064","Simon Tal","heartbeat-user","2024-01-16T03:35:20Z","2024-01-16T03:38:26Z","2024-01-16T03:47:27Z",,"2024-01-16T06:10:58Z","2024-01-16T03:47:27Z","2024-01-16T06:11:26.201Z","2:36:6","0:12:7","2:23:59","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4056","weiran.sun",,"2024-01-16T02:27:37Z","2024-01-16T02:38:02Z","2024-01-16T02:41:35Z",,"2024-01-16T03:00:48Z","2024-01-16T02:41:35Z","2024-01-16T03:01:19.266Z","0:33:42","0:13:58","0:19:44","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4054","Jianxun.Ma",,,,,,,,"2024-01-16T02:22:28.775Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4050","junbo.dai",,"2024-01-16T01:14:24Z","2024-01-16T01:13:59Z","2024-01-16T01:36:23Z",,"2024-01-16T01:56:42Z","2024-01-16T01:36:23Z","2024-01-16T01:57:13.241Z","0:42:49","0:21:59","0:20:50","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4047","guzhongren","guzhongren",,,,"2024-01-15T15:41:49Z","2024-01-15T16:09:35Z","2024-01-15T15:41:49Z","2024-01-15T16:10:04.028Z","0:28:15","0:0:0","0:28:15","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy e2e","false","4046","guzhongren","guzhongren",,,,,,,"2024-01-15T15:40:09.332Z",,,,"passed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4045","guzhongren","guzhongren",,,,"2024-01-15T14:30:35Z","2024-01-15T14:58:51Z","2024-01-15T14:30:35Z","2024-01-15T14:59:21.298Z","0:28:46","0:0:0","0:28:46","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4044","GuangbinMa",,"2024-01-15T09:00:10Z","2024-01-15T09:55:27Z","2024-01-15T10:06:21Z",,"2024-01-15T10:33:21Z","2024-01-15T10:06:21Z","2024-01-15T10:33:54.045Z","1:33:44","1:6:11","0:27:33","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4034","JiangRu1",,,,,,,,"2024-01-15T08:49:47.422Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":mag: Check Backend License","false","4033","Jianxun.Ma","heartbeat-user",,,,,,,"2024-01-15T08:14:07.027Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":pipeline: Upload pipeline.yml","false","4031","Jianxun.Ma","heartbeat-user",,,,,,,"2024-01-15T07:58:40.203Z",,,,"canceled","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4027","Jianxun.Ma",,,,,,,,"2024-01-15T08:25:40.681Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Run e2e","false","4018","GuangbinMa",,,,,,,,"2024-01-15T06:49:24.921Z",,,,"failed","main","" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4012","Steveay",,"2024-01-12T08:02:24Z","2024-01-15T01:27:53Z","2024-01-15T03:45:41Z",,"2024-01-15T04:04:34Z","2024-01-15T03:45:41Z","2024-01-15T04:05:06.398Z","68:2:42","67:43:17","0:19:25","passed","main","false" +"Thoughtworks-Heartbeat","Heartbeat",":rocket: Deploy prod","true","4001","andrea999",,"2024-01-12T09:06:51Z","2024-01-12T09:23:06Z","2024-01-15T01:37:22Z",,"2024-01-15T01:57:23Z","2024-01-15T01:37:22Z","2024-01-15T01:57:54.756Z","64:51:3","64:30:31","0:20:32","passed","main","false" From 6688b36426d2d11745db0adb62c863ebbce2a1ef Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Fri, 12 Apr 2024 15:54:44 +0800 Subject: [PATCH 03/11] ADM-898-fix fix: fix flag card logic --- .../MetricsStep/CycleTime/FlagCard.tsx | 2 +- .../MetricsStep/CycleTime/Table/index.tsx | 3 +++ .../containers/MetricsStep/CycleTime/index.tsx | 18 +++++++++--------- frontend/src/context/Metrics/metricsSlice.ts | 16 ++++++---------- frontend/src/utils/util.ts | 9 ++------- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx b/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx index cf3532ca48..48999ef0e1 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx @@ -2,7 +2,7 @@ import { selectTreatFlagCardAsBlock, updateTreatFlagCardAsBlock } from '@src/con import { FlagCardItem, ItemCheckbox, ItemText } from '@src/containers/MetricsStep/CycleTime/style'; import { useAppDispatch } from '@src/hooks/useAppDispatch'; import { useAppSelector } from '@src/hooks'; -import React from 'react'; +import React, { useEffect } from 'react'; const FlagCard = () => { const dispatch = useAppDispatch(); diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index b53e8533c8..e30de8d7df 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -21,6 +21,7 @@ import { FormControlLabel, Radio, Table, TableBody, TableContainer, TableHead, T import CellAutoComplete from '@src/containers/MetricsStep/CycleTime/Table/CellAutoComplete'; import EllipsisText from '@src/components/Common/EllipsisText'; import { useAppDispatch } from '@src/hooks/useAppDispatch'; +import { existBlockState } from '@src/utils/util'; import { useAppSelector } from '@src/hooks'; import React, { useCallback } from 'react'; import { theme } from '@src/theme'; @@ -57,6 +58,8 @@ const CycleTimeTable = () => { ); isColumnAsKey && resetRealDoneColumn(name, value); dispatch(updateCycleTimeSettings(newCycleTimeSettings)); + if (!existBlockState(newCycleTimeSettings)) { + } dispatch(updateReworkTimesSettings({ excludeStates: [], reworkState: null })); }, [cycleTimeSettings, dispatch, isColumnAsKey, resetRealDoneColumn], diff --git a/frontend/src/containers/MetricsStep/CycleTime/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/index.tsx index 1fcf12999d..058d15584a 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/index.tsx @@ -13,7 +13,7 @@ import FlagCard from '@src/containers/MetricsStep/CycleTime/FlagCard'; import { useAppDispatch } from '@src/hooks/useAppDispatch'; import { MESSAGE, TIPS } from '@src/constants/resources'; import { useEffect, useMemo, useState } from 'react'; -import { existBlockColumn } from '@src/utils/util'; +import { existBlockState } from '@src/utils/util'; import { useAppSelector } from '@src/hooks'; export const CycleTime = () => { @@ -21,22 +21,22 @@ export const CycleTime = () => { const flagCardAsBlock = useAppSelector(selectTreatFlagCardAsBlock); const displayFlagCardDropWarning = useAppSelector(selectDisplayFlagCardDropWarning); const warningMessage = useAppSelector(selectCycleTimeWarningMessage); - const { cycleTimeSettings, cycleTimeSettingsType } = useAppSelector(selectMetricsContent); - const hasBlockColumn = useMemo(() => { - return existBlockColumn(cycleTimeSettingsType, cycleTimeSettings); - }, [cycleTimeSettingsType, cycleTimeSettings]); + const { cycleTimeSettings } = useAppSelector(selectMetricsContent); + const hasBlockState = useMemo(() => { + return existBlockState(cycleTimeSettings); + }, [cycleTimeSettings]); const [shouldShowConflictMessage, setShouldShowConflictMessage] = useState(false); useEffect(() => { - if (hasBlockColumn && displayFlagCardDropWarning) { + if (hasBlockState && displayFlagCardDropWarning) { setShouldShowConflictMessage(true); dispatch(updateDisplayFlagCardDropWarning(false)); } - if (hasBlockColumn && flagCardAsBlock) { + if (hasBlockState && flagCardAsBlock) { dispatch(updateTreatFlagCardAsBlock(false)); } - }, [dispatch, flagCardAsBlock, displayFlagCardDropWarning, hasBlockColumn]); + }, [dispatch, flagCardAsBlock, displayFlagCardDropWarning, hasBlockState]); return (
@@ -44,7 +44,7 @@ export const CycleTime = () => { {warningMessage && } - {hasBlockColumn || } + {hasBlockState || }
); }; diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index 9fb44e60fb..c559783409 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -5,7 +5,7 @@ import { MESSAGE, METRICS_CONSTANTS, } from '@src/constants/resources'; -import { convertCycleTimeSettings, existBlockColumn, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; +import { convertCycleTimeSettings, existBlockState, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice'; import { createSlice } from '@reduxjs/toolkit'; import camelCase from 'lodash.camelcase'; @@ -294,15 +294,11 @@ function resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue: string[] function initTreatFlagCardAsBlock( preTreatFlagCardAsBlock: boolean, - preHasBlockColumn: boolean, + preHasBlockState: boolean, state: ISavedMetricsSettingState, ) { - if ( - !preTreatFlagCardAsBlock && - preHasBlockColumn && - !existBlockColumn(state.cycleTimeSettingsType, state.cycleTimeSettings) - ) { - state.treatFlagCardAsBlock = true; + if (!preTreatFlagCardAsBlock && preHasBlockState && !existBlockState(state.cycleTimeSettings)) { + state.treatFlagCardAsBlock = false; } } @@ -396,7 +392,7 @@ export const metricsSlice = createSlice({ const preJiraColumnsValue = getSortedAndDeduplicationBoardingMapping(state.cycleTimeSettings).filter( (item) => item !== METRICS_CONSTANTS.cycleTimeEmptyStr, ); - const preHasBlockColumn = existBlockColumn(state.cycleTimeSettingsType, state.cycleTimeSettings); + const preHasBlockColumn = existBlockState(state.cycleTimeSettings); const preTreatFlagCardAsBlock = state.treatFlagCardAsBlock; state.displayFlagCardDropWarning = @@ -463,7 +459,7 @@ export const metricsSlice = createSlice({ ? getCycleTimeSettingsByColumn(state, jiraColumns) : getCycleTimeSettingsByStatus(state, jiraColumns); } - initTreatFlagCardAsBlock(preTreatFlagCardAsBlock, preHasBlockColumn, state); + // initTreatFlagCardAsBlock(preTreatFlagCardAsBlock, preHasBlockColumn, state); resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue, state); if (!isProjectCreated && importedDoneStatus.length > 0) { diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 65f08103a5..ec0a6f1d84 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -162,11 +162,6 @@ export function convertCycleTimeSettings( return cycleTimeSettings?.map(({ status, value }: ICycleTimeSetting) => ({ [status]: value })); } -export function existBlockColumn( - cycleTimeSettingsType: CYCLE_TIME_SETTINGS_TYPES, - cycleTimeSettings: ICycleTimeSetting[], -) { - return cycleTimeSettingsType === CYCLE_TIME_SETTINGS_TYPES.BY_COLUMN - ? cycleTimeSettings.some(({ column }) => BLOCK_COLUMN_NAME.includes(column.toUpperCase())) - : cycleTimeSettings.some(({ status }) => BLOCK_COLUMN_NAME.includes(status.toUpperCase())); +export function existBlockState(cycleTimeSettings: ICycleTimeSetting[]) { + return cycleTimeSettings.some(({ value }) => METRICS_CONSTANTS.blockValue === value); } From 642da985d961ec6e1d6bfedc27a9baf3bda00584 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Fri, 12 Apr 2024 16:58:53 +0800 Subject: [PATCH 04/11] ADM-898-fix fix: init flag as block state when boarding mapping changed --- .../MetricsStep/CycleTime/Table/index.tsx | 33 ++++++++++++++----- .../MetricsStep/CycleTime/index.tsx | 5 --- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index e30de8d7df..2f8ed639a4 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -1,17 +1,19 @@ -import { - CYCLE_TIME_SETTINGS_TYPES, - DONE, - METRICS_CONSTANTS, - METRICS_CYCLE_SETTING_TABLE_HEADER_BY_COLUMN, - METRICS_CYCLE_SETTING_TABLE_HEADER_BY_STATUS, -} from '@src/constants/resources'; import { updateCycleTimeSettings, saveDoneColumn, selectMetricsContent, setCycleTimeSettingsType, updateReworkTimesSettings, + updateTreatFlagCardAsBlock, + ICycleTimeSetting, } from '@src/context/Metrics/metricsSlice'; +import { + CYCLE_TIME_SETTINGS_TYPES, + DONE, + METRICS_CONSTANTS, + METRICS_CYCLE_SETTING_TABLE_HEADER_BY_COLUMN, + METRICS_CYCLE_SETTING_TABLE_HEADER_BY_STATUS, +} from '@src/constants/resources'; import { StyledRadioGroup, StyledTableHeaderCell, @@ -46,8 +48,22 @@ const CycleTimeTable = () => { [cycleTimeSettings, dispatch], ); + function updateTreatFlagCardAsBlockByCycleTimeSetting( + newCycleTimeSettings: ICycleTimeSetting[], + preHasBlockState: boolean, + ) { + if (existBlockState(newCycleTimeSettings)) { + dispatch(updateTreatFlagCardAsBlock(true)); + } else { + if (preHasBlockState) { + dispatch(updateTreatFlagCardAsBlock(true)); + } + } + } + const saveCycleTimeOptions = useCallback( (name: string, value: string) => { + const preHasBlockState = existBlockState(cycleTimeSettings); const newCycleTimeSettings = cycleTimeSettings.map((item) => (isColumnAsKey ? item.column === name : item.status === name) ? { @@ -57,9 +73,8 @@ const CycleTimeTable = () => { : item, ); isColumnAsKey && resetRealDoneColumn(name, value); + updateTreatFlagCardAsBlockByCycleTimeSetting(newCycleTimeSettings, preHasBlockState); dispatch(updateCycleTimeSettings(newCycleTimeSettings)); - if (!existBlockState(newCycleTimeSettings)) { - } dispatch(updateReworkTimesSettings({ excludeStates: [], reworkState: null })); }, [cycleTimeSettings, dispatch, isColumnAsKey, resetRealDoneColumn], diff --git a/frontend/src/containers/MetricsStep/CycleTime/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/index.tsx index 058d15584a..07e69db021 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/index.tsx @@ -4,7 +4,6 @@ import { selectMetricsContent, selectTreatFlagCardAsBlock, updateDisplayFlagCardDropWarning, - updateTreatFlagCardAsBlock, } from '@src/context/Metrics/metricsSlice'; import SectionTitleWithTooltip from '@src/components/Common/SectionTitleWithTooltip'; import { WarningNotification } from '@src/components/Common/WarningNotification'; @@ -32,10 +31,6 @@ export const CycleTime = () => { setShouldShowConflictMessage(true); dispatch(updateDisplayFlagCardDropWarning(false)); } - - if (hasBlockState && flagCardAsBlock) { - dispatch(updateTreatFlagCardAsBlock(false)); - } }, [dispatch, flagCardAsBlock, displayFlagCardDropWarning, hasBlockState]); return ( From 3b403fcb561853765010d03ad8c1bfc8311888ca Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Fri, 12 Apr 2024 17:54:17 +0800 Subject: [PATCH 05/11] ADM-898-fix fix: remove useless code --- .../src/containers/MetricsStep/CycleTime/FlagCard.tsx | 2 +- frontend/src/context/Metrics/metricsSlice.ts | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx b/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx index 48999ef0e1..cf3532ca48 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/FlagCard.tsx @@ -2,7 +2,7 @@ import { selectTreatFlagCardAsBlock, updateTreatFlagCardAsBlock } from '@src/con import { FlagCardItem, ItemCheckbox, ItemText } from '@src/containers/MetricsStep/CycleTime/style'; import { useAppDispatch } from '@src/hooks/useAppDispatch'; import { useAppSelector } from '@src/hooks'; -import React, { useEffect } from 'react'; +import React from 'react'; const FlagCard = () => { const dispatch = useAppDispatch(); diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index c559783409..6dc04a295e 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -292,16 +292,6 @@ function resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue: string[] }; } -function initTreatFlagCardAsBlock( - preTreatFlagCardAsBlock: boolean, - preHasBlockState: boolean, - state: ISavedMetricsSettingState, -) { - if (!preTreatFlagCardAsBlock && preHasBlockState && !existBlockState(state.cycleTimeSettings)) { - state.treatFlagCardAsBlock = false; - } -} - export const metricsSlice = createSlice({ name: 'metrics', initialState, @@ -459,7 +449,6 @@ export const metricsSlice = createSlice({ ? getCycleTimeSettingsByColumn(state, jiraColumns) : getCycleTimeSettingsByStatus(state, jiraColumns); } - // initTreatFlagCardAsBlock(preTreatFlagCardAsBlock, preHasBlockColumn, state); resetReworkTimeSettingWhenMappingModified(preJiraColumnsValue, state); if (!isProjectCreated && importedDoneStatus.length > 0) { From 7a4982dc9a2eb3307fc903db93d9ff3569482241 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Fri, 12 Apr 2024 18:45:50 +0800 Subject: [PATCH 06/11] ADM-898-fix fix: fix flag logic --- .../src/containers/MetricsStep/CycleTime/Table/index.tsx | 6 +----- frontend/src/containers/MetricsStep/CycleTime/index.tsx | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index 2f8ed639a4..1396d47957 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -52,12 +52,8 @@ const CycleTimeTable = () => { newCycleTimeSettings: ICycleTimeSetting[], preHasBlockState: boolean, ) { - if (existBlockState(newCycleTimeSettings)) { + if (!existBlockState(newCycleTimeSettings) && preHasBlockState) { dispatch(updateTreatFlagCardAsBlock(true)); - } else { - if (preHasBlockState) { - dispatch(updateTreatFlagCardAsBlock(true)); - } } } diff --git a/frontend/src/containers/MetricsStep/CycleTime/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/index.tsx index 07e69db021..53683d4a93 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/index.tsx @@ -4,6 +4,7 @@ import { selectMetricsContent, selectTreatFlagCardAsBlock, updateDisplayFlagCardDropWarning, + updateTreatFlagCardAsBlock, } from '@src/context/Metrics/metricsSlice'; import SectionTitleWithTooltip from '@src/components/Common/SectionTitleWithTooltip'; import { WarningNotification } from '@src/components/Common/WarningNotification'; @@ -31,6 +32,9 @@ export const CycleTime = () => { setShouldShowConflictMessage(true); dispatch(updateDisplayFlagCardDropWarning(false)); } + if (hasBlockState && flagCardAsBlock) { + dispatch(updateTreatFlagCardAsBlock(false)); + } }, [dispatch, flagCardAsBlock, displayFlagCardDropWarning, hasBlockState]); return ( From 83a8ee9440fda889ed670be28fefc1eef0a7e677 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Mon, 15 Apr 2024 09:49:15 +0800 Subject: [PATCH 07/11] ADM-898-fix fix: fix eslint --- .../MetricsStep/CycleTime/Table/index.tsx | 16 ++++++++-------- frontend/src/context/Metrics/metricsSlice.ts | 2 -- frontend/src/utils/util.ts | 7 +------ 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index 1396d47957..d17c8a598a 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -48,14 +48,14 @@ const CycleTimeTable = () => { [cycleTimeSettings, dispatch], ); - function updateTreatFlagCardAsBlockByCycleTimeSetting( - newCycleTimeSettings: ICycleTimeSetting[], - preHasBlockState: boolean, - ) { - if (!existBlockState(newCycleTimeSettings) && preHasBlockState) { - dispatch(updateTreatFlagCardAsBlock(true)); - } - } + const updateTreatFlagCardAsBlockByCycleTimeSetting = useCallback( + (newCycleTimeSettings: ICycleTimeSetting[], preHasBlockState: boolean) => { + if (!existBlockState(newCycleTimeSettings) && preHasBlockState) { + dispatch(updateTreatFlagCardAsBlock(true)); + } + }, + [dispatch], + ); const saveCycleTimeOptions = useCallback( (name: string, value: string) => { diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index 6dc04a295e..c6fb708087 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -382,8 +382,6 @@ export const metricsSlice = createSlice({ const preJiraColumnsValue = getSortedAndDeduplicationBoardingMapping(state.cycleTimeSettings).filter( (item) => item !== METRICS_CONSTANTS.cycleTimeEmptyStr, ); - const preHasBlockColumn = existBlockState(state.cycleTimeSettings); - const preTreatFlagCardAsBlock = state.treatFlagCardAsBlock; state.displayFlagCardDropWarning = state.displayFlagCardDropWarning && !isProjectCreated && importedCycleTime.importedTreatFlagCardAsBlock; diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index ec0a6f1d84..657f2119ed 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -1,9 +1,4 @@ -import { - BLOCK_COLUMN_NAME, - CYCLE_TIME_LIST, - CYCLE_TIME_SETTINGS_TYPES, - METRICS_CONSTANTS, -} from '@src/constants/resources'; +import { CYCLE_TIME_LIST, CYCLE_TIME_SETTINGS_TYPES, METRICS_CONSTANTS } from '@src/constants/resources'; import { CleanedBuildKiteEmoji, OriginBuildKiteEmoji } from '@src/constants/emojis/emoji'; import { ICycleTimeSetting, IPipelineConfig } from '@src/context/Metrics/metricsSlice'; import { ITargetFieldType } from '@src/components/Common/MultiAutoComplete/styles'; From 0d4e14c000b080ea03dc628ec28590ea0d5e8806 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Mon, 15 Apr 2024 10:06:18 +0800 Subject: [PATCH 08/11] ADM-898-fix fix: fix eslint --- frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx | 2 +- frontend/src/context/Metrics/metricsSlice.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index d17c8a598a..f941f6c38f 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -73,7 +73,7 @@ const CycleTimeTable = () => { dispatch(updateCycleTimeSettings(newCycleTimeSettings)); dispatch(updateReworkTimesSettings({ excludeStates: [], reworkState: null })); }, - [cycleTimeSettings, dispatch, isColumnAsKey, resetRealDoneColumn], + [updateTreatFlagCardAsBlockByCycleTimeSetting, cycleTimeSettings, dispatch, isColumnAsKey, resetRealDoneColumn], ); const header = isColumnAsKey diff --git a/frontend/src/context/Metrics/metricsSlice.ts b/frontend/src/context/Metrics/metricsSlice.ts index c6fb708087..5dc1a73cdd 100644 --- a/frontend/src/context/Metrics/metricsSlice.ts +++ b/frontend/src/context/Metrics/metricsSlice.ts @@ -5,7 +5,7 @@ import { MESSAGE, METRICS_CONSTANTS, } from '@src/constants/resources'; -import { convertCycleTimeSettings, existBlockState, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; +import { convertCycleTimeSettings, getSortedAndDeduplicationBoardingMapping } from '@src/utils/util'; import { pipeline } from '@src/context/config/pipelineTool/verifyResponseSlice'; import { createSlice } from '@reduxjs/toolkit'; import camelCase from 'lodash.camelcase'; From 0f1153c11ae57727212df205ee2664fcfcb8bf68 Mon Sep 17 00:00:00 2001 From: Tingyu Dong Date: Mon, 15 Apr 2024 10:23:33 +0800 Subject: [PATCH 09/11] ADM-898:[frontend]fix: fix flagCardAsBlock in toggle case --- frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index f941f6c38f..87a5ab9a1a 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -103,6 +103,7 @@ const CycleTimeTable = () => { ), ); dispatch(saveDoneColumn([])); + dispatch(updateTreatFlagCardAsBlock(true)); }; return ( From 8fbd40f0582ff2cddb3f864207c908c4ba0ccf07 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Mon, 15 Apr 2024 11:23:51 +0800 Subject: [PATCH 10/11] ADM-898-fix test: add test --- frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx b/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx index ef27fad62d..363e4cb1a0 100644 --- a/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx +++ b/frontend/__tests__/containers/MetricsStep/CycleTime.test.tsx @@ -320,7 +320,7 @@ describe('CycleTime', () => { setup(); await userEvent.click(screen.getByRole('radio', { name: cycleTimeTypeLabels[1] })); - expect(mockedUseAppDispatch).toHaveBeenCalledTimes(3); + expect(mockedUseAppDispatch).toHaveBeenCalledTimes(4); expect(mockedUseAppDispatch).toHaveBeenCalledWith(setCycleTimeSettingsType(CYCLE_TIME_SETTINGS_TYPES.BY_STATUS)); expect(mockedUseAppDispatch).toHaveBeenCalledWith( updateCycleTimeSettings( @@ -331,6 +331,7 @@ describe('CycleTime', () => { ), ); expect(mockedUseAppDispatch).toHaveBeenCalledWith(saveDoneColumn([])); + expect(mockedUseAppDispatch).toHaveBeenCalledWith(updateTreatFlagCardAsBlock(true)); }); describe('cycle time by status', () => { From 5a15ba4dc2e00ba8dc7f683177c45f3dc4c0b429 Mon Sep 17 00:00:00 2001 From: Leiqiuhong Date: Mon, 15 Apr 2024 11:43:18 +0800 Subject: [PATCH 11/11] ADM-898-fix fix: fix logic --- frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx index 87a5ab9a1a..56f6aa7eda 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/Table/index.tsx @@ -103,7 +103,9 @@ const CycleTimeTable = () => { ), ); dispatch(saveDoneColumn([])); - dispatch(updateTreatFlagCardAsBlock(true)); + if (!existBlockState(cycleTimeSettings)) { + dispatch(updateTreatFlagCardAsBlock(true)); + } }; return (